51
votes

The challenge

The shortest code by character count to output an hourglass according to user input.

Input is composed of two numbers: First number is a greater than 1 integer that represents the height of the bulbs, second number is a percentage (0 - 100) of the hourglass' capacity.

The hourglass' height is made by adding more lines to the hourglass' bulbs, so size 2 (the minimal accepted size) would be:

_____
\ /
 \ /
 / \
/___\

Size 3 will add more lines making the bulbs be able to fit more 'sand'.

Sand will be drawn using the character x. The top bulb will contain N percent 'sand' while the bottom bulb will contain (100 - N) percent sand, where N is the second variable.

'Capacity' is measured by the amount of spaces () the hourglass contains. Where percentage is not exact, it should be rounded up.

Sand is drawn from outside in, giving the right side precedence in case percentage result is even.

Test cases

Input:
 3 71%
Output:
 _______
 \x xx/
 \xxx/
 \x/
 / \
 / \
 /__xx_\

Input:
 5 52%
Output:
 ___________
 \ /
 \xx xx/
 \xxxxx/
 \xxx/
 \x/
 / \
 / \
 / \
 / xxx \
 /xxxxxxxxx\

Input:
 6 75%
Output:
 _____________
 \x x/
 \xxxxxxxxx/
 \xxxxxxx/
 \xxxxx/
 \xxx/
 \x/
 / \
 / \
 / \
 / \
 / \
 /_xxxxxxxxx_\

Code count includes input/output (i.e full program).

21
  • 7
    I don't even use a calendar anymore. When these show up, I know it's Thursday! This one is cool. Commented Nov 5, 2009 at 21:48
  • Should the input be from a file, command line or stdin? Commented Nov 5, 2009 at 21:56
  • 1
    @kersny - yes that's what I'm looking at... basically does the middle change size as you add rows or is it supposed to stay the same? In the given example the width is different for even vs odd sizes. Commented Nov 5, 2009 at 22:28
  • 2
    @LiraNuna - that doesn't really help me - should an hourglass of size 4 have a capacity of 16 or 12? Commented Nov 5, 2009 at 22:44
  • 2
    No problem. I'm glad I understood something today. Now to waste company time implementing this in Enterprise Java! Commented Nov 5, 2009 at 22:59

13 Answers 13

36
votes

C/C++, a dismal 945 characters...

Takes input as parameters: a.out 5 52%

#include<stdio.h>
#include<memory.h>
#include<stdlib.h>
#define p printf
int h,c,*l,i,w,j,*q,k;const char*
 z;int main(int argc,char**argv)
 {h=atoi(argv[1]);c=(h*h*atoi(
 argv[2])+99)/100;l=new int[
 h*3];for(q=l,i=0,w=1;i<h;
 i++,c=(c-w)&~((c-w)>>31
 ),w+=2)if(c>=w){*q++=
 0;*q++ =0;* q++=w;}
 else {*q++=(c+1)/
 2;*q++=w-c;*q++
 =c/2;}p("_");
 for(i=0;i<h
 ;i ++)p (
 "__");p
 ("\n"
 );q
 =
 l+h
 *3-1;
 for (i=
 --h;i>=0;
 i--){p("%*"
 "s\\",h-i,"")
 ; z= "x0円 0円x";
 for(k=0;k<3;k++,q
 --,z+=2)for(j=0;j<*
 q;j++)p(z);q-=0;p("/"
 "\n");}q=l;for(i=0;i<=h
 ;i++){z =i==h? "_0円x0円_":
 " 0円x0円 ";p("%*s/",h-i,"");
 for(k=0;k<3;k++,q++,z+=2)for(
 j=0;j<*q;j++)p(z);p("\\\n") ;}}

...and the decrypted version of this for us mere humans:

