Elixir Christmas Tree Puzzle

Instructions

Use your elixir coding skills to create a Christmas tree.

Your code should create a triangle of 0 characters that is 9 rows high. At the top of the tree should be a star * and at the bottom there should be a trunk H. The star and trunk should be aligned, and the start should be directly above the top of the tree. The start and trunk are addional rows, therefore the entire tree is a total of 11 characters high.

Every row of the triangle should be properly indented, such that each row is centred on the row above. For example:

iex> --enter your code here--
        *
        0
       000
      00000
     0000000
    000000000
   00000000000
  0000000000000
 000000000000000
00000000000000000
        H

The solution that completes this puzzle with the shortest number of characters wins. However if you want to get creative you will get an honourable mention.

Submit your solution

Please submit your solution as a comment on the Github issue below, along with your Twitter handle (we will use this to link back to you if your solution is successful).

Solutions

Generating a Christmas tree seemed to really resonate with the everyone, as we had the most solutions submitted so far. We also had some really fun versions, in particular @henrik and @stoftis for their “interpretations” of what a Christmas tree should be like.

Winner

Winner: @ventsislaf

char count 92

p=&IO.puts :string.centre&1,17;p.('*');for n<-1..9,do: p.(List.duplicate ?0,n*2-1);p.('H')

Understanding

This solution from @ventsislaf builds a Christmas tree from three components, the star, body and trunk. The clever bit is that he figured out a generic function for building the components.

To understand what this generic anonymous function does, lets rewrite it:

p=&IO.puts :string.centre&1,17
#rewritten as
p = &(IO.puts( :string.centre(&1, 17) ))

The Erlang function :string.centre/2 will create a string where the String passed to it is centred by surrounding it with blank characters. The anonymous function above takes one parameter, the String to centre, and outputs it centred within 17 characters. The number of characters is dictated by the shape of the Chrismas tree. 9 rows will make the total width 17 characters for the bottom branches of the Christmas tree. This anonymous function is bound to the variable p and can then be used throughout the algorithm to draw each layer of the tree.

The first and last rows of the tree are created by a call to the anonymous function p.('*') and p.('H') respectively. The body of the Christmas tree is a bit more complex.

for n<-1..9,do: p.(List.duplicate ?0,n*2-1)

This elixir comprehension builds the triangular body of the Christmas tree. If we rewrite it, it becomes clearer what is going on.

for n <- 1..9, do: p.(List.duplicate( '0', n * 2 - 1))

The comprehension iterates over a range of interger values from 1..9 and binds each integer to the variable n. This value is then used by the body of the comprehension.
First it calls the anonymous function defined previously and passes in a new List. The List is constructed of a character List of 0s, @ventsislaf uses the shortcut ?0 to return the ASCII character 48 which maps to 0. He then uses the List.duplicate/2 function to create a new List. This function “Duplicates the given element (?0, in our case), n number of times.

As can be seen from the formula @ventsislaf gradually increments the Character List with n * 2 - 1 until it reaches a maximum length of 17. Each list is then passed into the :string.centre function, and this gives us our triangular shape.

A great solution, which highlights the power of tapping into Erlang functions within elixir.

Honourable mentions

Submitted by fishcakez:

char count 94

:io.format"~10s~s~n~10s~n",["*",(for n<-1..9,do: :io_lib.format"~n~#{9+n}.#{2*n-1}c",'0'),"H"]

This solution makes heavy use of the Erlang io.format/2 function. fishcakez builds a List of data which he passes to the io.format/2 function. Understanding what this data looks like is key to understanding this algorithm:

["*",(for n<-1..9,do: :io_lib.format"~n~#{9+n}.#{2*n-1}c",'0'),"H"]
# rewritten for clarity
["*",(--comprehension here--),"H"]

So the elixir comprehension builds a list of data, which is inserted between the '*' and the 'H'. So the elixir comprehension builds a list of data, which is inserted between the '*' and the 'H'. Looking at this comprehension in detail we see that it iterates over the rows from 1..9 and binds the value to n like the winning example. He then uses the Erlang io_lib.format/2 function to create a List of Lists describing a particular row. For instance when n = 1 the List output is:

n-1;:io_lib.format"~n~#{9+n}.#{2*n-1}c",'0'
# gives a List of Lists we can print to the console
IO.puts ['\n', [[32, ['  ', 32, 32], '  ', 32, 32], 48]]
# this outputs to the console

        0
:ok

Notice how the first character creates a newline, and then 9 spaces (ASCII character 32), preappended to a 0 (ASCII character 48).

