51

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

LiraNuna
  • 64,916
  • 15
  • 117
  • 140
  • 7
    I don't even use a calendar anymore. When these show up, I know it's Thursday! This one is cool. – Instantsoup Nov 05 '09 at 21:48
  • Should the input be from a file, command line or stdin? – Aaron Nov 05 '09 at 21:56
  • As with my previous code-golf questions - your choice. Most popular method is stdin, second most popular is command line arguments. I've seen only a few entries (answers) use files as input. – LiraNuna Nov 05 '09 at 22:00
  • Sorry to be a pest - is the example for 2 right? It seems like based on the other examples given it should be wider by 1 character... Like this: _____ \ / \x/ / \ /___\ – Aaron Nov 05 '09 at 22:08
  • @Aaron - Looking at it, I'm pretty sure example 2 is correct. But I'm not LiraNuna, so you might be right.. – Chris Lutz Nov 05 '09 at 22:16
  • @Aaron: I don't understand, the 2nd example looks correct. – LiraNuna Nov 05 '09 at 22:16
  • I think what @Aaron was looking at was that 2 was even and all the examples are odd. For even numbers, it would seem as though the bottom is \/ while odd numbered ones are \x/ – kersny Nov 05 '09 at 22:24
  • 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. – Aaron Nov 05 '09 at 22:28
  • @Aaron: my wording was wrong, not the examples :) to explain what I mean (on example 2): Hourglass of size 5 has capacity of 25. 25 * 25% = 13. putting 13 `x`s on a 25 capacity hourglass is even (since the `\x/` makes it 12+1) – LiraNuna Nov 05 '09 at 22:36
  • 2
    @LiraNuna - that doesn't really help me - should an hourglass of size 4 have a capacity of 16 or 12? – Aaron Nov 05 '09 at 22:44
  • I edited (hope you don't mind Lira) to hopefully cure the confusion. – Instantsoup Nov 05 '09 at 22:50
  • I think that is correct Instantsoup. Hint: the capacity is the square of the height – John La Rooy Nov 05 '09 at 22:51
  • @Instantsoup: Now there's a missing example for "giving the right side precedence", I'll add more. – LiraNuna Nov 05 '09 at 22:52
  • @Aaron - An hourglass of size n has capacity n*n. In this regard, there's no difference between an odd and even size hourglass. – Stephen Canon Nov 05 '09 at 22:53
  • Ah - the example of a height of 2 has been changed to what I originally thought it should be... thank you. – Aaron Nov 05 '09 at 22:54
  • I'm sorry for the confusion. Thanks Instantsoup for modifying the last example, I didn't know it'll cause trouble. – LiraNuna Nov 05 '09 at 22:58
  • 2
    No problem. I'm glad I understood something today. Now to waste company time implementing this in Enterprise Java! – Instantsoup Nov 05 '09 at 22:59
  • This question reminds me of Morse code - you can clearly see those who 'gets it' and those who don't. – LiraNuna Nov 06 '09 at 05:57
  • Why does the 945 char C solution have 50% more votes than the 191 char perl?? – John La Rooy Nov 07 '09 at 03:37
  • @gnibbler: because it was the first post to also be shaped like an hourglass. That definitely gave it a few extra votes in the beginning. – Evan Teran Nov 08 '09 at 02:18
  • @LiraNuna, Are you interested in re-asking your code-golf questions over on http://codegolf.stackexchange.com ? Apparently we can't migrate them because they are too old, but they were great questions and it'd be a pity to see them eventually get deleted here – John La Rooy Feb 19 '14 at 21:15

13 Answers13

36

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
  • 9,123
  • 5
  • 40
  • 38
  • 9
    I think you confuse this with IOCCC :D – LiraNuna Nov 05 '09 at 23:59
  • 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 Nov 06 '09 at 02:45
  • 1
    one thing to note is that this isn't valid C, just C++. so the "C/C++" in the heading is misleading. – Evan Teran Nov 07 '09 at 05:44
  • 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. – Respectech Aug 11 '13 at 00:08
23

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
mob
  • 117,087
  • 18
  • 149
  • 283
21

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

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
14

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
mob
  • 117,087
  • 18
  • 149
  • 283
8

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

  • This is a serious waste of Rebol cycles! – Graham Chiu Sep 09 '10 at 05:23
  • 1
    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: http://codegolf.stackexchange.com/ – HostileFork says dont trust SE Feb 03 '11 at 18:42
  • You actually achieved very good native compression using Rebmu. Trying to compress your Rebmu code added 2 bytes to the total. :-) – Respectech Aug 11 '13 at 00:07
  • @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. – HostileFork says dont trust SE Aug 11 '13 at 15:17
6

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
  • 34,834
  • 8
  • 106
  • 155
4

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
  • 87,561
  • 32
  • 179
  • 238
  • typedef char* C; will save a few characters, since you only use a character pointer. – Ólafur Waage Nov 07 '09 at 12:27
  • 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 Nov 07 '09 at 18:13
3

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/\/\\\\//}"
Community
  • 1
  • 1
ChristopheD
  • 112,638
  • 29
  • 165
  • 179
  • Input can be from stdin or command line args, so your 373 solution is valid. – LiraNuna Nov 09 '09 at 22:40
  • 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 Nov 09 '09 at 22:48
2

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
  • 19
  • 1
  • 1
  • 1
    "I need to find a better set of golf clubs." - I'd recommend a set of Perls. At least for golfing... ;-) – JasCav Nov 06 '09 at 14:11
  • 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 – IAdapter Nov 10 '09 at 10: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 Nov 10 '09 at 18:54
2

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
  • 3,589
  • 5
  • 27
  • 38
1

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
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
1

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
  • 12,840
  • 6
  • 48
  • 70
0

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
Community
  • 1
  • 1
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329