#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#define p printf
int h, c, *l, i, w, j, *q, k;
const char *z;
int main(int argc, char** argv)
{
 h = atoi(argv [1]);
 c = (h*h*atoi(argv[2])+99)/100;
 l = new int[h*3];
 for (q = l,i = 0,w = 1; i<h; i++,c = (c-w)&~((c-w)>>31),w += 2) {
 if (c>=w) {
 *q++ = 0;
 *q++ = 0;
 *q++ = w;
 } else {
 *q++ = (c+1)/2;
 *q++ = w-c;
 *q++ = c/2;
 }
 }
 p("_");
 for (i = 0; i<h; i++) {
 p("__");
 }
 p("\n");
 q = l+h*3-1;
 for (i = --h; i>=0; i--) {
 p("%*s\\",h-i,"");
 z = "x0円 0円x";
 for (k = 0; k<3; k++,q--,z += 2) {
 for (j = 0; j<*q; j++) {
 p(z);
 }
 }
 p("/\n");
 }
 q = l;
 for (i = 0; i<=h; i++) {
 z = i==h ? "_0円x0円_" : " 0円x0円 ";
 p("%*s/",h-i,"");
 for (k = 0; k<3; k++,q++,z += 2) {
 for (j = 0; j<*q; j++) {
 p(z);
 }
 }
 p("\\\n") ;
 }
}
Sign up to request clarification or add additional context in comments.

4 Comments

I think you confuse this with IOCCC :D
okay - I fixed it to fit the new 1st test case and added the const (which wouldn't have been necessary if you used a standards non-compliant compiler like Msdev)
one thing to note is that this isn't valid C, just C++. so the "C/C++" in the heading is misleading.
Why does this have so many upvotes when the author himself says it is a "dismal 945 characters"? Not being mean, just making sure I understand the logic or lack thereof behind the upvotes.
23
votes

Perl, 191 char

(削除) 205 (削除ここまで) (削除) 199 (削除ここまで) 191 chars.

$S=-int((1-.01*pop)*($N=pop)*$N)+$N*$N;$S-=$s=$S>++$r?$r:$S,
$\=$/.$"x$N."\\".x x($v=$s/2).$"x($t=$r++-$s).x x($w=$v+.5)."/$\
".$"x$N."/".($^=$N?$":_)x$w.x x$t.$^x$v."\\"while$N--;print$^x++$r

Explicit newline required between the 2nd and 3rd lines.

And with help of the new Acme::AsciiArtinator module:

$S=-int((1-.01*pop)*($N=pop
) *
 $ N
 ) +
 $ N
 *$N;( ${B},$
 F,${x})=qw(\\ / x
 );while($N){;/l
 ater/g;$S-=$s
 =$S>++$r?$r
 :$S;'than
 you';@o
 =(" "
 x--
 $ N
 . $
 B .
 x x
 ( $
 v =
 $ s
 / 2
 ) .$"x($t= $
 r++-$s).x x($w=$v+.5)
 .$F,@o,$"x$N.$F.($^=$N?
 $":_)x$w.x x$t.$^x$v.$B);
,ドル=$/}print$^x++$r,@o;think

10 Comments

Seems to be consistently printing out 2 two many _ characters on top. Also not printing newlines at the end, which is a nice thing to do.
@Chris: mobrule's answers always lack a newline :P
I think newlines at the end should be optional
Where's the picture of the hourglass?
@LiraNuna That'd be IOPPP (International Obfuscated Perl Programming Pandemonium)
|
21
votes

Golfscript - 136 Chars (Fits in a Tweet)

Be sure not to have a newline after the % for the input
eg
$ echo -n 3 71%|./golfscript.rb hourglass.gs

You can animate the hourglass like this:

$ for((c=100;c>=0;c--));do echo -n "15 $c%"|./golfscript.rb hourglass.gs;echo;sleep 0.1;done;