The first part of the algorithm prints 9 spaces and the content of the data. For example:

iex> :io.format"~10s~s~n~10s", ["*", ['\n', [[32, ['  ', 32, 32], '  ', 32, 32], 48]], "H"]
     *
     0
     H:ok

A sophisticated solution and very interesting use of io.format and io_lib.format. Well done.


Submitted by @henrik:

char count 99

d=&List.duplicate/2;s=d.(32,8);IO.puts [s,"*
",(for i<-0..8,do: [d.(32,8-i),d.(?0,i*2+1),10]),s,?H]

This solution uses the newline after the "* to save an extra character instead of using \n. @henrik uses a similar technique as the winning solution, in that he creates an anonymous function and binds it to a variable:

d=&List.duplicate/2

He can then use this function throughout his algorithm to output the various layers of the Christmas tree. The tree is constructed from spaces and values appended to those spaces. For example s = d.(32, 8) will create a list of 8 space characters (ASCII 32) and bind this List to the variable s. By using an elixir List @henrik builds up all the Christmas tree components, which he then outputs to the console with IO.puts.

So the List becomes a List of Lists. The first item being our variable bound to a List of 8 spaces, then a binary String holding a "*\n. Although the newline character \n is actually represented as one character as @henrik sneakily uses an iex carriage return instead of the symbol.

(for i <- 0..8, do: [d.(32, 8 - i), d.(?0, i * 2 + 1), 10])

The algorithm that generates the body of the tree works by building multiple Lists of spaces and characters. A comprehension iterates over each row from 0..8 and binds this value to i. The first item of the generated List gradually decrements a List of spaces until an empty List is left. The second item gradually increments a character list of ASCII represented zeros (0 in ASCII is 48 which is given by ?0 for readability). Finally the last character in the List is the ASCII value 10 which is a newline character.

This is what the algorithm is doing for clarity.

[
  ['        ', '0', 10],
  ['       ', '000', 10],
  ..
  [[], '00000000000000000', 10]
]

Just remember that IO.puts on a List of Lists, including character data (codepoints) will be output as a single binary String value.

Finally @henrik adds the Christmas tree trunk to the end of the master List ,s,?H]. Again he uses the List of 8 spaces to position the trunk which is the ASCII character 72 represented by ?H.

Excellent, there are some really interesting concepts and great use of Lists.


Submitted by @vanstee:

char count 101

d=&List.duplicate/2;p=&IO.puts d.(32,9-&1)++&2;p.(1,'*');for i<-1..9,do: p.(i,d.(48,2*i-1));p.(1,'H')

@vanstee uses two anonymous functions in his algorithm.d=&List.duplicate/2 and p=&IO.puts d.(32,9-&1)++&2. The first function duplicates a List of items, n number of times. Lets look at the second function in a bit more detail:

p=&IO.puts d.(32,9-&1)++&2
# rewritten as:
p = &(IO.puts(d.(32, 9 - &1) ++ &2))
# when run will output
iex(8)> p.(2, '000')
       000
:ok

This function creates a two character Lists and appends them together. The first parameter &1 is the size of the character List of space characters (ASCII 32). The second parameter &2 is another character List of the “body” characters.

To create the star and trunk all @vanstee has to do is invoke his anonymous function with the correct values: p.(1,'*') and p.(1,'H')

Finally he builds the body of the Christmas tree with a comprehension to iterate over each row modifying his anonymous function parameters.

for i<-1..9,do: p.(i,d.(48,2*i-1))
# rewritten as:
for i <- 1..9, do: p.(i, d.(?0, 2 * i - 1))
# which will output:
iex> for i <- 1..9, do: p.(i, d.(?0, 2 * i - 1))
        0
       000
..
00000000000000000
[:ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok]

Well done @vanstee, who also influenced some of the other solutions.


Submitted by @rubysolo:

char count 103

c=&[:string.centre(&1,17),'\n'];IO.puts c.('*')++(for i<-0..8,do: c.(List.duplicate 48,2*i+1))++c.('H')

@rubysolo used the Erlang function :string.centre/2 which he wrapped in an anonymous function that takes a single binary String parameter. As the tree body will only be 9 rows high we know that the width will be a maximum of 17 characters.

The algorithm uses the ++ List append operator to append the star row, the body rows and the trunk row.

for i<-0..8,do: c.(List.duplicate 48,2*i+1)
# rewritten as:
for i <- 0..8, do: c.(List.duplicate(?0, 2 * i + 1))
# which will output a List of Lists
[
  ['        0        '],
  ['       000       '],
  ..
  ['00000000000000000']
]

