Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elixir Christmas Tree Puzzle #3

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

Elixir Christmas Tree Puzzle #3

emson opened this issue Dec 4, 2015 · 31 comments

Comments

@emson
Copy link
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
Copy link

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
Copy link
Owner Author

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
Copy link

Cool, thanks :)

@vanstee
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link
Owner Author

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
Copy link

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
Copy link
Owner Author

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
Copy link

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
Copy link
Owner Author

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
Copy link

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
Copy link

henrik commented Dec 7, 2015

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

@emson
Copy link
Owner Author

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
Copy link

: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
Copy link
Owner Author

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
Copy link

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
Copy link

@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
Copy link

: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
Copy link

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
Copy link

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
Copy link
Owner Author

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
Copy link

@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
Copy link

@ventsislaf well done!

@henrik
Copy link

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
Copy link

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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants