26
\$\begingroup\$

Task

Your task is to draw these beautiful oak trees:

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.
\$\endgroup\$
  • \$\begingroup\$ 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 \$\endgroup\$ – Noodle9 Mar 26 at 11:40
  • \$\begingroup\$ @Noodle9 I am not entirely sure what is standard practice for ascii-art contests, I guess I'd have to look it up \$\endgroup\$ – RGS Mar 26 at 12:27
  • 1
    \$\begingroup\$ What range of input n must the program handle? Any value or just the 6 shown in the task? \$\endgroup\$ – spuck Mar 26 at 16:24
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ – RGS Mar 26 at 18:37
  • 1
    \$\begingroup\$ @MarkStewart I thought that's a hole where you'd enter to be in the tree, part of the tree! :D \$\endgroup\$ – Noodle9 Mar 26 at 21:55
18
\$\begingroup\$

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\$:

heights

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
|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Just knew as soon as I caught up to you, you'd golf a few bytes off your score - bravo! :D \$\endgroup\$ – Noodle9 Mar 26 at 13:55
  • 1
    \$\begingroup\$ @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 \$\endgroup\$ – Arnauld Mar 26 at 14:00
11
\$\begingroup\$

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
|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Sorry, comments out of order now! My fault, I deleted while I double checked on TIO. \$\endgroup\$ – ElPedro Mar 26 at 21:27
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ – Noodle9 Mar 26 at 21:27
  • \$\begingroup\$ I think you're right. My bad. It's been a long day. \$\endgroup\$ – ElPedro Mar 26 at 21:32
  • \$\begingroup\$ @ElPedro np - thanks for trying! :-) \$\endgroup\$ – Noodle9 Mar 26 at 21:32
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ – Noodle9 Mar 27 at 19:01
9
\$\begingroup\$

Charcoal, 78 71 bytes

Nθ≔×¹·⁵⊕θηW›ηⅈ«↑P×⁻ηⅈ@Mⅉ←»J⁰θ×θ_P×⊘⁺³θ_↑⊕θ⸿W›θⅈ«P×⁻θⅈ@M⁺²ⅉ¹»Jθⅉ↗‖OO﹪θ².

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

Nθ

Input n.

≔×¹·⁵⊕θη

Calculate the half width of the reference row.

W›ηⅈ«

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

... move up a line, ...

P×⁻ηⅈ@

... print some canopy, ...

Mⅉ←»

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

J⁰θ

Jump to the left side of the base.

×θ_

Print the left side of the base.

P×⊘⁺³θ_

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

↑⊕θ

Print the trunk.

⸿

Move to the beginning of the reference row.

W›θⅈ«

While there is still foliage to print, ...

P×⁻θⅈ@

... print some foliage, ...

M⁺²ⅉ¹»

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

Jθⅉ↗

Move to the mirror location of the ..

‖OO﹪θ²

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.

|improve this answer|||||
\$\endgroup\$
8
\$\begingroup\$

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`
|improve this answer|||||
\$\endgroup\$
7
\$\begingroup\$

05AB1E, 106 105 103 99 97 95 93 92 bytes

$×0I>;úR©«D1s∍ILηOD¦<‚vyvDðyL<ǝ}r}r)ʒ1å}`'.I>ǝ)DJ0¢Iα®иIú0'_I×쬮¦∍«)˜IÉi.ºëº}»T„@|‡I≠i'.ð.;

Try it online or verify the first 10 test cases.

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.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ I'm not saying it is a bad solution but.... this is a very long 05AB1E solution xD \$\endgroup\$ – RGS Mar 26 at 9:13
  • \$\begingroup\$ Good luck golfing it! Ping me when you do some progress with it :D \$\endgroup\$ – RGS Mar 26 at 9:40
  • \$\begingroup\$ @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. :) \$\endgroup\$ – Kevin Cruijssen Mar 26 at 17:33
  • \$\begingroup\$ Alright, you're in the 2 digits now! good job \$\endgroup\$ – RGS Mar 26 at 17:44
  • 1
    \$\begingroup\$ @KevinCruijssen beat you by 8 bytes, surprisingly enough. \$\endgroup\$ – Magic Octopus Urn Apr 1 at 17:54
1
\$\begingroup\$

05AB1E, 86 81 77 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.

|improve this answer|||||
\$\endgroup\$
  • 1
    \$\begingroup\$ 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 ; 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 , since the transliterate also works with lists. \$\endgroup\$ – Kevin Cruijssen Apr 1 at 18:09
  • \$\begingroup\$ How have I never known about ·...? \$\endgroup\$ – Magic Octopus Urn Apr 1 at 18:15
  • \$\begingroup\$ 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. \$\endgroup\$ – Kevin Cruijssen Apr 1 at 18:29
  • \$\begingroup\$ @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. \$\endgroup\$ – Magic Octopus Urn 2 days ago
  • \$\begingroup\$ 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. \$\endgroup\$ – Kevin Cruijssen 2 days ago

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.