# Draw an ASCII oak tree (or a woman with curly hair?)

1 ->
@@@
@@@@@
@|.|@
_|_|_

2 ->
@@
@@@@@@
@@@@@@@@
@@| .|@@
|  |
__|__|__

3 ->
@@@@@
@@@@@@@@@
@@@@@@@@@@@
@@@|   |@@@
@|  .|@
|   |
___|___|___

4 ->
@@
@@@@@@@@
@@@@@@@@@@@@
@@@@@@@@@@@@@@
@@@@|    |@@@@
@@|   .|@@
|    |
|    |
____|____|____

5 ->
@@@@@
@@@@@@@@@@@
@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@
@@@@@|     |@@@@@
@@@|    .|@@@
|     |
|     |
|     |
_____|_____|_____

6 ->
@@@@@@@@
@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@
@@@@@@|      |@@@@@@
@@@@|      |@@@@
@|     .|@
|      |
|      |
|      |
______|______|______


(that one sandbox commenter felt were similar to women with curly hair!)

The trees are allowed to be surrounded by any extra whitespace your solution creates as long as that doesn't break the tree, of course.

# Algorithm

As per the examples above, we take n to represent the width of the trunk and the specs of the tree will be given in terms of that.

Given n:

1. the height of the trunk is n + 1;
2. the width of the trunk in space characters is n;
3. the top row of the trunk is n times the character @, followed by |, n times the space character , another vertical pipe | and then n times @.
4. from the reference row up to the top of the crown, we only use @ as follows:
• there is one row with the same width as the reference row and each subsequent row above is shortened by one @ than the previous shortening;
5. from the reference row downwards, we chop 2 @ from each side and then each row we go down, we chop one more @ than the previous row;
6. the last trunk row that is surrounded by a @ has a . immediately to the left of the right vertical pipe |;
7. the bottom most row has the underscore _ in all the tree width, except where the trunk vertical pipes | are.
• Do we have to draw the tree/woman or can we return a list of strings? Not only saves bytes but makes the code testable! :D – Noodle9 Mar 26 at 11:40
• @Noodle9 I am not entirely sure what is standard practice for ascii-art contests, I guess I'd have to look it up – RGS Mar 26 at 12:27
• What range of input n must the program handle? Any value or just the 6 shown in the task? – spuck Mar 26 at 16:24
• @spuck your program should work in theory for any n. If your language overflows at some point or you blow some recursion limit or wtv, that is fine. – RGS Mar 26 at 18:37
• @MarkStewart I thought that's a hole where you'd enter to be in the tree, part of the tree! :D – Noodle9 Mar 26 at 21:55

# JavaScript (ES8),  215 ... 199  197 bytes

f=(n,k=(W=3*n+2)**.5-.5|0,x=.5-k-(W-n)**.5,R=(n,k)=>S=''.padEnd(n,'.@_'[k]))=>k+n+2?(w=k*-~k/2,k<0?(x>0?s=R(n,k+n+3):R(w+=~k,s=R(n-1)+R(1,~~x))+R(n-w,1))+|${s}|+S:R(w)+R(W-w*2,1))+ +f(n,k-1):''  Try it online! or Test it online! against an ungolfed, straightforward implementation ### How? We define $$\H\$$ as the height of the upper part of the crown (where the width is increasing) and $$\h\$$ as the height of the lower part (where the width is decreasing). Example for $$\n=6\$$: We have: $$H_n=\left\lfloor\sqrt{3n+2}+\frac{1}{2}\right\rfloor$$ and: $$h_n=\left\lfloor\sqrt{2n+2}-\frac{1}{2}\right\rfloor$$ By computing these values beforehand, we can draw the tree from top to bottom with a single loop, which is implemented here as a recursive function. The total height of the tree is $$\H_n+n+1\$$. We use a counter $$\k\$$ going from $$\H_n-1\$$ to $$\-n-1\$$.  3 | ......@@@@@@@@ | upper crown 2 | ...@@@@@@@@@@@@@@ | 1 | .@@@@@@@@@@@@@@@@@@ | 0 | @@@@@@@@@@@@@@@@@@@@ | ---+----------------------+------------- -1 | @@@@@@| |@@@@@@ | lower crown -2 | ..@@@@| |@@@@ | -3 | .....@| .|@ | ---+----------------------+------------- -4 | | | | trunk only -5 | | | | -6 | | | | ---+----------------------+------------- -7 | ______|______|______ | roots  The number of leading spaces for the upper part of the crown is the $$\k\$$-th triangular number: $$T_k=\frac{k\times(k+1)}{2}$$ The number of leading spaces for the lower part of the crown is given by: $$\frac{(k+1)\times(k-2)}{2}=T_k-k-1$$ ### Commented f = ( // f is a recursive function taking: n, // n = input k = (W = 3 * n + 2) // W = 3n + 2 = total width of the tree ** .5 - .5 | 0, // k = counter, initialized to floor(sqrt(W) - 1/2) x = .5 - k - (W - n) ** .5, // x = 1/2 - k - sqrt(W - n) R = (n, k) => // R is a helper function returning and saving in S: S = // a character identified with k ('.', '@', '_' or ''.padEnd(n, '.@_'[k]) // a space) repeated n times ) => // k + n + 2 ? // if k is not equal to -n - 2: ( w = k * -~k / 2, // initialize w to the k-th triangular number k < 0 ? // if k is negative: ( x > 0 ? // if x is positive (trunk only or roots): s = // set s to: R(n, k + n + 3) // '_' * n if k = -n - 1, or space * n otherwise : // else (lower crown): R( // append ... w += ~k, // ... w - k - 1 spaces s = R(n - 1) + // and set s to n - 1 spaces followed by R(1, ~~x) // '.' if floor(x) = 0, or another space otherwise ) + // R(n - w, 1) // append '@' * (n - w) ) + // |${s}| +            //     append s surrounded by '|' characters
S                     //     append S
:                       //   else (upper crown):
R(w) +                //     append w spaces
R(W - w * 2, 1)       //     append '@' * (W - 2w)
) +                       //
\n +                    //   append a line-feed
f(n, k - 1)               //   append the result of a recursive call with k - 1
:                           // else:
''                        //   stop recursion

