Elixir Christmas Tree Puzzle #3

Open
emson opened this Issue Dec 4, 2015 · 31 comments

Comments

Projects
None yet
8 participants
@emson
Owner

emson commented Dec 4, 2015

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.

http://elixirgolf.com/articles/elixir-christmas-tree-puzzle/

  1. Please add your solutions as comments to this Github issue
  2. Remember to add your Twitter handle (I will link to it if you get a mention)
    Many thanks, Ben
    When the puzzle is over I'll write them up on http://elixirgolf.com and link back to your Twitter handle
@notexactlyawe

This comment has been minimized.

Show comment
Hide comment
@notexactlyawe

notexactlyawe Dec 4, 2015

Assuming you have h defined beforehand as the number of rows of 0s you want

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

I believe 136 characters. This goes down to 134 when replacing h with 9 (h-1 then cancels down to 8).
twitter: @notexactlyawe

Assuming you have h defined beforehand as the number of rows of 0s you want

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

I believe 136 characters. This goes down to 134 when replacing h with 9 (h-1 then cancels down to 8).
twitter: @notexactlyawe

@emson

This comment has been minimized.

Show comment
Hide comment
@emson

emson Dec 4, 2015

Owner

Great @notexactlyawe you don't need the h=9 for this puzzle - it's all a fixed size. Also you can remove a few brackets:

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"

So you're at 131 chars!

Owner

emson commented Dec 4, 2015

Great @notexactlyawe you don't need the h=9 for this puzzle - it's all a fixed size. Also you can remove a few brackets:

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"

So you're at 131 chars!

@notexactlyawe

This comment has been minimized.

Show comment
Hide comment
@notexactlyawe

notexactlyawe Dec 4, 2015

Cool, thanks :)

Cool, thanks :)

@vanstee

This comment has been minimized.

Show comment
Hide comment
@vanstee

vanstee Dec 4, 2015

Got it down to 101 chars using List.duplicate/2.

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')

I think there might be a way to get a little lower with String.rjust, but the least I could do with that one was 117.

@vanstee on twitter as well.

Edit: D'oh not sure why I included the spaces on the right side.

vanstee commented Dec 4, 2015

Got it down to 101 chars using List.duplicate/2.

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')

I think there might be a way to get a little lower with String.rjust, but the least I could do with that one was 117.

@vanstee on twitter as well.

Edit: D'oh not sure why I included the spaces on the right side.

@henrik

This comment has been minimized.

Show comment
Hide comment
@henrik

henrik Dec 4, 2015

My "own" solution, 104 characters (the linebreak is significant and counts as a single character):

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

10 is the ASCII code for a newline.

If I borrow @vanstee's List.duplicate trick (clever!), I can get down to 99 characters:

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]

32 is the ASCII code for a space. ? (questionmark-space) also works but gives a warning.

All these solutions make use of Elixir's wonderful chardata concept, where IO.puts accepts nested arrays of strings or codepoints and joins them into a string.

henrik on Twitter.

henrik commented Dec 4, 2015

My "own" solution, 104 characters (the linebreak is significant and counts as a single character):

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

10 is the ASCII code for a newline.

If I borrow @vanstee's List.duplicate trick (clever!), I can get down to 99 characters:

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]

32 is the ASCII code for a space. ? (questionmark-space) also works but gives a warning.

All these solutions make use of Elixir's wonderful chardata concept, where IO.puts accepts nested arrays of strings or codepoints and joins them into a string.

henrik on Twitter.

@stoft

This comment has been minimized.

Show comment
Hide comment
@stoft

stoft Dec 5, 2015

My feeble attempts started out something like this (before I realized I was printing 17 lines instead of 17 zeroes...):

       *
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       H

