Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Since I saw the first one a few years ago, I always was subjugated by this kind of word clock where the time is actually spelled out by words being lit up or not into a meaningful sentence.

A Word Clock

The text displayed on that clock is the following.

IT IS HALF TEN
QUARTER TWENTY
FIVE MINUTES TO
PAST TWO THREE
ONE FOUR FIVE
SIX SEVEN EIGHT
NINE TEN ELEVEN
TWELVE O'CLOCK

Your task is to write such a working clock. Words are lit up if and only if they're relevant to printing the current time. Otherwise, they're lit down. Words are "lit up" by being printed and are "lit down" by being replaced by a number of spaces being the length of the word.

Example: if the current time is 17:23, the printed text must be exactly the following:

IT IS 
        TWENTY
FIVE MINUTES
PAST
         FIVE

Rules

  1. The time printed is the 12h variant, but without AM/PM.
  2. The rounding is done on the base of minutes only (seconds are totally irrelevant). The rounding is done to the closest multiple of 5. So, for example, even though 17:52:38 really is closest to 17:55, but since the seconds are irrelevant, 17:52 is actually rounded down to 17:50, and the text printed is "IT IS TEN MINUTES TO SIX" (with relevant spacing). So if XX is a multiple of five, XX will be used from HH:(XX-2):00 until HH:(XX+2):59. The word MINUTES must appear if FIVE, TEN or TWENTY are lit up in the minutes section (before "TO" or "PAST").
  3. All irrelevant words are replaced by as many spaces as needed to keep the text where it is located in the template above. Trailing spaces may be trimmed. Spaces relevant to keeping the text at the expected position must be kept.
  4. Trailing lines may be trimmed as well. Relevant empty lines are still required to appear. Example:

    IT IS      TEN
    
         MINUTES
    PAST TWO
    
  5. Do not light up TEN on the first line or FIVE on the third line when these values refer to the hours.

  6. You may accept an input. If you accept an input, the input will be the time to print in any valid format you want (string, list of integers, native time type your language support, ...), but no parameters are allowed if they're not related to the time to print. If you support no input, then you must use the current time. If you support both, that's better but there's no bonus ;)
  7. Your code may be a program, a function, a lambda but not snippet.
  8. If your language supports printing in any way, it must print the result (in a file, on the standard output, I don't mind). If your language doesn't support printing in any way, it is allowed to simply "return" the expected text. The result may be either all uppercase or all lowercase, not a mix of both.
  9. Standard loopholes apply.
  10. This is code-golf so the shortest code wins!
  11. In the measure of possible, please provide a link to an online interpreter of your language.

Test cases

Input:  <no input> (the current local time is 19:20)
Output:     
IT IS
        TWENTY
     MINUTES
PAST 

    SEVEN

Input: 13:15
Output: (empty line is being printed)
IT IS
QUARTER

PAST
ONE

Input: 13:58
Output: (rounding)
IT IS


     TWO



       O'CLOCK

Input: 14:30
Output: (half is always a edge-case)
IT IS HALF


PAST TWO

Input: 15:35
Output: (light up "TO")
IT IS
        TWENTY
FIVE MINUTES TO

    FOUR

Input: 10:00
Output: (do not use the TEN or FIVE on the first line when referring to the hours)
IT IS





    TEN
      O'CLOCK

Input: 12:00
Output: (O'CLOCK and a lot of empty lines)
IT IS






TWELVE O'CLOCK
share|improve this question
3  
Great challenge, I really like it! – Timtech 13 hours ago

TI-Basic, 335 334 bytes

Pretty much perfect, since the TI-84 calcs have 8x16 letter displays, and this is 8x15. Date is taken as input in order to support calcs earlier than TI-84, which do not have internal clocks. With no arguments, Input gets input by default into X and Y, similar to Prompt X,Y. So make X be hours (anything >=0 will work correctly) and have Y be minutes.

Input 
ClrHome
int(Y/5-5.6
Output(1,1,"IT IS
If Ans=~6
Output(8,8,"O'CLOCK
If 2=abs(3-abs(Ans
Output(3,1,"FIVE
If 4=abs(Ans
Output(1,12,"TEN
If 3=abs(Ans
Output(2,1,"QUARTER
If 2=abs(Ans
Output(2,8,"TWENTY
If sum(abs(Ans)={1,2,4,5
Output(3,5,"MINUTES
If not(Ans
Output(1,7,"HALF
If Ans<1 and Ans+6
Output(4,1,"PAST
If Ans>0
Output(3,14,"TO
12fPart(12(^~1)(X+(Y>32->A
If not(Ans
12->A
{21,6,10,25,30,41,45,51,61,66,70,81
.05Ans(A
Output(int(4+Ans),20fPart(Ans),sub("ONE   TWO   THREE FOUR  FIVE  SIX   SEVEN EIGHT NINE  TEN   ELEVENTWELVE",6A-5,6

Note 1: For TI-84+ you can replace Input with something like getTime:Ans(1->X:getTime:Ans(2->Y. Also, int(Y/5-5.6 is equivalent to round(Y/5,0)-6. And no, in this case int( could not be interchanged with iPart(.

Note 2: This prints the input just fine, but for asethetic reasons you probably want Pause at the end of the program to avoid the Done message upon program termination.

Edit: Byte count lowered because tokenized (see note on previous revision), also a bug is fixed thanks to @Neil. Third, fixed a bug I found myself which would have costed 2 bytes but the optimization actually saved 1 byte overall, and I also realized that I am getting input so I don't need to call getTime (duh?). Last, for anyone who wants to confirm this byte count, the only two byte token is sub(.

share|improve this answer
    
It seems to me that after outputting O'CLOCK the code will go on to output PAST too... – Neil 3 hours ago
    
Thanks for the catch there @Neil -- you're right. That's fixed now, thanks! – Timtech 3 hours ago
    
It's impressive to see a TI-Basic answer leading the scoreboard, congrats ! – Aaron 2 hours ago
    
Thanks @Aaron, I guess it's because of the low byte count. Maybe I'll add a complete explanation to explain the golfs. – Timtech 2 hours ago
1  
I count 545 bytes could you please enlighten me? – Christoph 1 hour ago

PHP, 387 384 353 bytes

<?foreach($a=explode(_,"IT IS _HALF _TEN_
_QUARTER _TWENTY_
_FIVE _MINUTES _TO_
_PAST _TWO _THREE_
_ONE _FOUR _FIVE_
_SIX _SEVEN _EIGHT_
_NINE _TEN _ELEVEN_
_TWELVE _O'CLOCK")as$i=>$w)echo$w<A||strstr(["`",df,d,c,a,f][abs(6-$m=$date_i/5+.5|0)].($m%3?_g:_).($m%12?$m<7?j:h:z).nkloprsrvwxy[$m<7?$date_h-1:$date_h%12],chr($i+95))?$w:str_pad("",strlen($w));

loops through data and checks if either the current element is a newline or its index is in a generated string that contains the indexes of the words to light up (ascii=index+95).

May yield notices if you dont´t use the default value for error_reporting (22519).

Test it online.

breakdown

                                            // loop through words
<?foreach($a=explode(_,"IT IS _HALF _TEN_
_QUARTER _TWENTY_
_FIVE _MINUTES _TO_
_PAST _TWO _THREE_
_ONE _FOUR _FIVE_
_SIX _SEVEN _EIGHT_
_NINE _TEN _ELEVEN_
_TWELVE _O'CLOCK")as$i=>$w)echo$w<A||strstr(    // if word is newline or in
     ["`",df,d,c,a,f][abs(6-$m=$date_i/5+.5|0)]         // minutes
    .($m%3?_g:_)                                        // "it is", "minutes"
    .($m%12?$m<7?j:h:z)                             // "past"/"to"/"o'clock"
    .nkloprsrvwxy[$m<7?$date_h-1:$date_h%12]        // hours (-1 if minutes<=30; %12 else)
,chr($i+95))?$w:str_pad("",strlen($w));             // then print word, else print spaces

Golfs:

  • $x/5+.5|0 is two bytes shorter than round($x/5).
  • Two calls of date are one byte shorter than assigning the date result to a variable.
  • Using a single assigment golfed away the variable that the light-up indexes were stored in.
  • A string for the light-up indexes instead of an array saved around 30 bytes.
  • $w<A is three bytes shorter than $w=="\n" - make sure that no space is after a delimiter!
  • abs saved 8 bytes on the minutes word.
  • Thanks @Christoph: Moving from numbers to letters for the map rendered the separators obsolete and allowed one more string instead of array; saved 23+8 bytes.
    Using underscore as the first index allowed to include it into the "it is" alternatives; and it rendered the quotation for the hours obsolete.
share|improve this answer
    
This is very nice! I tried this implementation in Java when making sure the question was solvable, but it ended up being so complicated I gave up and used a longer, but more straightforward approach! So congrats on succeeding where I failed ;) – Olivier Grégoire 1 hour ago
1  
$a=is unnecessary as far as I can tell. By replacing the numbers in strstr with chars and replacing _.$i._ with chr(65+$i) I golfed down your version to 361. – Christoph 1 hour ago
1  
@Christoph: I got it down to 355 with that; thanks. Golfing on it. – Titus 55 mins ago

Batch, 789 bytes

@echo off
if not "%1"=="" setlocal&set time=%1
set/a"h=%time:~0,2%,m=6%time:~3,2%%%60+2,h+=m/35,h%%=12,m=6-m/5%%12,n=m%%3,o=m%%2
set p=
set s=
call:c %m:-=% "IT IS HALF" 0 "IT IS      TEN" 4 "IT IS"
call:c %m:-=% QUARTER 3 "        TWENTY" 2 "        TWENTY" 1
if %m% lss 0 set s= TO
call:c 0 "            " %n% "     MINUTES" %o% "FIVE MINUTES"
set s=
set p=PAST 
if %m%==6 set p=     
if %m% lss 0 set p=     
call:c %h% TWO 2 "    THREE" 3
set p=
call:c %h% ONE 1 "    FOUR" 4 "          FIVE" 5
call:c %h% SIX 6 "    SEVEN" 7 "          EIGHT" 8
call:c %h% NINE 9 "     TEN" 10 "         ELEVEN" 11
if %m%==6 set s= O'CLOCK
call:c %h% TWELVE 0 "      "
exit/b
:c
set t=%1
:l
if %t%==%3 echo(%p%%~2%s%&exit/b
shift
shift
if not "%3"=="" goto l
echo(%p%%~2%s%

Note: Trailing space after PAST and 5 trailing spaces on each of the following two lines.

share|improve this answer
    
I ran it and it does just fine (bar one missing space for the hour 3)! I just don't understand what's going on (with call:c for instance)! I should probably learn some batch :) – Olivier Grégoire 1 hour ago
    
@OlivierGrégoire Sadly a space had gone missing but that's fixed now. – Neil 1 hour ago

JavaScript, 567 564 551 542 529 527 519 bytes

f=(h,m)=>{c={m6:"HALF ",m2:"TEN\n",m3:"QUARTER ",m4:"TWENTY\n",m1:"FIVE ",m:"MINUTES ",t:"TO\n",p:"PAST ",h2:"TWO ",h3:"THREE\n",h1:"ONE ",h4:"FOUR ",h5:"FIVE\n",h6:"SIX ",h7:"SEVEN ",h8:"EIGHT\n",h9:"NINE ",h10:"TEN ",h11:"ELEVEN\n",h0:"TWELVE ",m0:"O'CLOCK"};l=[];p="push";m=Math.round(m/5);if(m>11){m=0;h++}if(m>6){l[p]("t");m=12-m;h++}else if(m)l[p]("p");if(m%3)l[p]("m");if(m==5)l[p]("m4","m1");else l[p]("m"+m);l[p]("h"+h%12);s="IT IS ";for(k in c)s+=(l.indexOf(k)>=0)?c[k]:c[k].replace(/\S/g," ");console.log(s)}

f(19, 20);
f(13, 15);
f(13, 58);
f(14, 30);
f(15, 35);
f(10, 0);
f(12, 0);

I don't know if it's OK to take hours and minutes as arguments. You said "list of integers". Does it still count?

It's my first time golfing, by the way. What do you think?


Edit: Saved a few bytes by dividing minutes. Thanks, Olivier!

share|improve this answer
    
Yes, the idea behind that rule is that you can take arguments, but only if they relate to the input, which is the time to print. – Olivier Grégoire 2 hours ago
1  
Regarding the golfing, you can probably golf some bytes if you consider dividing your minutes by 5. Also, you might want to move c inside your function. You shouldn't lose any byte by doing so, I guess, but this makes it more like a snippet than a function (to me, at least, maybe JS gurus will say otherwise). – Olivier Grégoire 2 hours ago

JavaScript (ES6), 291 bytes

Takes input as two integers with currying syntax (h)(m)

h=>m=>`IT IS HALF TEN
QUARTER TWENTY
FIVE MINUTES TO
PAST TWO THREE
ONE FOUR FIVE
SIX SEVEN EIGHT
NINE TEN ELEVEN
TWELVE O'CLOCK`.replace(/\S+/g,w=>(k/=2)&1?w:w.replace(/./g,' '),k=[x=1<<20,176,162,132,168,184,129,120,104,68,98,112,x,m<33&&(h+=11),h%=12][m/5+.4|0]*8|6|2048<<(h?h<3?h-1:h:2))

Test cases

let f =

h=>m=>`IT IS HALF TEN
QUARTER TWENTY
FIVE MINUTES TO
PAST TWO THREE
ONE FOUR FIVE
SIX SEVEN EIGHT
NINE TEN ELEVEN
TWELVE O'CLOCK`.replace(/\S+/g,w=>(k/=2)&1?w:w.replace(/./g,' '),k=[x=1<<20,176,162,132,168,184,129,120,104,68,98,112,x,m<33&&(h+=11),h%=12][m/5+.4|0]*8|6|2048<<(h?h<3?h-1:h:2))
;

[[13,15],[13,58],[14,30],[15,35],[10,0],[12,0]].map(([h, m]) => {
  console.log('h = ' + h, 'm = ' + m);
  console.log(f(h)(m));
});

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.