views:

2126

answers:

13

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).

+23  A: 

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
mobrule
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 Lutz
@Chris: mobrule's answers always lack a newline :P
LiraNuna
I think newlines at the end should be optional
gnibbler
Where's the picture of the hourglass?
gnibbler
gj mobrule, it only took you 4 tries :D
LiraNuna
had my code-golf questions became IOCCC as well?
LiraNuna
@LiraNuna That'd be IOPPP (International Obfuscated Perl Programming Pandemonium)
hobbs
I dunno; but my perl keeps saying STDOUT_TOP ; echo '5 52%' |perl -e '$S=-int((1-.01*pop)*($N=pop)*$N)+$N*$N;$,=$/;$S-=$s=$S>++$r?$r:$S,@o=($"x--$N."\\".x x($v=$s/2).$"x($t=$r++-$s).x x($w=$v+.5)."/",@o,$"x$N."/".($^=$N?$":_)x$w.x x$t.$^x$v."\\")while$N;print$^x++$r,@o'
dlamblin
@dlamblin: commandline arguments.
LiraNuna
Add links to CPAN modules in the form of `http://search.cpan.org/perldoc/Acme::AsciiArtinator`
Brad Gilbert
If you remove all the `$` and replace `while`,`print` etc with single letter identifiers it's *almost* as short as golfscript :)
gnibbler
@gnibbler: It's funny that Perl does as well as it does at code golf when 20% of the characters are `$`.
mobrule
+35  A: 

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= "x\0 \0x";
        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? "_\0x\0_":
   " \0x\0 ";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 = "x\0 \0x";
        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 ? "_\0x\0_" : " \0x\0 ";
        p("%*s/",h-i,"");
        for (k = 0; k<3; k++,q++,z += 2) {
            for (j = 0; j<*q; j++) {
                p(z);
            }
        }
        p("\\\n") ;
    }
}
Aaron
I think you confuse this with IOCCC :D
LiraNuna
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)
Aaron
one thing to note is that this isn't valid C, just C++. so the "C/C++" in the heading is misleading.
Evan Teran
+2  A: 

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.

exabytes18
"I need to find a better set of golf clubs." - I'd recommend a set of Perls. At least for golfing... ;-)
JasCav
you could use static import for system.out and and field for boolean. true*4+false*3=16+15=31static boolean b;+!b*4+b*3=17+8+3=28
01
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.
exabytes18
+3  A: 

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";}}
Evan Teran
typedef char* C; will save a few characters, since you only use a character pointer.
Ólafur Waage
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.
Evan Teran
+1  A: 

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
gnibbler
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
@LiraNuna, B is a string, and I need to add to the start not the end
gnibbler
+20  A: 
gnibbler
I always laugh when I see golfscript in one of these.
Ron Warholic
@Sid: How come? Use the right tool for the job!
LiraNuna
I like the way it is self documenting
gnibbler
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
@mobrule, Are you going to get your lasers down to 122 chars then :) http://stackoverflow.com/questions/1480023/code-golf-lasers/1698642#1698642
gnibbler
Crikey. There's even a better python Laser solution now.
mobrule
+1  A: 

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);}}
Ian P
+13  A: 

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
mobrule
Nice algorithm.`r+=2`
gnibbler
I ported this to golfscript but it came out ~20 longer than my existing solution :(
gnibbler
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...
DigitalRoss
(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
@DigitalRoss, My golfscript takes advantage of the symmetry too, but in a surprising way!
gnibbler
+3  A: 

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/\/\\\\//}"
ChristopheD
Input can be from stdin or command line args, so your 373 solution is valid.
LiraNuna
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 ;-)
ChristopheD
A: 

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
DigitalRoss
+6  A: 

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

Apocalisp
+3  A: 

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));?>
ChronoFish
My eyes! The pain! Thanks for reminding me.
Kugel
+2  A: 

Rebmu: 190 chars

rJ N 0% rN 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

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.rebol

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.)

Hostile Fork
This is a serious waste of Rebol cycles!
Graham Chiu