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

New Year Countdown in Elixir #5

Open
emson opened this issue Dec 18, 2015 · 13 comments
Open

New Year Countdown in Elixir #5

emson opened this issue Dec 18, 2015 · 13 comments

Comments

@emson
Copy link
Owner

emson commented Dec 18, 2015

The puzzle is to create a countdown timer that displays the number of Days:Hours:Minutes:Seconds until the next New Year. This countdown timer will decrease every cond until 000:00:00:00.
See the website for more details : http://elixirgolf.com/articles/new-year-countdown-in-elixir/

  • The code will be executed using the elixir countdown.exs terminal command. We will manually copy all your solutions into this file, so just add comments below to submit your solutions.
  • The timer will be in the following format Days:Hours:Minutes:Seconds
  • The time should be represented as numbers, separated by colons: 000:00:00:00
  • Every second the timer should decrease by 1 second, and all the other fields should adjust accordingly.
  • The timer should only display one timer at any time, i.e. it should have some mechanism to clear the screen or update itself every second. It should NOT display a long list of times, like the example above.
  • The timer should countdown to the next New Year e.g. January 1st 2016.

As a bit of fun and a side project maybe consider the following flourishes:

  • Colour the numbers
  • Allow the countdown timer to dynamically adjust to the following New Year after this one
  • Trigger some exciting output when the countdown reaches 000:00:00:00.
  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
@padde
Copy link

padde commented Dec 18, 2015

213 chars (214 bytes)

  • the timer sends message 0 every second to the current process, this will hopefully prevent timing issues (printing + sleep 1000 will actually take longer than a second and eventually results in a skipped second)
  • stole the trick of encoding the number 1000 as from @henrik
  • Used the f = fn(f) -> f.(f) end trick to be able to do recursion without having to define a module
  • 2016-01-01 00:00 is hard-coded as 1451606400
  • used :erlang.system_time 1 to get the current unix time in seconds
  • Used the DEL character \r to clear the line before printing the new values
:timer.send_interval ,0
f=fn f->receive do 0->t=1451606400-:erlang.system_time 1
s=rem t,60
t=div t,60
m=rem t,60
t=div t,60
:io.format'\r~3..0B:~2..0B:~2..0B:~2..0B',[div(t,24),rem(t,24),m,s]
end
f.(f)end
f.(f)

Twitter: @der_padde

@henrik
Copy link

henrik commented Dec 19, 2015

176 characters (counting newlines):

for x<-1451606400-:erlang.system_time(1)..0do
m=div x,60
s=rem x,60
h=div m,60
m=rem m,60
:io.fwrite"~3..0B:~2..0B:~2..0B:~2..0B\r",[div(h,24),rem(h,24),m,s]
:timer.sleep end

(Stole the shorter :erlang.system_time(1) from @padde, who also suggested inlining the div and rem – thank you!)

An important difference to @padde's solution is that this one is likely to drift. See notes below.

What it does:

  • Calculates the number of seconds between now and new year 2016.
  • Loops from that number down to 0.
  • On each iteration, it calculates times using a "divmod" strategy (integer division, and the modulus of the same).
  • It uses format strings to output this.
  • It uses \r (carriage return) to rewrite the same line. Since the line length never shrinks (thanks to the specified zero-padding), we don't need to clear out the line first.
  • It sleeps for a second before the next iteration.

Notes:

  • Hard-coded to UTC new year 2016 (the magic number 1451606400 is that timestamp)
  • The implementation is obviously a bit stupid (probably wouldn't handle e.g. computer sleep gracefully, and risks some drift since each iteration takes more than zero time) – optimised for length :)
  • Probably behaves weirdly if run after the fact (but it does stop nicely at 0, if run before)
  • is 1000. Figured it out like so: to_string [1000]

Some abandoned ideas:

  • Tried using hexadecimal for the hardcoded timestamp but it was the same length (0x5685C180)
  • Tried to abbreviate :os.system_time(:seconds) with something like :os.system_time/1.0e9 but with little luck since you get a float, not an int.

My Gist is here if you want to see a bit of the progression: https://gist.github.com/henrik/856b02213fddb7829fdd

Twitter: @henrik

@padde
Copy link

padde commented Dec 19, 2015

@henrik quick win: by inlining the div/rem as I did, you can shave off another 10 chars or so. I tried hard to extract a function for this but it was always much longer than writing it directly.

If you would allow me to criticise your solution: it will run slow rather quickly because doing the computations and printing, then sleeping for a second will actually take longer than a second.

@henrik
Copy link

henrik commented Dec 19, 2015

@padde Good call on the div/rem. Never tried the non-abbreviated version there :) Thank you!

Yeah, I realized pretty quickly that it is prone to drift (put it in my notes above). I might make a stab at a drift-safe one, though you've done a good job of that already :)

@henrik
Copy link

henrik commented Dec 19, 2015

@padde Looked for ways to abbreviate yours; not easy… If one doesn't care about performance, seems it can be shortened to skip the whole interval thing and just have a very CPU-wasteful loop:

f=fn f->t=1451606400-:erlang.system_time 1
s=rem t,60
t=div t,60
m=rem t,60
t=div t,60
:io.format'\r~3..0B:~2..0B:~2..0B:~2..0B',[div(t,24),rem(t,24),m,s]
f.(f)end
f.(f)

@emson
Copy link
Owner Author

emson commented Dec 19, 2015

Oh wow... very interesting. It's amazing what these puzzles bring out. Well done @padde and @henrik

@padde
Copy link

padde commented Dec 20, 2015

@henrik nice idea! However it seems to be stuck at the first second. I guess the massive amount of printing takes way too long for this to work.

Maybe one could combine that approach with sleeping for some time. According to the sampling theorem we need a total cycle time that is below 500ms in order not to miss a second. So we should be safe with a sleep time < (500ms - max(time for other computations)). In theory we cannot predict how long it will take, but in practice we should be safe with a sleep time of say 100ms (or ?d).

f=fn f->t=1451606400-:erlang.system_time 1
s=rem t,60
t=div t,60
m=rem t,60
t=div t,60
:io.format'\r~3..0B:~2..0B:~2..0B:~2..0B',[div(t,24),rem(t,24),m,s]
:timer.sleep ?d
f.(f)end
f.(f)

For the record: this joint effort takes 185 chars (and an equal amount of bytes since there are no multi byte characters involved).

@henrik
Copy link

henrik commented Dec 31, 2015

@padde Hm, stuck at the first second? Seems to run fine for me, though it spikes the CPU. Haven't left it running for a long time, maybe it degrades.

@padde
Copy link

padde commented Jan 6, 2016

@henrik checked again, still does not work for me. Very strange. Are you running the latest versions of Erlang and Elixir?

@henrik
Copy link

henrik commented Jan 6, 2016

@padde Yeah, now running Elixir 1.2 and Erlang/OTP 18. On OS X.

Bumped the timestamp since it's past new year's:

countdown

@padde
Copy link

padde commented Jan 7, 2016

@henrik me too. It seems to update the time once as soon as I send Ctrl-C to iex though:

ezgif com-video-to-gif

@henrik
Copy link

henrik commented Jan 7, 2016

I use iTerm - maybe it has a problem in Terminal.app? Will try that tomorrow.

@padde
Copy link

padde commented Jan 7, 2016

I think I found it. This seems to be a tmux problem, don't really understand why this happens though.

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

3 participants