which evolved into this (I think I've got the beginnings of a nice console game though)

       *
       0
      0 
     0  
    0   
   0    
  0     
 0      
0       
0        
0         
0          
0           
0            
0             
0              
0               
       H

and then into the most lopsided tree ever:

       *
       0
      00
     000
    0000
   00000
  000000
 0000000
00000000
000000000
0000000000
00000000000
000000000000
0000000000000
00000000000000
000000000000000
0000000000000000
       H

It was cracking me up quite a bit though. 😂

stoft commented Dec 5, 2015

My feeble attempts started out something like this (before I realized I was printing 17 lines instead of 17 zeroes...):

       *
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       0
       H

which evolved into this (I think I've got the beginnings of a nice console game though)

       *
       0
      0 
     0  
    0   
   0    
  0     
 0      
0       
0        
0         
0          
0           
0            
0             
0              
0               
       H

and then into the most lopsided tree ever:

       *
       0
      00
     000
    0000
   00000
  000000
 0000000
00000000
000000000
0000000000
00000000000
000000000000
0000000000000
00000000000000
000000000000000
0000000000000000
       H

It was cracking me up quite a bit though. 😂

@stoft

This comment has been minimized.

Show comment
Hide comment
@stoft

stoft Dec 5, 2015

And the hilarity just continues!

      *
       0
       000
       00000
       0000000
       000000000
       00000000000
       0000000000000
       000000000000000
                       H
       *
       00
       0  
       0  0
       0    
       0    0
       0      
       0      0
       0        
       0        0
       0          
       0          0

stoft commented Dec 5, 2015

And the hilarity just continues!

      *
       0
       000
       00000
       0000000
       000000000
       00000000000
       0000000000000
       000000000000000
                       H
       *
       00
       0  
       0  0
       0    
       0    0
       0      
       0      0
       0        
       0        0
       0          
       0          0
@stoft

This comment has been minimized.

Show comment
Hide comment
@stoft

stoft Dec 5, 2015

Finally! My humble solution of way too many chars (I have yet to look at the other solutions mind you):

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

stoft commented Dec 5, 2015

Finally! My humble solution of way too many chars (I have yet to look at the other solutions mind you):

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

This comment has been minimized.

Show comment
Hide comment
@stoft

stoft Dec 5, 2015

...and a solution that's 2 chars shorter than my previous one! (well aware that I can shave whitespace and similar from my previous solution) But this one is so "stupid" I have to post it. :)

IO.puts "        *
        0
       000
      00000
     0000000
    000000000
   00000000000
  0000000000000
 000000000000000
00000000000000000
        H"

stoft commented Dec 5, 2015

...and a solution that's 2 chars shorter than my previous one! (well aware that I can shave whitespace and similar from my previous solution) But this one is so "stupid" I have to post it. :)

IO.puts "        *
        0
       000
      00000
     0000000
    000000000
   00000000000
  0000000000000
 000000000000000
00000000000000000
        H"
@stoft

This comment has been minimized.

Show comment
Hide comment
@stoft

stoft Dec 5, 2015

My original solution with trimmings (pun intended), 117 chars (which I'm pretty happy with):

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

edit: @stoftis on twitter

stoft commented Dec 5, 2015

My original solution with trimmings (pun intended), 117 chars (which I'm pretty happy with):

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

edit: @stoftis on twitter

@stoft

This comment has been minimized.

Show comment
Hide comment
@stoft

stoft Dec 5, 2015

And now reading through the comments I realized I probably duplicated @vanstee 's solution he mentioned but never posted. :)

stoft commented Dec 5, 2015

And now reading through the comments I realized I probably duplicated @vanstee 's solution he mentioned but never posted. :)

@emson

This comment has been minimized.

Show comment
Hide comment
@emson

emson Dec 5, 2015

Owner

That's great... although I think the tree needs to be a bit more festive:

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

emson commented Dec 5, 2015

That's great... although I think the tree needs to be a bit more festive:

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

This comment has been minimized.

Show comment
Hide comment
@stoft

stoft Dec 5, 2015

Oh I agree!

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]

🎅 🎄 ⛄️ 🎁

stoft commented Dec 5, 2015

Oh I agree!

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]

🎅 🎄 ⛄️ 🎁

@emson

This comment has been minimized.

Show comment
Hide comment
@emson

emson Dec 5, 2015

Owner

Or this one is even better...

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]

Which gives:

        *
       ~ox
      ~o~~~
     ~~~~~~o
    ~~o~~~~~~
   ~x~~~x~x~x~
  ~x~~x~~oxo~o~
 ~~~ox~o~o~~~~ox
~~~ooox~~o~o~~~xx
        H
Owner

emson commented Dec 5, 2015

Or this one is even better...

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]

Which gives:

        *
       ~ox
      ~o~~~
     ~~~~~~o
    ~~o~~~~~~
   ~x~~~x~x~x~
  ~x~~x~~oxo~o~
 ~~~ox~o~o~~~~ox
~~~ooox~~o~o~~~xx
        H
@henrik

This comment has been minimized.

Show comment
Hide comment
@henrik

henrik Dec 6, 2015