• Just knew as soon as I caught up to you, you'd golf a few bytes off your score - bravo! :D – Noodle9 Mar 26 at 13:55
• @Noodle9 In fact, I've spent more time than usual golfing it before posting a first version, so it's getting hard to shave a few more... :p – Arnauld Mar 26 at 14:00

# Python 3, 249 $$\\cdots\$$ 211 210 bytes

Saved 6 $$\\cdots\$$ 17 18 bytes thanks to Kevin Cruijssen!!!

def f(n):
i=j=1
t,y,e,w,s,x,S=[],n*'_','|',0,3*n+2,n*' ',n
while s>1:t=[w*' '+s*'@']+t;s-=2*i;w+=i;i+=1
while S>0:j+=1;t+=[x[S:]+S*'@'+e+x[1:]+'. '[S>j]+e+S*'@'];S-=j
return t+[(x+e)*2]*(n-j+1)+[(y+e)*2+y]


Try it online!

Before golfing

def f(n):
w=s=3*n+2
t=[]
i=1
while s>1:
t=[' '*((w-s)//2)+'@'*s]+t
s-=2*i
i+=1
b=s=n
i=2
while s>0:
t+=[(n-s)*' '+s*'@'+'|'+~-n*' '+'. '[s-i>0]+'|'+s*'@']
s-=i
b-=1
i+=1
while b:
t+=[n*' '+'|'+n*' '+'|']
b-=1
t+=[n*'_'+'|'+n*'_'+'|'+n*'_']
return t

• Sorry, comments out of order now! My fault, I deleted while I double checked on TIO. – ElPedro Mar 26 at 21:27
• @ElPedro Are you sure? That's invalid syntax as it is but I can't see how it would work even if it were. w has to be incremented by i before i is incremented by 1. – Noodle9 Mar 26 at 21:27
• I think you're right. My bad. It's been a long day. – ElPedro Mar 26 at 21:32
• @ElPedro np - thanks for trying! :-) – Noodle9 Mar 26 at 21:32
• @mypetlion Unfortunately your suggestions don't work. s is being decremented by 2*i every loop (as i is incremented by 1 each loop) so it often never reaches 0. And [y*3+e*2] doesn't draw the base. – Noodle9 Mar 27 at 19:01

# Charcoal, 78 71 bytes

Ｎθ≔×¹·⁵⊕θηＷ›ηⅈ«↑Ｐ×⁻ηⅈ@Ｍⅉ←»Ｊ⁰θ×θ_Ｐ×⊘⁺³θ_↑⊕θ⸿Ｗ›θⅈ«Ｐ×⁻θⅈ@Ｍ⁺²ⅉ¹»Ｊθⅉ↗‖ＯＯ﹪θ².


Try it online! Link is to verbose version of code. Explanation:

Ｎθ


Input n.

≔×¹·⁵⊕θη


Calculate the half width of the reference row.

Ｗ›ηⅈ«


While there is still canopy left to print, ...

↑


... move up a line, ...

Ｐ×⁻ηⅈ@


... print some canopy, ...

Ｍⅉ←»


... and move to the start of the next row of canopy.

Ｊ⁰θ


×θ_


Print the left side of the base.

Ｐ×⊘⁺³θ_


Print half of the middle of the base, allowing space for the trunk.

↑⊕θ


Print the trunk.

⸿


Move to the beginning of the reference row.

Ｗ›θⅈ«


While there is still foliage to print, ...

Ｐ×⁻θⅈ@


... print some foliage, ...

Ｍ⁺²ⅉ¹»


... and adjust the width of the foliage according to the number of lines of foliage already printed.

Ｊθⅉ↗


Move to the mirror location of the ..

‖ＯＯ﹪θ²


Reflect the half tree drawn so far to almost complete the tree and also move the cursor to the location of the ..

.


Complete the tree.

# Java 11, 282 273 bytes

n->{String r="",y="_".repeat(n),e="|",N="\n",S=" ",q=S.repeat(n-1),z;int i=1,w=0,s=3*n+2;for(;s>1;s-=2*i,w+=i++)r=S.repeat(w)+"@".repeat(s)+N+r;for(i=1,s=n;s>0;s-=i)r+=S.repeat(n-s)+(z="@".repeat(s))+e+q+(s>++i?S:".")+e+z+N;return r+((q+=S+e)+q+N).repeat(n-i+1)+y+e+y+e+y;}


Port of @Noodle9's Python answer, after I helped him golf it a bit.
-9 bytes thanks to @Arnauld.

Try it online.

Explanation:

n->{                     // Method with integer parameter and String return-type
String r="",           //  Result-String, starting empty
y="_".repeat(n),     //  Temp-String y, consisting of the input amount of "_"
e="|",               //  Temp-String e, containing "|"
N="\n",              //  Temp-String N, containing a newline
S=" ",               //  Temp-String S, contain a space
q=S.repeat(n-1),     //  Temp-String q, consisting of the input-1 amount of spaces
z;                   //  Temp-String z, uninitialized
int i=1,               //  Integer i, starting at 1
w=0,                 //  Integer w, starting at 0
s=3*n+2;             //  Integer s, starting at 3 times the input + 2
for(;s>1               //  Continue looping as long as s is larger than 1:
;                  //    After every iteration:
s-=2*i,           //     Decrease s by i twice
w+=i              //     Increase w by i
++)           //     And increase i by 1
r=S.repeat(w)       //   Prepend w amount of spaces;
+"@".repeat(s)    //   s amount of "@";
+N                //   and a newline
+r;             //   to the result-String
for(i=1,               //  Reset i to 1
s=n;               //  Reset s to the input
s>0;               //  Continue looping as long as s is larger than 0:
s-=i)              //    After every iteration: decrease s by i
r+=                  //   Append the result-String with:
S.repeat(n-s)     //    The input minus s amount of spaces;
+(z="@".repeat(s))//    s amount of "@";
+e                //    a "|";
+q                //    the input-1 amount of spaces;
+(s>++i?          //    If s is larger than i+1
//    (by first increasing i by 1 with ++i)
S              //     a space;
:               //    Else:
".")           //     a ".";
+e                //    a "|";
+z                //    the input minus s amount of spaces again;
+N;               //    and a newline character
return r               //  After both loops: return the result-String,
+              //  appended with:
((q+=S+e)     //   q with an additional space and "|" appended
+q   //   twice
+N)//   and a newline
.repeat(n-i+1)//   Repeated n-i+1 amount of times
+y+e+y+e       //   As well as two times y and "|"
+y;}   //   And an additional third y


# 05AB1E, 10610510399979593 92 bytes

$×0I>;úR©«D1s∍ILηOD¦<‚vyvDðyL<ǝ}r}r)ʒ1å}'.I>ǝ)DJ0¢Iα®иIú0'_I×ì¬®¦∍«)˜IÉi.ºëº}»T„@|‡I≠i'.ð.;  Explanation: $                # Push 1 and the input-integer
×               # Pop both, and push a string consisting of the input amount of "1"s
0              # Push a 0
I>;           # Push the (input+1)/2
ú          # Prepend that many spaces to the "0" (truncates decimals)
R         # Reverse it so the spaces are trailing
©        # Store it in variable ® (without popping)
«       # Append it to the string of 1s
D                # Duplicate it
1               # Push a 1
s              # Swap the two values on the stack
∍             # Extend the "1" to a size equal to the string-length
IL           # Push a list in the range [1, input]
η          # Get the prefixes of this list
O         # And sum each inner prefix
D        # Duplicate this list of integer
|       # Remove the leading 1
<      # Decrease each value by 1
‚     # And pair the two lists together
v                # Loop over this pair of list of integers:
yv              #  Inner loop y over each of those lists of integers:
D             #   Duplicate the string at the top of the stack
yL          #   Push a list in the range [1, y]
<         #   Decrease it by 1 to make it [0, y)
ð   ǝ        #   And replace the characters at those indices with a space
}r             #  After the inner loop: reverse all values on the stack
}r               # After the outer loop: reverse all values on the stack
)              # And wrap all values on the stack into a list
ʒ             # Filter this list by:
1å           #  Only keep lines which contain a "1"
}            # After the filter: Push all values separated to the stack again
'.I>ǝ      '# Replace the space at index input+1 with a "."
)      # And re-wrap all values on the stack into a list again
D                # Duplicate this list of lines
J               # Join them all together
0¢             # Count the amount of "0"s in this string
Iα           # Get the absolute difference with the input
®и         # Repeat ® (the "|" with trailing spaces) that many times as list
Iú       # Prepend the input amount of spaces to each string
0                # Push a 0
'_I×ì          '# Prepend the input amount of "_"
¬          # Push its first character (without popping), which is a "_"
®¦∍       # Repeat it the length of ® - 1 amount of times
«      # Append it to the "0" with leading "_"
)                # Wrap all values on the stack into a list again
˜               # Flatten it
IÉi            # If the input is odd:
.º          #  Mirror each line with the last character overlapping
ë            # Else:
º           #  Mirror each line without overlap
}»               # After the if-else: join all lines by newlines
T              # Push 10
„@|           # Push string "@|"
‡          # Transliterate all "1" to "@" and all "0" to "|"
I≠i            # If the input is NOT 1:
'.ð.;      '#  Replace the first "." with a space
# (after which the result is output implicitly)