Golfscript - 136 Chars
Make sure you don't save it with an extra newline on the end or it will print an extra number

);' ': /(~:
;0=~100.@-
.**\/:t;'_':&&
*.n
,{:y *.'\\'+{[&'x':x]0t(:t>=}:S~
(y-,{;S\+S+.}%;'/'++\+}%.{&/ *}%\-1%{-1%x/ *&/x*}%) /&[*]++n* 

Golfscript - 144 Chars

);' ':|/(~:^.*:X
 ;0=~100.@-X*\/
 X'x':x*'_':&
 @*+:s;&&&+
 ^*n^,{:y
 |*.[92
 ]+{s
 [)
 \#
 :s;]
 }:S~^(
 y-,{;S\+
 S+.}%;'/'+
 +\+}%.{&/|*}
 %\-1%{-1%x/|*&
/x*}%)|/&[*]++n*

How it works
First do the top line of underscores which is 2n+1 Create the top half of the hourglass, but use '_' chars instead of spaces, so for the 3 71% we would have.

\x__xx/
 \xxx/
 \x/

Complete the top half by replacing the "_" with " " but save a copy to generate the bottom half

The bottom half is created by reversing the whole thing

 /x\
 /xxx\
/xx__x\

Replacing all the 'x' with ' ' and then then '_' with 'x'

 / \
 / \
/ xx \

Finally replace the ' ' in the bottom row with '_'

 / \
 / \
/__xx_\

Roundabout but for me, the code turned out shorter than trying to generate both halves at once

5 Comments

I always laugh when I see golfscript in one of these.
I like the way it is self documenting
I'm going to have to start thinking in terms of moral victories. I'll set my first bar at 133% of the golfscript solution.
@mobrule, Are you going to get your lasers down to 122 chars then :) stackoverflow.com/questions/1480023/code-golf-lasers/…
Crikey. There's even a better python Laser solution now.
14
votes

Python, 213 char

N,p=map(int,raw_input()[:-1].split())
S=N*N-N*N*(100-p)/100
_,e,x,b,f,n=C='_ x\/\n'
o=""
r=1
while N:N-=1;z=C[N>0];s=min(S,r);S-=s;t=r-s;v=s/2;w=s-v;r+=2;o=n+e*N+b+x*v+e*t+x*w+f+o+n+e*N+f+z*w+x*t+z*v+b
print _*r+o

4 Comments

I ported this to golfscript but it came out ~20 longer than my existing solution :(
There's at least 3 good ideas here: computing the total number of 'x' characters at the top, exploiting the x/space mirror, and generating inside-out. Nice job...
(I imagine the perl program does all that too but I'm not going to look: I don't want my eyes to bleed :-)
@DigitalRoss, My golfscript takes advantage of the symmetry too, but in a surprising way!
8
votes

Rebmu: 188 chars

rJ N 0% rN Wad1mpJ2 S{ \x/ }D0 Hc&[u[Z=~wA Qs^RTkW[isEL0c[skQdvK2][eEV?kQ[tlQ]]pcSeg--B0[eZ1 5]3]prRJ[si^DspSCsQfhS]eZ1[s+DcA+wMPc2no]]]Va|[mpAj**2]prSI^w{_}Ls+W2 h1tiVsb1n -1 chRVs{_}hLceVn1

It's competitive with the shorter solutions here, though it's actually solving the problem in a "naive" way. More or less it's doing the "sand physics" instead of exploiting symmetries or rotating matrices or anything.

H defines a function for printing a half of an hourglass, to which you pass in a number which is how many spaces to print before you start printing "x" characters. If you're on the top half, the sand string is constructed by alternating appends to the head and the tail. If you're on the bottom it picks the insertion source by skipping into the middle of the string. Commented source available at:

http://github.com/hostilefork/rebmu/blob/master/examples/hourglass.rebmu

But the real trick up Rebmu's sleeve is it's a thin dialect that doesn't break any of the parsing rules of its host language (Rebol). You can turn this into a Doomsday visualization by injecting ordinary code right in the middle, as long you code in lowercase:

>> rebmu [rJ birthday: to-date (ask "When were you born? ") n: (21-dec-2012 - now/date) / (21-dec-2012 - birthday) Wad1mpJ2 S{ \x/ }D0 Hc~[u[Ze?Wa Qs^RTkW[isEL0c[skQdvK2][eEV?kQ[tlQ]]pcSeg--B0[eZ1 5]3]prRJ[si^DspSCsQfhS]eZ1[s+DcA+wMPc2no]]]Va|[mpAj**2]prSI^w{_}Ls+W2h1tiVsb1n -1 chRVs{_}hLceVn1]

Input Integer: 10
When were you born? 23-May-1974
_____________________
\ /
 \ /
 \ /
 \ /
 \ /
 \ /
 \ /
 \x xx/
 \xxx/
 \x/
 / \
 / \
 / xx \
 /xxxxxxx\
 /xxxxxxxxx\
 /xxxxxxxxxxx\
 /xxxxxxxxxxxxx\
 /xxxxxxxxxxxxxxx\
 /xxxxxxxxxxxxxxxxx\
/xxxxxxxxxxxxxxxxxxx\

O noes! :)