Good to see the use of Erlang in the solution. Well done.


Submitted by @stoftis:

char count 117

for x<-0..10,y=x<1&&"*"||x>9&&"H"||"0",z=1<x&&x<10&& String.rjust("0",x-1,?0)||"",do: IO.puts String.rjust(z,8)<>y<>z

This solution makes heavy use of Strings. It uses a comprehension differently from many of the other solutions. First it iterates over the full 11 rows i.e. it includes both the star and trunk rows. The comprehension then uses two filters to figure out what to pass to it’s body.

y=x<1&&"*"||x>9&&"H"||"0"
# this filter can be rewritten as:
y = (x < 1) && "*" || x > 9 && "H" || "0"

This filter is passed the row value bound to x if it is 0 then the * is returned if it is 10 then the H is returned else 0 is returned. All these values are bound to the variable y.

z=1<x&&x<10&& String.rjust("0",x-1,?0)||""
# can be rewritten as:
z = ( (x > 1) && (x < 10) && (String.rjust("0", x - 1, ?0)) ) || ""

This filter will bind the String of 0 values to the variable z if the comprehension value of x is in the body range e.g. 1..9 otherwise it returns an empty String. The String of 0 values will adjust according to what row the comprehension is iterating over.

For each row we now have a String value for both y and z, and the body of the comprehension will output these to the console:

IO.puts String.rjust(z,8)<>y<>z

This String.rjust/2 function appends both the values of y and z to it’s result. By using rjust the resulting String is right justified and padded with the default padding , which is whitespace.

Elixir String functions are poweful in their own right and we don’t always have to revert down to using Erlang functions. Good thinking @stoftis


Submitted by @notexactlyawe:

char count 131

import String;a=duplicate " ",8;IO.puts"#{a}*";for i<-1..h do IO.puts"#{duplicate " ",9-i}#{duplicate "0",i*2-1}"end;IO.puts"#{a}H"

This algorithm is another all String solution which builds and outputs a String for each row. First the String module is imported so that we can save characters. Maybe we could wrap this String.duplicate/2 function in an anonymous function to save even more space.

@notexactlyawe then binds 8 blank characters to the a variable which acts as padding for the star and trunk a=duplicate " ",8.

a=duplicate " ",8;IO.puts"#{a}*"
# and
IO.puts"#{a}H"

The body then uses similar techniques we’ve seen before, by building a String of spaces and concatenating it with the body String values, using interpolation "#{}".

for i<-1..h do IO.puts"#{duplicate " ",9-i}#{duplicate "0",i*2-1}"end

A nice use of Strings and String interpolation. Thank you.


Bonus Credit

As a bit of fun we wanted to see what creative versions of the Christmas tree the community could come up with. These solutions aren’t the smallest, but they certainly show off other aspects of the elixir language.

Submitted by @henrik:

import IO.ANSI;r=reset;b=blink_slow;g=green;y=yellow;d=&List.duplicate/2;s=d.(32,8);t=Stream.cycle([[g,?/],[red,?o],[g,?\\],[y,b,?i,r]]);IO.puts [s,y,b,"*\n",r,(for i<-0..8,do: [d.(32,8-i),Enum.take(t,i*2+1),10]),s,r,?H]

This solution was amazing and outputs a coloured Christmas tree with flashing lights, as you can see below. (If you use iTerm2 you will need to turn on the “blink” checkbox, to see it flash).

Really impressive thanks very much.


Submitted by @stoftis:

d=&List.duplicate/2;s=d.(32,8);IO.puts [s,IO.ANSI.yellow <> "* ",(for i<-0..8,do: [d.(32,8-i),d.(Enum.random([IO.ANSI.green, IO.ANSI.red]) <> Enum.random(~W(> = < ~ ~)),i*2+1),10]),s,?H]

A great use of colour and just shows how easy it is to add fun flourshies in elixir code.


Submitted by @emson:

d=&List.duplicate/2;s=d.(32,8);IO.puts [s,"*
",(for i<-0..8,do: [d.(32,8-i),(for x<-1..i*2+1,into: [],do: Enum.random ~W(~ x o ~ ~)),10]),s,?H]
.       *
       ~ox
      ~o~~~
     ~~~~~~o
    ~~o~~~~~~
   ~x~~~x~x~x~
  ~x~~x~~oxo~o~
 ~~~ox~o~o~~~~ox
~~~ooox~~o~o~~~xx
        H

The sigil ~W(~ x o ~ ~) is a very clean way of creating simple Lists. Thanks for this.


Resources

comments powered by Disqus