r)ʒ1å}'.I>ǝ)D could alternatively be )ʒþà}ć'.N>ǝšÂs for the same byte-count.

• I'm not saying it is a bad solution but.... this is a very long 05AB1E solution xD – RGS Mar 26 at 9:13
• Good luck golfing it! Ping me when you do some progress with it :D – RGS Mar 26 at 9:40
• @RGS Golfed 14 bytes in total, so I'm fairly happy now. Can probably save a bit more bytes, but this will do for now. :) – Kevin Cruijssen Mar 26 at 17:33
• Alright, you're in the 2 digits now! good job – RGS Mar 26 at 17:44
• @KevinCruijssen beat you by 8 bytes, surprisingly enough. – Magic Octopus Urn Apr 1 at 17:54

# 05AB1E, 868177 67 bytes

Î×2.ø$×.ø©DSdJD.ΓN>·.$}r[D¬≠#NÌF¦¨]\\0'..;R®1K¹;.D®TS'_:).c2Ý… @|‡


Try it online!

-5 -9 -19 @ Kevin Cruijssen, thanks!

This is some unorthodox stuff, definitely not my best answer... It's been awhile, I'm rusty. Also tried to use some of the new commands like Δ which did not go as planned, and I had to use a global register. Still trying to golf that whole part out.

For the purpose of the explanation I will mean input for the duration.

[       Code      ] #     [        Explanation        ]
====================#========================================
$# Push 1 and I. 3*Ì # (3 * I) + 2 × # "1" repeat (3 * I) + 2 times. Ð # Triplicate. ====================# Setting up the first line of the bottom crown. ¹x # Push I and 2I. ‚ # [I, 2I] > # [I + 1, 2I + 1] o # [2 ^ (I + 1), 2 ^ (2I + 1)] ¥ # 2 ^ (I + 1) - 2 ^ (2I + 1) b # Convert to binary (I=2: 11000) - # Subtract from previous value (I=2: 11111111-11000=11100111) н # Remove from array (get first element) ====================# Setting up the first line of the bottom crown. ¹ # Push I. x>‚ # [I, 2I + 1) o # [2 ^ I, 2 ^ (2I + 1)] b # Convert to binary (I=2: [100,100000]) O # Sum (I=2: 100100) + # Add to previous (I=2: 11200211) ©ˆ # Store in register, push to global array. ====================# Setting up the first line of the top crown. ˆ # Push the line of 1's that starts the top crown. ====================# Creating the top crown. Δ # Until this code stops changing the value... N>· # (Iteration + 1) * 2 .$              # Remove (Iteration + 1) * 2 characters
Dˆ            # Dupe and push to global array.
}¯R        # Push global array reversed, flatten.

