I really recommend that you have a go at it yourself first, it is much more rewarding to solve it on your own! My solution is in no way the optimal one, but still: Here be spoilers, etc! With that out of the way, here is a short description of the problem.
The code golf site allows entries in perl, python, ruby or php. I settled on perl, partly for traditional reasons, but also it's the one I'm most familiar with of the four, even if I don't do much programming in it any more.
In this challenge you must draw an analogue clock face, using the time provided to you in 24-hour digital format.
Example time (given on stdin):21:35
First things first, parse the in-data. The obvious way would be to use a regexp in this fashion:
but since I don't care about readability here I pull a perl-trick out of the tool chest:
This matches the ':' and leaves me with the left hand side/hours in $` and the right hand side/minutes in $'. (Yeah, I kept the 'perlvar' man-page up in a window for the entire session.)
Now I want to place the "hands" in an array @c with 12 positions. Each position should contain 0 for no hand, 1 for the hour, 2 for the minute and 3 if both are overlapping:
$c[$`%12]++;Simple enough. All that is left now is the tricky business of drawing the face.
After a couple of false starts I went with a template.
$_="hAxdLgBxxaKmCxxJoDxxaImExxdHgFxhG"I put it into $_ so I don't have to specify which variable I want to do the operations on. Starting with this I first insert newlines:
s/x/\n/g;Then convert small-caps to spaces (a=1 space, b=2 spaces ...):
s/[a-o]/' 'x(ord($&)-96)/ge;the 'e' flag at the end of the regexp lets the replace-part be evaluated as code. $& contains the latest match, ord gives the ascii-value of the character, and ' 'x value, gives value repetitions of the ' ' string.
And finally, the capital letters are replaced with the hands from @c, and output:
/[A-L]/(o,h,'m',x)[$c[ord($&)-65]]/ge; printIn the same manner as the previous line, the replace is evaluated. (o,h,'m'x)[value] is just an array-lookup that converts 0,1,2,3 to 'o','h','m' or 'x'. I discovered that only 'm' need to be in quotes (it's a match operator), the other characters got interpreted as strings by themselves.
All put together it looks like this:
<>=~/:/;The newlines should of course be removed to save a couple of bytes, but I've kept them here for readability.
Success, 154 bytes. Short enough for an SMS, but not quite short enough to fit in a twitter-message.
The #1 spot on the Code Golf-site had solved it in 101 bytes, so I was motivated to at least try to get it twitter-sized.
First thing I realized was that I didn't have to write '\n' for newlines, I could just simple use newlines in the code, which made that replacement unnessary. So I ended up with a template looking like this:
$_="hAAlso, I could shave some bytes off by merging the two replace-operations into one:
s/\S/($t=ord($&)-96)>0?' 'x$t:(o,h,'m',x)[$c[$t+31]]/ge;This got me down to 133 bytes. And something looking like this:
Good enough for my 140-byte "twitter-limit". I was about to quit there for the evening when I realized I could remove the capital letters from the template completely:
<>=~/:/;$c[$`%12]++;$c[$'/5]+=2;$_="hNot completely satisfied with the algorithm "abs( ($t++ %2)*12 - $t/2)"that supplies the sequence (0, 11, 1, 10, 2, 9, 3, 8, 4, 7, 5, 6) for the index to the hands, but it got me down to a total of 126 bytes.
And that was that! Now all I have to do is remove another 25 characters from that and I'm in the #1 spot...
I've done another one of the code golf challenges, I'll see if I'll do a write up on it later.