The hourglass challenge

43

8

The Hourglass

This hourglass has 60 "sands of time", and it completely fills each chamber. The clock is 19 characters wide and 13 characters in height. Each chamber has 5 rows of sand and there is a row in the centre that can hold 1 sand. The top row can hold 17 sands, the next one 15 and so on (see below). Sands fall in to the bottom chamber at the rate of one sand per second.

       START                     3 SECONDS LATER               38 SECONDS LATER

███████████████████            ███████████████████            ███████████████████
█.................█   17       █..............   █   14       █                 █    0
██...............██   15       ██...............██   15       ██               ██    0
███.............███   13       ███.............███   13       ███.......      ███    7  
█████.........█████   09       █████.........█████   09       █████.........█████   09 
███████.....███████   05       ███████.....███████   05       ███████.....███████   05 
█████████.█████████   01       █████████.█████████   01       █████████.█████████   01 
███████     ███████            ███████  .  ███████            ███████  .  ███████  
█████         █████            █████    .    █████            █████    .    █████   
███             ███            ███      .      ███            ███....         ███
██               ██            ██               ██            ██...............██  
█                 █            █                 █            █.................█  
███████████████████            ███████████████████            ███████████████████   

The Challenge

Display the hourglass (no numbers or headings are required) after a certain period of time (0 ≤ t ≤ 60).

Coding Rules

  1. The hourglass should look exactly as shown here. You may replace the character and/or the . character with whatever you like to fit your language (Unicode, ASCII compatibility issues).
  2. The input should be a number such as 45 or 7. Display the clock after these many seconds.
  3. The output can either be displayed or saved to a file. No extra text or labels as shown above are required - just the hourglass is all we need.
  4. If the user enters t > 60, you don't have to handle the error.

Points

  1. Shortest code wins.

Renae Lider

Posted 2015-06-08T06:35:13.560

Reputation: 1 474

I don't know if it's just me, but the hourglasses looks completely different in the editor than in the post itself. That's probably because the █ character doesn't get displayed with the proper width. – Dennis – 2015-06-08T06:53:39.277

1Does a grain of sand touch the current layer, then align to the left one second later? – xnor – 2015-06-08T07:25:11.450

@xnor You are free to choose. – Renae Lider – 2015-06-08T08:34:17.647

2What should be the output for t==59? – edc65 – 2015-06-08T09:07:26.903

3Congrats on making the Stack Exchange Hot Network Questions list! – Alex A. – 2015-06-09T15:06:26.040

@AlexA. Oh that is great, I am humbled. hehe.. but I don't know what " Hot Network Questions" are. Where can I find that? – Renae Lider – 2015-06-09T21:42:30.910

@RenaeLider: They appear along the right side of the page in the form of a site icon and a question title, visible from all Stack Exchange sites. You can also see the list on the Stack Exchange questions homepage.

– Alex A. – 2015-06-09T21:56:21.453

Interesting. Just 7 answers. The slightly similar Code Golf: Hourglass on [SO] was more popular.

– manatwork – 2015-06-17T13:40:09.820

1

This is not really an answer, as it doesn't fit the specs, but take a look here for a submission in Desmos. (Press the play button and set the speed to 0.15x for ~1 unit/second)

– Conor O'Brien – 2015-10-02T13:29:41.930

Answers

27

JavaScript (ES6), 203 208 233 270 256 characters

Edit Revised using a loop instead of a sequence of calls.

Edit Added top and bottom row that were missing.

A function returning the output. Run the snippet in Firefox to test.

f=w=>[h='█'[R='repeat'](19),...[17,15,13,9,5,1,5,9,13,15,17].map((d,i)=>(t=i>5?-v:v,v-=i<5?d:1-d,e=' '[R](d/2),b='█'[R](10-d/2),b+('.'[R](t<d&&d-t)+e+' .'[i>4&w>i-6&t>=d|0]+e).slice(0,d)+b),v=w),h].join`
`

// Less golfed
F=
w=>[h='█'.repeat(19),
    ... [17, 15, 13, 9, 5, 1, 5, 9, 13, 15, 17].map(
     (d,i) => (
       t = i>5 ? -v : v,
       v -= i<5 ? d : 1-d, 
       e = ' '.repeat(d / 2),
       b = '█'.repeat(10 - d / 2),
       b + 
        ('.'.repeat(t < d && d - t) 
         + e + ' .'[i > 4 & w > i-6 & t >= d | 0]
         + e).slice(0,d)
       + b
     ), v = w
    ),
    h].join('\n')