====================# Creating the bottom crown.
[                   # Infinite loop...
D¬≠#               # Duplicate last value, break if it doesn't start with 1.
NÌ             # (2 * Iteration) + 1
F¦¨          # Loop (2 * Iteration) + 1 time and remove head/tail.
]\\       # End loop, remove 2 values.

====================# Adding the knot of the tree.
0'..;               # Find and replace first 0 with a period.
R              # Reverse it from the left side to the right.

====================# Creating the trunk.
®                   # Push the register.
1K                 # Remove the extra leaves (1's).
¹;.D             # Push I/2 copies of this.

====================# Creating the ground.
®                   # Push register for bottom
TS'_:              # Replace all leaves (1's) and spaces (0's) with '_' (3's)

====================# Pulling it all together.
)                   # Wrap stack to array.
.c                 # Center.
2Ý               # Push [0,1,2].
… @|           # Push " @|".
‡          # Replace 0 with ' ', 1 with '@' and 2 with '|'.

====================# Done!


working on updating the explanation, I am on my cell phone at the moment.

• 2* can be ·; you can remove the ´ (since you don't use the global array later on anymore, so no need to empty it); 2N+ can be NÌ; you can remove the } before the ]; You can remove the R at the \\R0, since it's a palindrome anyway; and you can remove the J after the 2Ý, since the transliterate also works with lists. – Kevin Cruijssen Apr 1 at 18:09
• How have I never known about ·...? – Magic Octopus Urn Apr 1 at 18:15
• You forgot to remove the } before the ] in your actual code/TIO. And what you were trying with Δ was probably .Γ instead. You can use r.ΓN>·.$}r[ instead of ˆˆΔN>·.$Dˆ}¯R[ for -3: try it online. – Kevin Cruijssen Apr 1 at 18:29
• @KevinCruijssen only way I'm gonna pull less than 71 is if I get the 12021 pattern made in better than $3*Ì×Ð¹x‚>o¥b-н¹x>‚obO+© I think I can I just a not seeing it. – Magic Octopus Urn Apr 2 at 15:09 • 67 bytes in that case by replacing that part you mentioned (including the r after ©) with Î×2.ø$×.ø©DSdJD. ;) Hadn't realized that first part was only for the 12021 pattern. – Kevin Cruijssen Apr 2 at 15:19

# Perl 5, 215 bytes

$n=pop;$y=$n*3+2;while($y>0){$_=$"x$x.'@'x$y.$/.$_;$x+=++$z;$y-=2*$z}print;$i=$x=$n++;$k=2;while($n--){$_=$"x$j.'@'x$i.'|'.$"x$x.'|'.'@'x$i.$"x$j;$k>=$i?($i&&s/ \|@/.|@/,$i=0,$j=$x):($i-=$k,$j+=$k++);$n||y/ /_/;say}  Try it online! Takes size of tree as single command-line argument. I'm sure it can probably be improved. Ungolfed: $n = pop;                                   # get tree size from command line

## Crown section ##

$x = 0; # initial number of leading spaces to print$y = $n * 3 + 2; # initial number of @s to print$z = 0;                                     # used for increment/decrement value

while($y > 0) # build from bottom up as long as we have @s to add {$_ = ' 'x$x . '@'x$y . "\n" . $_; # new row of crown, prepended to existing crown$z++;                               # increase increment/decrement counter
$x +=$z;                           # increase leading spaces for next row
$y -= 2*$z;                         # decrease number of @s for next row
}

print;                                      # print the crown (don't use say to avoid extra LF)

## Trunk section ##

$x =$n++;                                  # width of trunk
$i =$x;                                    # number of @s before/after trunk
$j = 0; # number of leading/trailing spaces$k = 2;                                     # number of leading/trailing @s to remove in next row

while($n--) # build each row of the trunk {$_ = ' 'x$j . '@'x$i . '|' . ' 'x$x . '|' . '@'x$i . ' 'x$j; # new row of trunk if($k >= $i) { # if next row won't have any @s s/ \|@/.|@/ if($i);         # set dot in trunk for last row with any @s
$i=0; # no @s in next row$j=$x; # spaces in next row equal to trunk width } else {$i -= $k; # reduce @s for next row$j += $k; # increase spaces for next row$k++;                       # increase increment/decrement counter
}

if(\$n == 0) {                       # if this is the last row
y/ /_/;                     # replace spaces with underscores
}

say;                                # print the row
}
`