Hehe, I've also been meaning to make a festive one.

This one assumes your terminal allows ANSI blinking. Did some things to keep it short but I haven't gone full golf on it:

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]

Even without blinking enabled in the terminal, you could probably use IO.ANSI.clear and a timer if anyone wants to go further with this :D

henrik commented Dec 6, 2015

Hehe, I've also been meaning to make a festive one.

This one assumes your terminal allows ANSI blinking. Did some things to keep it short but I haven't gone full golf on it:

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]

Even without blinking enabled in the terminal, you could probably use IO.ANSI.clear and a timer if anyone wants to go further with this :D

@emson

This comment has been minimized.

Show comment
Hide comment
@emson

emson Dec 6, 2015

Owner

@stoft & @henrik oh man they are great. Brilliant. I have to say I laughed out loud when I saw the flashing lights.
Well done

Owner

emson commented Dec 6, 2015

@stoft & @henrik oh man they are great. Brilliant. I have to say I laughed out loud when I saw the flashing lights.
Well done

@rubysolo

This comment has been minimized.

Show comment
Hide comment
@rubysolo

rubysolo Dec 6, 2015

Best I can do so far is 103 characters:

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

Note that it has an extra blank line below the trunk. If that is not acceptable, I could switch IO.puts to IO.write, then I'm at 104 characters.

@rubysolo

rubysolo commented Dec 6, 2015

Best I can do so far is 103 characters:

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

Note that it has an extra blank line below the trunk. If that is not acceptable, I could switch IO.puts to IO.write, then I'm at 104 characters.

@rubysolo

@henrik

This comment has been minimized.

Show comment
Hide comment
@henrik

henrik Dec 7, 2015

@rubysolo Oh, neat discovery with :string.centre!

henrik commented Dec 7, 2015

@rubysolo Oh, neat discovery with :string.centre!

@emson

This comment has been minimized.

Show comment
Hide comment
@emson

emson Dec 7, 2015

Owner

@rubysolo thanks for your solution. Great that you used Erlang. I think it's all to easy to think of elixir as one language, and forget that you have this whole other ecosystem. Well done!
PS for this puzzle I haven't implemented @henrik elixir test Gist - therefore I'm happy to ignore the extra blank line.

Owner

emson commented Dec 7, 2015

@rubysolo thanks for your solution. Great that you used Erlang. I think it's all to easy to think of elixir as one language, and forget that you have this whole other ecosystem. Well done!
PS for this puzzle I haven't implemented @henrik elixir test Gist - therefore I'm happy to ignore the extra blank line.

@fishcakez

This comment has been minimized.

Show comment
Hide comment
@fishcakez

fishcakez Dec 8, 2015

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

89 characters

We did this at the Elixir Southampton Meetup

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

89 characters

We did this at the Elixir Southampton Meetup

@emson

This comment has been minimized.

Show comment
Hide comment
@emson

emson Dec 8, 2015

Owner

Excellent thanks a lot. @fishcakez could you add your Twitter handle and I'll link the newsletter to you, if you are ok with it? Thanks a lot. Maybe you could come up to the Edinburgh elixir meet up 😉

Owner

emson commented Dec 8, 2015

Excellent thanks a lot. @fishcakez could you add your Twitter handle and I'll link the newsletter to you, if you are ok with it? Thanks a lot. Maybe you could come up to the Edinburgh elixir meet up 😉

@henrik

This comment has been minimized.

Show comment
Hide comment
@henrik

henrik Dec 8, 2015

@fishcakez Very cool solution! I count 100 chars, but you can get it to 96 by trimming some parentheses and spaces:

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

henrik commented Dec 8, 2015

@fishcakez Very cool solution! I count 100 chars, but you can get it to 96 by trimming some parentheses and spaces:

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

This comment has been minimized.

Show comment
Hide comment
@fishcakez

fishcakez Dec 8, 2015

@henrik ah good spot. Forgot to escape interpolation so our counting method messed up. With that parentheses trick maybe we got a shorter one.

@emson, sorry not got twitter. But we messed up counting. Will have too see about going to one if I can bring in some other things.

@henrik ah good spot. Forgot to escape interpolation so our counting method messed up. With that parentheses trick maybe we got a shorter one.

@emson, sorry not got twitter. But we messed up counting. Will have too see about going to one if I can bring in some other things.

@fishcakez

This comment has been minimized.

Show comment
Hide comment
@fishcakez