// TEST            

O.innerHTML=f(+I.value)

function tick(d) {
  var i=+I.value+d
  I.value=i
  O.innerHTML=f(i)
}

var int=0;

function auto()
{
  function go()
  {
    var t = I.value;
    O.innerHTML=f(++t)
    if (t>70)t=0;
    I.value = t;
  }
  if (A.checked && !int)
  { 
    int = setInterval(go, 200);
  }
  else if (!A.checked && int)
  {
    clearInterval(int);
    int = 0;
  }
}
input[type=text] { width: 3em }
<button onclick='tick(-1)'>-</button>
<input type=text id=I value=0 onchange='tick(0)' >
<button onclick='tick(1)'>+</button>
<input type=checkbox id=A onclick='auto()'>Fly time
<pre id=O><pre>

edc65

Posted 2015-06-08T06:35:13.560

Reputation: 31 086

5For my curiosity, why was this downvoted twice? I can find no problem with it. – manatwork – 2015-06-08T10:28:47.553

This answer is awesome! Open it in Firefox, people! – Cristian Lupascu – 2015-06-08T12:10:12.203

1Good answer, but the top and base of the hourglass seems to be missing. – Renae Lider – 2015-06-08T12:42:46.840

@manatwork not sure (of course) but there was a flaw indeed – edc65 – 2015-06-08T12:57:28.327

Please make a setInterval(), javascript would be great for that: setTimeout( tick, 1000); – Martijn – 2015-06-08T13:32:54.480

@Martijn done, just a little faster ... – edc65 – 2015-06-08T14:06:28.240

Now it should be nice if downvoters could change their minds ... – edc65 – 2015-06-09T19:25:02.100

Did template literals exist back in 2015? Because if so, you can swap the join('\n') for joinBacktickNewlineBacktick. – user2428118 – 2017-01-16T13:00:40.830

@user2428118 yes they did, FireFox 34, december 2014. Good hint, thanks – edc65 – 2017-01-16T14:05:02.753

18

Python 2, 200

t=input()+1
s=' '*t+'.'*60+' '*70
n=0
d=sum((1<t<56,2<t<48,3<t<36,4<t<22))
for c in'ctrplhdhlprtc':i=ord(c)-99;print[s[n+i:n:-1],[s[180-n-i+d:][:i],'.'][5+d*3>i>0]][n>59].center(i).center(19,'#');n+=i

xnor has made a 197 byte version in the chat.

I would post an explanation, but I've lost track of how it actually works...

Also, here's an animated version with curses:

hourglass

from curses import*
w=initscr()

for t in range(1,61):
    s=' '*t+'.'*60+' '*70
    n=0
    d=sum((1<t<56,2<t<48,3<t<36,4<t<22))
    for i in 0,17,15,13,9,5,1,5,9,13,15,17,0:w.addstr([s[n+i:n:-1],[s[180-n-i+d:][:i],'.'][5+d*3>i>0]][n>59].center(i).center(19,'#')+"\n");n+=i
    w.refresh()
    w.clear()
    napms(999)

endwin()

grc

Posted 2015-06-08T06:35:13.560

Reputation: 18 565

1kudos, You made me discover that strings have a center() method ! – dieter – 2015-06-08T15:24:49.557

13

Python 2.7, 362 356 347

e,c,x,a,b,n=' .#ab\n';R,r,s,l,T,m=range,str.replace,'',19,[1,2,3,5,7,9],-1
for t in[T,T[:m][::m]]:s+=''.join([n+x*y+c*(l-y*2)+x*y for y in t]);c=b
s=list(s)
for i in R(130,220,20):s[i]=a
for _ in R(input()):s[s.index('.')]=e;i=s.index(a)if a in s else 219-s[::m].index(b);s[i]='.'
for l in(x*l+r(r(''.join(s),a,e),b,e)+n+x*l).split(n):print l[::m]

hourglass

Output at 38 seconds :

###################
#                 #
##               ##
###.......      ###
#####.........#####
#######.....#######
#########.#########
#######  .  #######
#####    .    #####
###...   .      ###
##...............##
#.................#
###################

dieter

Posted 2015-06-08T06:35:13.560

Reputation: 2 010

2What kind of Python code is that? I would love to learn to code like them. Please guide in right direction. – Rajat Saxena – 2015-06-08T12:20:41.820

1I am not sure if this is a valid answer. If you look at the example he made for 38seconds, you can see that the lowest dot which is still falling is not in the line where the dots align. (in the 4th line from bottom, at the center) – RononDex – 2015-06-08T13:39:43.217

3@RononDex : As explained in the question's comments, the filling order is not so strict - my implementation sticks the grains of sand to the left in a way that is slightly different from the question, but still valid – dieter – 2015-06-08T15:21:36.413

3

C 544

Here's what I have so far for a C solution.

#include <stdio.h>
int main(int z,char **a){int r,i,y=i=0,v,d,t,m,s=atoi(a[1]),n[13]={0,43,28,15,6,1,0,1,5,13,25,39,0};char H[13][20];while(y<13){int x,b=x=i=0;v=y-6;t=3+abs(v);m=2*abs(v);d=t<m?t:m;d=9-d;if(d==0)d=10;while (b<d){H[y][b]='#';H[y][18-b]='#';b++;}while(x<19-2*b){if(x<=s-n[y])H[y][x+b]=v>0?' ':'.';else H[y][x+b]=v>0?'.':' ';x++;}if(s>58)r=0;else if(s==58)r=1;else if(s==57)r=2;else if(s==56)r=3;else if(s>38)r=4;else if(s>24)r=3;else if(s>12)r=2;else if(s>4)r=1;while(i<r){H[7+i][9]='.';i++;}H[y][19]='\n';y++;}fputs(H,stdout);}

Compiled with the following command:

gcc -w -o hourglass hourglass.c  // I realize I should have cast H as a char *, but since it works this way, I just decided to suppress the error from the compiler instead to save space.

Admittedly, this language has a lot of bulk- that include statement was a bit of a handicap coming right out of the blocks, but I really was just looking for an excuse to practice using C.

I hope you like my solution, and let me know if you see ways to improve.

martin

Posted 2015-06-08T06:35:13.560

Reputation: 191

2There's a lot a ways to improve, making it worse C but a lot shorter. – edc65 – 2015-06-15T10:51:35.953

1Hints: for instead of where,?: instead of if else, global int variables don't need int, include not needed. (I like your math) – edc65 – 2015-06-15T13:06:54.257

1317: r,i,y,x,b,d,n[]={0,43,28,15,6,1,0,1,5,13,25,39,0};char H[14][20];main(s,a)char**a;{for(s=atoi(a[1]);y<13;){b=x=i=0;d=abs(y-6);d+=3<d?3:d;d=9-d?9-d:10;r=s>55?59-s:s>38?4:s>24?3:s>12?2:s>4?1:r;for(;b<19;)H[y][b++]=35;for(;x<19-2*d;x++)H[y][x+d]=(x>s-n[y]?" .":". ")[y>6];for(;i<r;)H[7+i++][9]=46;H[y++][b]=10;}puts(H);} (beware, strange invisible chars are added to long comments like this) – edc65 – 2015-06-15T13:08:55.487

3

Matlab, 252 bytes

The idea is constructing a matrix that looks like this:

0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
0  17  16  15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
0   0  32  31  30  29  28  27  26  25  24  23  22  21  20  19  18   0   0
0   0   0  45  44  43  42  41  40  39  38  37  36  35  34  33   0   0   0
0   0   0   0   0  54  53  52  51  50  49  48  47  46   0   0   0   0   0
0   0   0   0   0   0   0  59  58  57  56  55   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0  60   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0 116 117  61 118 119   0   0   0   0   0   0   0
0   0   0   0   0 108 109 110 111  62 112 113 114 115   0   0   0   0   0
0   0   0  96  97  98  99 100 101  63 102 103 104 105 106 107   0   0   0
0   0  82  83  84  85  86  87  88  64  89  90  91  92  93  94  95   0   0
0  66  67  68  69  70  71  72  73  65  74  75  76  77  78  79  80  81   0
0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0

From there it is easy to fill the entries with the strings dependent on n (filling all entries that are greater than n and smaller than n+60 with dots)

function c=f(n);
b=zeros(13,19);
z=[0,17,32,45,54,59];
y=-2:3;
for k=2:6;
    d=k+sum(k>4:5);
    b(k,d:20-d)=z(k):-1:z(k-1)+1;
    b(14-k,d:19-d)=68+(z(k-1):z(k)-2)-k;
end;
b(8:12,11:19)=b(8:12,10:18);
b(7:12,10)=60:65;c=[ones(13,19)*32,''];
c(~b)='¶';c(n<b)=46;c(b>n+60)=32

For n=38 we get this output:

¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶
¶                 ¶
¶¶               ¶¶
¶¶¶.......      ¶¶¶
¶¶¶¶¶.........¶¶¶¶¶
¶¶¶¶¶¶¶.....¶¶¶¶¶¶¶
¶¶¶¶¶¶¶¶¶.¶¶¶¶¶¶¶¶¶
¶¶¶¶¶¶¶  .  ¶¶¶¶¶¶¶
¶¶¶¶¶    .    ¶¶¶¶¶
¶¶¶...   .      ¶¶¶
¶¶...............¶¶
¶.................¶
¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶

flawr

Posted 2015-06-08T06:35:13.560

Reputation: 40 560

2

Haskell 512 Bytes

h=[17,15,13,9,5,1];b=drop 1$reverse h;n#s=[1..n]>>[s];s='.';n =' ';c q=(q#n)++(60-q)#s;f q|q<=5=q#s|3>2=g#s where{g=foldl i 5 (scanl (+) 0 h);i x y=if q>x+y then x-1 else x};e q=j#s++(59-length(k q)-(j))#n where{j=q-length(f q)};l q=c q++k q++(reverse$e q);p _ []=[];p x y=reverse(z++take q x++z):p (drop (head y) x) (tail y)where{q=head y;z=replicate(div (19-q) 2) '|'};k q= (concat.map(\x -> z x ++ "." ++ z x).take (length.f$q)$b)where{z x=(div x 2)#n};m n=mapM_ putStrLn $ t ++ p (l n) (h++b) ++ t;t=[19#'|'] 

Input m 55

Output

|||||||||||||||||||
|                 |
||               ||
|||             |||
|||||         |||||
|||||||.... |||||||
|||||||||.|||||||||
|||||||  .  |||||||
|||||.........|||||
|||.............|||
||...............||
|.................|
|||||||||||||||||||

Input m 48 Output

    |||||||||||||||||||
    |                 |
    ||               ||
    |||             |||
    |||||......   |||||
    |||||||.....|||||||
    |||||||||.|||||||||
    |||||||  .  |||||||
    |||||..       |||||
    |||.............|||
    ||...............||
    |.................|
    |||||||||||||||||||

brander

Posted 2015-06-08T06:35:13.560

Reputation: 111

1Welcome to PPCG! I see you already golfed this quite a bit, but there is still a lot of unnecessary white space left, eg. surrounding ++. Also you may save some bytes by predefining length and reverse. – Laikoni – 2017-01-23T23:42:42.420

2

Java, 712

Input is taken from the command line. Handles both negative values for the time as well as larger than required.

enum H{;public static void main(String[]r){int x=0,y=0,z=0,l,t=Integer.parseInt(r[0]);String b="",d="█",e=" ",f=".",n="\n",j,k,a="███████████████████"+n;int[]w={17,15,13,9,5},v;int[][]h=new int[10][];for(;x<5;){l=w[x];v=(h[x++]=new int[l--]);l/=2;v[l]=++z;for(y=0;y++<l;){v[l-y]=++z;v[l+y]=++z;}}for(z=0;x>0;){l=w[--x];v=(h[9-x]=new int[l--]);v[l/2]=++z;}for(;x<5;){l=(w[x]-1)/2;v=h[9-x++];for(y=0;y++<l;){v[l-y]=++z;v[l+y]=++z;}}p(a);for(x=0;x<5;x++){l=w[x];j=b;for(y=0;y++*2<19-l;)j+=d;k=b;for(y=0;y<l;)k+=t<h[x][y++]?f:e;p(j+k+j+n);}j="█████████";p(j+f+j+n);for(;x>0;){l=w[--x];j=b;for(y=0;y++*2<19-l;)j+=d;k=b;for(y=0;y<l;)k+=t<h[9-x][y++]?e:f;p(j+k+j+n);}p(a);}static void p(String s){System.out.print(s);}}

output:

time: 0
███████████████████
█.................█
██...............██
███.............███
█████.........█████
███████.....███████
█████████.█████████
███████     ███████
█████         █████
███             ███
██               ██
█                 █
███████████████████

time: 1
███████████████████
█........ ........█
██...............██
███.............███
█████.........█████
███████.....███████
█████████.█████████
███████  .  ███████
█████         █████
███             ███
██               ██
█                 █
███████████████████

time: 9
███████████████████
█....         ....█
██...............██
███.............███
█████.........█████
███████.....███████
█████████.█████████
███████  .  ███████
█████    .    █████
███      .      ███
██       .       ██
█      .....      █
███████████████████

time: 41
███████████████████
█                 █
██               ██
███..         ..███
█████.........█████
███████.....███████
█████████.█████████
███████  .  ███████
█████    .    █████
███   .......   ███
██...............██
█.................█
███████████████████

It fills the sand from the center expanding outwards. I can probably golf it more if I get lazy with how it fills the bottom half and empties the top half. But for now I quite like it.

Jack Ammo

Posted 2015-06-08T06:35:13.560

Reputation: 430

1

C#, 382 410

it might still be possible to reduce it by a few bytes...

class Program{static void Main(){int u=60-22,d=u,i,j,k,l,m;var c=new char[260];var r=new int[]{0,17,15,13,9,5,1,5,9,13,15,17,0,54,45,32,17,0};for(i=0;i<13;){m=0;l=(19-r[i])/2-1;for(j=19;j>=0;){k=i*20+j--;var b=j>=l&&j<r[i]+l;if(i>6&b)c[k-r[i]+m++ +m]=r[i+6]<d&&d-->0||j==8&r[i+6]>d&&d-->0?'.':' ';else c[k]=i<7&b?u-->1?' ':'.':'█';}c[++i*20-1]='\n';}System.Console.WriteLine(c);}}

Fiddler - 38sec

Fredou

Posted 2015-06-08T06:35:13.560

Reputation: 119

Cool! How does it take input? – isaacg – 2015-06-10T05:34:36.930

@isaacg, oops, i will fix that later today, i knew it was too short for a c# solution :-) for now change u=60-22 to something else – Fredou – 2015-06-10T12:29:36.467

Why you use int u=60-22 and not just int u=38?? – dev-masih – 2015-10-03T08:04:52.770

1

Ruby: 196 190 186 185 184 characters

u=[0,17,15,13,9,5].map{|i|(?.*i).center 19,?#}*$/
(?1..$*[0]).map{u[?.]=' '}
l=u.reverse
5.times{|i|l[p=i*20+9]==?.&&l[' ']&&(l[p]=?|)&&l[' ']=?.}
puts u,?#*9+?.+?#*9,l.tr('. | ',' .')

CW because not conforms exactly to the posted samples as this consumes sand starting from the left. Mostly just a demonstration of String.[]= method.

Sample run:

bash-4.3$ ruby hg.rb 38
###################
#                 #
##               ##
###      .......###
#####.........#####
#######.....#######
#########.#########
#######  .  #######
#####    .    #####
###      .   ...###
##...............##
#.................#
###################

Ruby: 215 characters

This is generates the exact required output:

u=[0,17,15,13,9,5].map{|i|(?.*i).center 19,?#}*$/
(?1..$*[0]).map{u[?.]=' '}
l=u.reverse
5.times{|i|l[p=i*20+9]==?.&&l[' ']&&(l[p]=?|)&&l[' ']=?.}
puts ([u,?#*9+?.+?#*9,l.tr('. | ',' .')]*$/).split($/).map &:reverse

Sample run:

bash-4.3$ ruby hg.rb 38
###################
#                 #
##               ##
###.......      ###
#####.........#####
#######.....#######
#########.#########
#######  .  #######
#####    .    #####
###...   .      ###
##...............##
#.................#
###################

manatwork

Posted 2015-06-08T06:35:13.560

Reputation: 17 865