(Note: A major reason I'm able to write and debug Rebmu programs is because I can break into ordinary coding at any point to use the existing debugging tools/etc.)

4 Comments

This is a serious waste of Rebol cycles!
I strongly disagree. Rebol dialecting is powerful, and more examples are always helpful because the existing documentation has not really taught people enough about dialecting "wins". Plus the code golfing mindset is one that could be particularly receptive to the do more with less anti-bloat mindset. There is now an entire StackExchange site dedicated to Code Golfing, Rebmu can give cool Rebol tricks exposure: codegolf.stackexchange.com
You actually achieved very good native compression using Rebmu. Trying to compress your Rebmu code added 2 bytes to the total. :-)
@Respectech Dr. Rebmu told me he considered adding a decompression primitive, e.g. caret-string ^{a89jfoiMM20...}, and have that expand into a BINARY! of the decompressed Base64 encoded data. But is holding off because he's afraid people might abuse it, and it would risk Rebmu programs becoming unreadable.
6
votes

Haskell. 285 characters. (Side-effect-free!)

x n c=h s++'\n':reverse(h(flip s)) where h s=r w '-'++s '+' b(w-2)0 p;w=(t n);p=d(n*n*c)100
s x n i o p|i>0='\n':l++s x n(i-2)(o+1)(max(p-i)0)|True=[] where l=r o b++'\\':f d++r(i#p)n++f m++'/':r o b;f g=r(g(i-(i#p))2)x
b=' '
r=replicate
t n=1+2*n
d=div
(#)=min
m=(uncurry(+).).divMod

Run with e.g. x 5 50

community wiki

Comments

4
votes

A c++ answer, is 592 chars so far, still having reasonable formatting.

#include<iostream>
#include<string>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef string S;
typedef int I;
typedef char C;
I main(I,C**v){
 I z=atoi(v[1]),c=z*z,f=ceil(c*atoi(v[2])/100.);
 cout<<S(z*2+1,'_')<<'\n';
 for(I i=z,n=c;i;--i){
 I y=i*2-1;
 S s(y,' ');
 C*l=&s[0];
 C*r=&s[y];
 for(I j=0;j<y;++j)
 if(n--<=f)*((j&1)?l++:--r)='x';
 cout<<S(z-i,' ')<<'\\'<<s<<"/\n";
 }
 for(I i=1,n=c-f;i<=z;++i){
 I y=i*2-1;
 S s(y,'x');
 C*l=&s[0];
 C*r=&s[y];
 for(I j=0;j<y;++j)
 if(n++<c)*(!(j&1)?l++:--r)=(i==z)?'_':' ';
 cout<<S(z-i,' ')<<'/'<<s<<"\\\n";
 }
}

If i decide to just forget formatting it reasonably, i can get it as low as 531:

#include<iostream>
#include<string>
#include<cstdlib>
#include<cmath>
using namespace std;typedef string S;typedef int I;typedef char C;I main(I,C**v){I z=atoi(v[1]),c=z*z,f=ceil(c*atoi(v[2])/100.);cout<<S(z*2+1,'_')<<'\n';for(I i=z,n=c;i;--i){I y=i*2-1;S s(y,' ');C*l=&s[0];C*r=&s[y];for(I j=0;j<y;++j)if(n--<=f)*((j&1)?l++:--r)='x';cout<<S(z-i,' ')<<'\\'<<s<<"/\n";}for(I i=1,n=c-f;i<=z;++i){I y=i*2-1;S s(y,'x');C*l=&s[0];C*r=&s[y];for(I j=0;j<y;++j)if(n++<c)*(!(j&1)?l++:--r)=(i==z)?'_':' ';cout<<S(z-i,' ')<<'/'<<s<<"\\\n";}}

2 Comments

typedef char* C; will save a few characters, since you only use a character pointer.
I looked into that, and it actually wont. Because Currently I can write: C*l with no space, if C is a char* I will need to have a space! and write: C l so it will be a net loss.
3
votes

Bash: (削除) 639 (削除ここまで) - 373 characters

I thought I would give bash a try (haven't seen much code-golfing in it). (my version: GNU bash, version 3.2.48(1)-release (i486-pc-linux-gnu))

Based on Mobrule's nice python answer.

Optimizations must still be available, so all suggestions are welcome!

Start from the command line, e.g. : ./hourglass.sh 7 34%

function f () { for i in `seq 1ドル`;do printf "2ドル";done; }
N=1ドル;S=$[1ドル*1ドル-1ドル*1ドル*$[100-${2/\%/}]/100]
b='\';o=$b;n="\n";r=1;while [ $N -gt 0 ];do
N=$[N-1];z=" ";s=$r;[ $N -eq 0 ]&& z=_;[ $S -lt $r ]&& s=$S
S=$[S-s];t=$[r-s];v=$[s/2];w=$[s-v];r=$[r+2]
o=$n`f $N " "`$b`f $v x;f $t " ";f $w x`/$o$b$n`f $N " "`/`f $w "$z";f $t x;f $v "$z"`$b
done;f $r _;echo -e "${o/\/\\\\//}"

2 Comments

Input can be from stdin or command line args, so your 373 solution is valid.
Thanks, I've updated the answer accordingly. I'm not really a good bash scripter so I still hope some people will chime in with some optimalizations. Very nice code-golf questions every time LiraNuna ;-)
2
votes

Java; 661 characters

public class M{public static void main(String[] a){int h=Integer.parseInt(a[0]);int s=(int)Math.ceil(h*h*Integer.parseInt(a[1])/100.);r(h,h-1,s,true);r(h,h-1,s,false);}static void r(int h,int c,int r,boolean t){if(c<0)return;int u=2*(h-c)-1;if(t&&c==h-1)p(2*h+1,0,'_','_',true,0,false);int z=r>=u?u:r;r-=z;if(t)r(h,c-1,r,true);p(u,z,t?'x':((c==0)?'_':' '),t?' ':'x',t,c,true);if(!t)r(h,c-1,r,false);}static void p(int s,int n,char o,char i,boolean t,int p,boolean d){int f=(s-n);int q=n/2+(!t&&(f%2==0)?1:0);int e=q+f;String z = "";int j;for(j=0;j<p+4;j++)z+=" ";if(d)z+=t?'\\':'/';for(j=0;j<s;j++)z+=(j>=q&&j<e)?i:o;if(d)z+=t?'/':'\\';System.out.println(z);}}

I need to find a better set of golf clubs.

community wiki

3 Comments

"I need to find a better set of golf clubs." - I'd recommend a set of Perls. At least for golfing... ;-)
you could use static import for system.out and and field for boolean. true*4+false*3=16+15=31 static boolean b;+!b*4+b*3=17+8+3=28
I agree that I could have shaved a few more characters with all those boolean values. As for the print line, there's only one, so there's no sense importing all of it for that single case.
2
votes

PHP - 361

<?$s=$argv[1];$x='str_pad';$w=$s*2-1;$o[]=$x('',$w+2,'_');
$r=$s*ceil($w/2);$w=$r-($r*substr($argv[2],0,-1)/100);$p=0;
$c=-1;while($s){$k=$s--*2-1;$f=$x($x('',min($k,$w),' '),$k,'x',2);
$g=$x($x('',min($k,$w),'x'),$k,' ',2);$w-=$k;$o[]=$x('',$p)."\\$f/";
$b[]=$x('',$p++)."/$g\\";}$b[0]=str_replace(' ','_',$b[0]);
krsort($b);echo implode("\n",array_merge($o,$b));?>
community wiki

Comments

1
vote

Python - 272 chars

X,p=map(int,raw_input()[:-1].split())
k=X*X;j=k*(100-p)/100
n,u,x,f,b,s='\n_x/\ '
S=list(x*k+s*j).pop;T=list(s*k+u*(2*X-j-1)+x*j).pop
A=B=""
for y in range(X):
 r=S();q=T()
 for i in range(X-y-1):r=S()+r+S();q+=T();q=T()+q
 A+=n+s*y+b+r+f;B=n+s*y+f+q+b+B
print u+u*2*X+A+B
community wiki

2 Comments

wouldn't B=n+s*y+f+q+b+B be better if you write B+=n+s*y+f+q+b? Saves one char
@LiraNuna, B is a string, and I need to add to the start not the end
1
vote

Exabyte18's java converted to C#, 655 bytes:

public class M {public static void Main(){int h = Convert.ToInt32(Console.ReadLine());
int s = Convert.ToInt32(h * h * Convert.ToInt32(Console.ReadLine()) / 100);r(h,h-1,s,true);
r(h,h-1,s,false);Console.ReadLine();}static void r(int h, int c, int r, bool t){
if(c<0) return;int u=2*(h-c)-1;if (t&&c==h-1)p(2*h+1,0,'_','_',true,0,false);
int z=r>=u?u:r; r-=z;if (t)M.r(h,c-1,r,true); p(u,z,t?'x':((c==0)?'_':' '), t?' ':'x',t,c,true);
if(!t)M.r(h,c-1,r,false);}static void p(int s, int n, char o, char i, bool t, int p, bool d)
{int f=(s-n);int q=n/2+(!t&&(f%2==0)?1:0);int e=q+f;string z="";int j;for(j=0;j<p+4;j++) z+=" ";if(d)z+=t?'\\':'/';
for (j=0;j<s;j++) z+=(j>=q&&j<e)?i:o; if(d)z+=t?'/':'\\';Console.WriteLine(z);}}
community wiki

Comments

0
votes

Ruby, (削除) 297 (削除ここまで) 254 (after compression)

Run both with ruby -a -p f.rb

n,p = $F.map{|i|i.to_i}
r="\n"
y=''
g,s,u,f,b=%w{x \ _ / \\}
$> << u*2*n+u+r # draw initial underbar line
a=u
c=100.0/n/n # amount of sand a single x represents
e = 100.0 # percentage floor to indicate sand at this level
n.times{ |i|
 d=2*n-1-2*i # number of spaces at this level
 e-= c*d # update percentage floor
 x = [((p - e)/c+0.5).to_i,d].min
 x = 0 if x<0
 w = x/2 # small half count
 z = x-w # big half count
 d = d-x # total padding count
 $> << s*i+b+g*w+s*d+g*z+f+r
 y=s*i+f+a*z+g*d+a*w+b+r+y
 a=s
}
$_=y

Ruby, 211

This is mobrule's tour de force, in Ruby. (And still no final newline. :-)

m,p=$F.map{|i|i.to_i}
q=m*m-m*m*(100-p)/100
_,e,x,b,f=%w{_ \ x \\ /}
n="\n"
o=''
r=1
while m>0
m-=1
z=m>0?e:_
s=q<r ?q:r
q-=s
t=r-s
v=s/2
w=s-v
r=r+2
o=n+e*m+b+x*v+e*t+x*w+f+o+n+e*m+f+z*w+x*t+z*v+b
end
$_=_*r+o

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.