fishcakez Dec 9, 2015

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

94 by using ~10s.

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

94 by using ~10s.

@ventsislaf

This comment has been minimized.

Show comment
Hide comment
@ventsislaf

ventsislaf Dec 9, 2015

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

92 characters.

I did this after the Southampton Elixir Meetup. Thanks for the great time guys and specially @fishcakez for coming all the way there!

Twitter handle: @ventsislaf

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

92 characters.

I did this after the Southampton Elixir Meetup. Thanks for the great time guys and specially @fishcakez for coming all the way there!

Twitter handle: @ventsislaf

@henrik

This comment has been minimized.

Show comment
Hide comment
@henrik

henrik Dec 9, 2015

@ventsislaf Wow. You can shave another two off for an even 90:

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

I intentionally did not try for centering since the example output of the challenge had no right-side spaces, but the visual output is the same – it's fun seeing all kinds of solutions.

henrik commented Dec 9, 2015

@ventsislaf Wow. You can shave another two off for an even 90:

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

I intentionally did not try for centering since the example output of the challenge had no right-side spaces, but the visual output is the same – it's fun seeing all kinds of solutions.

@emson

This comment has been minimized.

Show comment
Hide comment
@emson

emson Dec 9, 2015

Owner

Great solution well done... for a moment I thought you could change the Erlang char '*' to ?* but it appears because it's an Erlang function you can't. Interesting thanks!

Owner

emson commented Dec 9, 2015

Great solution well done... for a moment I thought you could change the Erlang char '*' to ?* but it appears because it's an Erlang function you can't. Interesting thanks!

@ventsislaf

This comment has been minimized.

Show comment
Hide comment
@ventsislaf

ventsislaf Dec 9, 2015

@henrik nice! I couldn't spot that after all the minimizing.

@emson I had the same thought 😆

I have to say that this is probably the most unreadable 90 characters of code I've ever wrote. Great fun, thank you for the quiz!

@henrik nice! I couldn't spot that after all the minimizing.

@emson I had the same thought 😆

I have to say that this is probably the most unreadable 90 characters of code I've ever wrote. Great fun, thank you for the quiz!

@fishcakez

This comment has been minimized.

Show comment
Hide comment

@ventsislaf well done!

@henrik

This comment has been minimized.

Show comment
Hide comment
@henrik

henrik Dec 9, 2015

@emson I don't think it's so much about being an Erlang function – ?* is the same as 42; '*' is the same as [42]; '*' is thus also the same as [?*]. So some Erlang functions require an Erlang string (array of characters), others accept standalone characters/integers. Elixir functions make similar distinctions.

The reason I could use ?* and friends was because I made use of Elixir chardata, which has a kind of equivalent in Erlang iolists/iodata. In some contexts, you're allowed to mix strings with standalone characters and the language makes sense of it all. I've been meaning to blog about this some day – it's a really useful concept.

henrik commented Dec 9, 2015

@emson I don't think it's so much about being an Erlang function – ?* is the same as 42; '*' is the same as [42]; '*' is thus also the same as [?*]. So some Erlang functions require an Erlang string (array of characters), others accept standalone characters/integers. Elixir functions make similar distinctions.

The reason I could use ?* and friends was because I made use of Elixir chardata, which has a kind of equivalent in Erlang iolists/iodata. In some contexts, you're allowed to mix strings with standalone characters and the language makes sense of it all. I've been meaning to blog about this some day – it's a really useful concept.

@rubysolo

This comment has been minimized.

Show comment
Hide comment
@rubysolo

rubysolo Dec 10, 2015

Here's a different approach -- it's not anywhere near competitive at 150-ish characters, but it was fun to play with, so I thought I'd share it. 😄

m=' *H0'
c=fn
x,x when x<0->x*-1
_,x when x<0->0
_,_->3 end
z=&Enum.at m,c.(&1,&1-abs &2-9)
for i<-1..11,do: IO.puts Enum.map 1..17, &z.(rem(i,11)-2,&1)

Here's a different approach -- it's not anywhere near competitive at 150-ish characters, but it was fun to play with, so I thought I'd share it. 😄

m=' *H0'
c=fn
x,x when x<0->x*-1
_,x when x<0->0
_,_->3 end
z=&Enum.at m,c.(&1,&1-abs &2-9)
for i<-1..11,do: IO.puts Enum.map 1..17, &z.(rem(i,11)-2,&1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment