Can't see the forest for the keys

17

1

Write a program or function that takes in a nonempty list of integers in any reasonable convenient format such as 4, 0, -1, -6, 2 or [4 0 -1 -6 2].

Print or return a string that depicts the list as an ASCII art forest where each number becomes a tree of proportional height. Each tree takes up four columns of text in the output as follows:

  • A positive integer N becomes a tree whose base is __|_ and top is ^ , with N layers of / \ in between.

    For example, when N = 1 the tree is

      ^
     / \
    __|_
    

    when N = 2 the tree is

      ^
     / \
     / \
    __|_
    

    when N = 3 the tree is

      ^
     / \
     / \
     / \
    __|_
    

    and so on.

  • A negative integer N becomes just like the corresponding positive tree except a vertical bar is between the branch slashes instead of a space.

    For example, when N = -1 the tree is

      ^
     /|\
    __|_
    

    when N = -2 the tree is

      ^
     /|\
     /|\
    __|_
    

    when N = -3 the tree is

      ^
     /|\
     /|\
     /|\
    __|_
    

    and so on.

  • When the integer is 0 there is technically no tree, just an empty space of four underscores:

    ____
    

The underscores at the base of each tree must line up in the output, i.e. all the trees must have their bases at the same level. Also, a single underscore is added to the end of the line of underscores after the last tree. This makes it so every tree has an empty column of "air" on either side of it.

As an example, the output for 4 0 -1 -6 2 would be

              ^
             /|\
  ^          /|\
 / \         /|\
 / \         /|\  ^
 / \      ^  /|\ / \
 / \     /|\ /|\ / \
__|_______|___|___|__

Note how the tree patterns always have a leading column of empty space but an underscore had to be added to pad the right side of the last tree.

Also:

  • Trailing spaces on any lines are fine, but there should be no unnecessary leading spaces.
  • Leading newlines are not allowed (the tallest tree should touch the top of the output text grid) and only up to one trailing newline is allowed.
  • The list may contain any integers from -250 to 250 inclusive. Handling taller trees is not required.

The shortest code in bytes wins.

More Examples

3:

  ^
 / \
 / \
 / \
__|__

-2:

  ^
 /|\
 /|\
__|__

0:

_____

0, 0:

_________

0, 1, 0:

      ^
     / \
______|______

0, -1, 2, -3, 4:

                  ^
              ^  / \
          ^  /|\ / \
      ^  / \ /|\ / \
     /|\ / \ /|\ / \
______|___|___|___|__

Calvin's Hobbies

Posted 2015-12-07T03:56:21.367

Reputation: 84 000

Answers

6

Pyth, 48 bytes

j_.t+sm.i,J\_?d++\|sm?>d0\ \|d\^Jms+Jmkd"/\\"QJd

Try it online: Demonstration or Test Suite

Too lazy for a full explanation. Here just short overview:

I generate the columns first. So the image:

      ^ 
  ^  /|\
 / \ /|\
__|___|__

gets generated as:

["_", "_/", "| ^", "_\", "_", "_//", "|||^", "_\\", "_"]

Notice, that I'm generating only the lower part (everything without the spaces). And I'm also generating them from bottom to top. This is done pretty straightforward.

Then I can use the .t method to append spaces to the strings, so that each string has an equal length. And afterwards I reverse the order and print.

Jakube

Posted 2015-12-07T03:56:21.367

Reputation: 21 462

As a note: If this is an actual output, you might have forgotten to add a trailing _ (underscore) after the last tree. – insertusernamehere – 2015-12-07T22:56:24.077

1@insertusernamehere Thanks, completely overlooked the trailing _. – Jakube – 2015-12-07T23:00:43.047

7

Python 2, 165 bytes

a=input()
l=max(map(abs,a))
while l+2:s=' _'[l<0];print(s+s.join((([' ^ ','//| \\\\'[x>0::2],'   '][cmp(abs(x),l)],'_|_')[l<0],s*3)[x==0]for x in a)+s).rstrip();l-=1

This is a full program that accepts a list as input. I'm still golfing this horrid mess.

xsot

Posted 2015-12-07T03:56:21.367

Reputation: 5 069

4

PHP, 231 277 bytes

This challenge has a beautiful output.

$x=fgetcsv(STDIN);for(;$i<2+max(array_map(abs,$x));$i++)for($j=0;$j<count($x);){$_=$x[$j++];$o[$i].=!$i?$_?'__|_':____:(abs($_)>=$i?0>$_?' /|\\':' / \\':($i-1&&abs($_)==$i-1?'  ^ ':'    '));}echo implode("
",array_reverse($o))."_";

Reads a comma separated list (whitespaces are optional) from STDIN:

$ php trees.php
> 1, 2, 0, -4, 6

Ungolfed

$x=fgetcsv(STDIN);
for(;$i<2+max(array_map(abs,$x));$i++)
    for($j=0;$j<count($x);){
        $_=$x[$j++];
        $o[$i] .= !$i ? $_?'__|_':____
                      : (abs($_)>=$i ? 0>$_?' /|\\':' / \\'
                                     : ($i-1&&abs($_)==$i-1 ? '  ^ ' : '    '));
    }
echo implode("\n",array_reverse($o))."_";

Edits

  • Saved 46 bytes. Discarded array initialization, replaced if/else with ternary operators and moved some of the variables around to save a few bytes.

insertusernamehere

Posted 2015-12-07T03:56:21.367

Reputation: 4 551

2

Ruby, 157 156 153 characters

->h{r=[]
h.map{|i|j=i.abs
r+=[s=?_,?/*j+s,i==0?s:?^+(i>0?' ':?|)*j+?|,?\\*j+s].map{|l|l.rjust(h.map(&:abs).max+2).chars}}
r.transpose.map{|l|l*''}*$/+?_}

Written just because initially Array.transpose looked like a good idea. Not anymore.

Sample run:

2.1.5 :001 > puts ->h{r=[];h.map{|i|j=i.abs;r+=[s=?_,?/*j+s,i==0?s:?^+(i>0?' ':?|)*j+?|,?\\*j+s].map{|l|l.rjust(h.map(&:abs).max+2).chars}};r.transpose.map{|l|l*''}*$/+?_}[[4, 0, -1, -6, 2]]
              ^     
             /|\    
  ^          /|\    
 / \         /|\    
 / \         /|\  ^ 
 / \      ^  /|\ / \
 / \     /|\ /|\ / \
__|_______|___|___|__

manatwork

Posted 2015-12-07T03:56:21.367

Reputation: 17 865

Collecting the pieces into a separate array instead of returning them from the first map should allow to avoid the use of reduce. – manatwork – 2015-12-08T19:00:24.147

1

PowerShell, 167 bytes

$h=(($r=($args|%{$a,$x=$_,-$_-ge0
'_';'_'+'/'*$a;'|_'[!$_]+(' ','|')[$_-lt0]*$a+'^'*!!$_
'_'+'\'*$a})+'_')|% Le*|sort)[-1]
$z=$r|% *ht $h;--$h..0|%{-join($z|% Ch* $_)}

Try it online!

Explanation:

  1. Draw a rotated forest line by line

    Rotated forest

  2. Pad right and

  3. Rotate -90.

Unrolled:

# a rotated forest with underscore after the last tree
$rotatedForest=$args|%{
    $abs,$x=$_,-$_-ge0
    '_'
    '_'+'/'*$abs
    '|_'[!$_]+(' ','|')[$_-lt0]*$abs+'^'*!!$_
    '_'+'\'*$abs
}
$rotatedForest += '_'

# calc height
$height = ($rotatedForest|% Length|sort)[-1]

# PadRight all trees
$z = $rotatedForest|% PadRight $height

# Rotate -90
--$height..0|%{
    -join($z|% Chars $_)
}

mazzy

Posted 2015-12-07T03:56:21.367

Reputation: 4 832

0

C#, 318 bytes

I tried transposing the array. I'm not sure if that was the best solution.

string F(int[]a){int i=a.Length,j,w=i*4+1,h=0;string f="",o=f;for(;i-->0;){j=a[i];f+=","+" _,".PadLeft(j=j>0?j+3:-j+3,'\\')+(j>3?"^"+"|_,".PadLeft(j,a[i]<0?'|':' '):"_,")+" _,_".PadLeft(j+1,'/');h=h<j?j:h;}f="_".PadLeft(h=h>3?h:2,'\n')+f;for(i+=h<3?1:0;++i<h;)for(j=w;j-->0;)o+=f.Split(',')[j].PadLeft(h)[i];return o;}

Indentation and newlines for clarity:

string F(int[]a)
{
    int i=a.Length,
        j,
        w=i*4+1,
        h=0;
    string f="",o=f;
    for(;i-->0;){
        j=a[i];
        f+=","+" _,".PadLeft(j=j>0?j+3:-j+3,'\\')+(j>3?"^"+"|_,".PadLeft(j,a[i]<0?'|':' '):"_,")+" _,_".PadLeft(j+1,'/');
        h=h<j?j:h;
    }
    f="_".PadLeft(h=h>3?h:2,'\n')+f;
    for(i+=h<3?1:0;++i<h;)
        for(j=w;j-->0;)
            o+=f.Split(',')[j].PadLeft(h)[i];
    return o;
}

Hand-E-Food

Posted 2015-12-07T03:56:21.367

Reputation: 7 912