16
\$\begingroup\$

You need to build a pyramid from cubes. Cubes can be viewed from 2 angles:

  _____        _____
 /\    \      /    /\
/  \____\    /____/  \
\  /    /    \    \  /
 \/____/      \____\/

This is an example for 2-size cubes from the 2 possible angles. The height of the cubes is $size slashes (or back-slashes), and the width of the cube is 2 * $size underscores. The top level width should contain an extra underscore character.

Input will be provided as a string containing a number (size of cubes), slash or backslash (to indicate direction/angle), and another number (height of the pyramid).

Examples:

Input:

1/1

Output:

 ___
/\__\
\/__/

Input:

1\1

Output:

 ___
/__/\
\__\/

Input:

2/1

Output:

  _____
 /\    \
/  \____\
\  /    /
 \/____/

Input:

1/2

Output:

     ___ 
 ___/\__\
/\__\/__/
\/__/\__\
    \/__/

Input:

2\2

Output:

  _____          
 /    /\         
/____/  \_____   
\    \  /    /\ 
 \____\/____/  \ 
 /    /\    \  /
/____/  \____\/ 
\    \  /        
 \____\/        

Input:

1/3

Output:

         ___  
     ___/\__\
 ___/\__\/__/
/\__\/__/\__\
\/__/\__\/__/
    \/__/\__\
        \/__/
  • Trailing/leading whitespaces are OK.
  • Standard loopholes are disallowed.
  • You can assume input will always be valid.
  • You may assume the input won't cause too big output, i.e: no line wrapping when the output is printed to the terminal.
  • Size of cube & height of pyramid is positive (i.e. ≥ 1)
  • This is code-golf, so shortest code in bytes wins.

Current Winner is:

Glen O with 270 bytes in julia

challenge stays open. if you beat the current best, I'll update the accepted answer.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ Your cubes are different from the ones in recent diamond pattern challenges, in that the top row has 2s+1 underscores, wheras the other challenges had 2s underscores on the top row and 2s-1 on the other rows. That means your horizontal pitch is 3s+1. I guess it's good to mix things around. Just making the observation in case someone misses it. \$\endgroup\$ Aug 11, 2015 at 13:58
  • 1
    \$\begingroup\$ what are the max values of size and height? can we assume they are one digit? \$\endgroup\$ Aug 11, 2015 at 14:00
  • \$\begingroup\$ no, you may not assume it is one digit, but you may assume the provided input won't cause the output to be "too big", i.e. won't cause line wrapping in your terminal. \$\endgroup\$
    – gilad hoch
    Aug 11, 2015 at 14:03

3 Answers 3

9
\$\begingroup\$

Perl, 343 bytes

$_=<>;$_=~/(\d+)(\S)(\d+)/;$v=$2eq'/';$w=$1*3+1;for$y(0..$1*2*$3){$k=$w*$3+$1-1;for$z(0..$k){$x=$v?$k-$z:$z;$q=($y+$1-1)/$1|0;$r=$x/$w|0;$d=$q+$r&1;$f=($y-1)%$1;$f=$1-$f-1if$d;$g=($x-$f)%$w;$u=$r;$q=2*$3-$q+1,$u++if$q>$3;print(($q>=$r&&$q&&$g==0)||($q>$r&&$g==$w-$1)?$d^$v?'/':'\\':$q>=$u&&$y%$1==0&&$g>0&&$g<($w-$1+($q==$r))?"_":$")}print$/}

Multiline with comments:

$_=<>;$_=~/(\d+)(\S)(\d+)/;$v=$2eq'/'; # read input
$w=$1*3+1; # compute width of each cube in chars
for$y(0..$1*2*$3){$k=$w*$3+$1-1;for$z(0..$k){ # iterate over rows, columns
    $x=$v?$k-$z:$z;   # flip x co-ordinate based on 2nd param
    $q=($y+$1-1)/$1|0;$r=$x/$w|0;   # parallelogram row and column index
    $d=$q+$r&1;  # flag parallelogram as left or right leaning
    $f=($y-1)%$1;$f=$1-$f-1if$d;  # compute a zig-zag offset
    $g=($x-$f)%$w;  # compute column position, offset by zig-zag
    $u=$r;$q=2*$3-$q+1,$u++if$q>$3; # vertical threshold for printing chars   
    print(($q>=$r&&$q&&$g==0)||($q>$r&&$g==$w-$1)?$d^$v?'/':'\\': # output slash
    $q>=$u&&$y%$1==0&&$g>0&&$g<($w-$1+($q==$r))?"_":$") # underscore or space
}print$/}   # print out newline at end of row

Example output:

2/3
                _____  
               /\    \ 
         _____/  \____\
        /\    \  /    /
  _____/  \____\/____/ 
 /\    \  /    /\    \ 
/  \____\/____/  \____\
\  /    /\    \  /    /
 \/____/  \____\/____/ 
       \  /    /\    \ 
        \/____/  \____\
              \  /    /
               \/____/ 

I also tried implementing it as a C function using the same algorithm, hoping to save bytes from the luxury of single-character variable names, but it ended up 15 bytes larger, at 358 bytes (needs to be compiled with -std=c89 under gcc to leave off the void in the function header):

j(char*s){int c,p,v,x,y,k,z,u,g,w,r,d,q,f;char e;sscanf(s,"%d%c%d",&c,&e,&p);v=e=='/';w=c*3+1;for(y=0;y<=c*2*p;y++){k=w*p+c-1;for(z=0;z<=k;z++){x=v?k-z:z;q=(y+c-1)/c;r=x/w;d=q+r&1;f=(y+c-1)%c;if(d)f=c-f-1;g=(x-f)%w;u=r;if(q>p){q=2*p-q+1;u++;}printf("%c",(q>=r&&q&&g==0)||(q>r&&g==w-c)?d^v?'/':'\\':q>=u&&y%c==0&&g>0&&g<(w-c+(q==r))?'_':' ');}printf("\n");}}
\$\endgroup\$
3
  • \$\begingroup\$ You should be able to get most, if not all those 15 bytes back on the C version: printf("%c" --> putchar( , printf("\n") --> puts("") , move all int declarations outside the function, then you can eliminate the int (see meta.codegolf.stackexchange.com/q/5532/15599), change all character literals for their ascii codes eg ' ' --> 32. Refactoring your for loops eg for(k+1;z--;) can also help but is trickier. \$\endgroup\$ Aug 12, 2015 at 5:56
  • \$\begingroup\$ Also I think e can be an int provided you initialize it to zero. sscanf will only overwrite the least significan byte and may leave any existing garbage in the other three bytes. \$\endgroup\$ Aug 12, 2015 at 6:02
  • \$\begingroup\$ finally I think a full program will go better than a function in this case. You gain three extra characters from main instead of j but you don't have to pass parameter s around, and you can take advantage of automatic initialization of global variables. \$\endgroup\$ Aug 12, 2015 at 6:22
3
\$\begingroup\$

Julia - 503 455 369 346 313 270 bytes

f=A->(t=47∉A;h='/'+45t;(m,n)=int(split(A,h));W=2m*n+1;X=(l=3m+1)n+m+1;s=fill(' ',X,W);s[end,:]=10;for i=1:n,j=i:n,M=1:m s[M+i*l+[[L=[J=(2j-i)m,J,J-m]+M W-L]X.-[l,m,0] [0 m].+[1,m,m].+[J,J+m,J-m]X-l]]=[1,1,1]*[h 139-h 95 s[i*l,i*m-m+1]=95]end;print((t?s:flipud(s))...))

Ungolfed:

function f(A)
  t=47∉A      # Determine if '/' is in A ('/' = char(47))
  h='/'+45t   # Set h to the appropriate slash ('\\' = char(92), 92=47+45)
  (m,n)=int(split(A,h)) # Get the two integers from A
  W=2m*n+1    # Get number of rows of output (vertical height of pyramid)
  X=(l=3m+1)n+m+1 # Get columns of output + 1 (for newlines)
  s=fill(' ',X,W) # Create 'canvas' of size X x W
  s[end,:]=10 # Put newlines at end of each output row
  for i=1:n, j=i:n, M=1:m
    # This is where the fun happens.
    # K and L represent the shifting points for '/' and '\\' in the
    # horizontal and vertical directions.
    # They are used to make the code neater (and shorter).
    K=M+i*l-[l,m,0]
    L=[J,J,J-m]+M
    # The next two assign the slashes to appropriate places
    s[K+L*X]=h
    s[K+(W-L)X]=139-h
    # This one puts the first 2m underscores in each of the underscore sets
    s[M+i*l-l+[0 m].+[1,m,m].+[J,J+m,J-m]X]=95
    # This places the final underscores on the top edges (repeatedly)
    s[i*l,i*m-m+1]=95
  end
  # The above produces the array orientation for backslash, but uses
  # the "wrong" slashes along the diagonals if there's a forward slash.
  # This line flips the matrix in that case, before printing it.
  print((t?s:flipud(s))...))
end

Usage:

f("3/2")

or

f("2\\3")
\$\endgroup\$
3
\$\begingroup\$

Ruby, 332

The only golfing done so far is elimination of comments and indents. I will golf later.

gets.match(/\D/)
d=$&<=>"@"
n=$`.to_i
m=2*n
p=$'.to_i
a=(0..h=4*n*p).map{' '*h*2}
(p*p).times{|j|
x=h-j/p*(3*n+1)*d
y=h/2+(j/p-j%p*2)*n
if j%p<=j/p
(-n).upto(n){|i|
a[y+i][i>0?x+m+1-i :x-m-i]=?/
a[y+i][i>0?x-m-1+i :x+m+i]='\\'
a[y+n][x+i]=a[y-n][x+i]=a[y][x-d*(n+i-1)]=?_
a[y+i+(i>>9&1)][x+d*i.abs]='\_/'[(0<=>i*d)+1]
}
end
}
puts a

I set up an array of spaces and poke the individual characters into it. There is quite a lot of overdrawing both of one cube on top of another (work from bottom to top) and within the cube itself, to avoid extra code. I make the pyramid by drawing a rhombus (similar to https://codegolf.stackexchange.com/a/54297/15599) and surpressing the top half.

The hard part was drawing the scalable cube. I started off with a perimeter hexagon with 2n+1 _characters on the horizontal sides. I also had had 2n+1 / and \ ,so I had one too many, but by plotting the _ last I overwrite them.

The internal lines are the only ones that are changed depending on the direction the cube is facing. I plot all the / and \ with a single assignment. abs helps reverse the direction, and i>>9&1 adds an extra 1 to the negative values of i, which lowers the top part down. for i=0 one of the required _ is overplotted, so the selector string '\_/' contains all three symbols, selected according to the sign of i.

The whitespace around the output is ample but not excessive: 4*p*n high and 8*p*n wide (the latter is to enable the apex cube to always be in the centre of the output.) I understand "trailing / leading whitespaces" to include whole lines, but can revise if necessary.

Ungolfed code

gets.match(/\D/)                                   #find the symbol that is not a digit. it can be extracted from $&
d=$&<=>"@"                                         #direction, -1 or 1 depends if ascii code for symbol is before or after "@"
n=$`.to_i                                          #side length extracted from match variable $`
m=2*n
p=$'.to_i                                          #pyramid height extracted from match variable $'
a=(0..h=4*n*p).map{' '*h*2}                        #setup an array of h strings of h*2 spaces

(p*p).times{|j|                                    #iterate p**2 times
  x=h-j/p*(3*n+1)*d                                #calculate x and y coordinates for each cube, in a rhombus
  y=h/2+(j/p-j%p*2)*n                              #x extends outwards (and downwards) from the centre, y extends upwards 

  if j%p<=j/p                                      #only print the bottom half of the rhombus, where cube y <= cube x  
    (-n).upto(n){|i|                               #loop 2n+1 times, centred on the centre of the cube 
      a[y+i][i>0?x+m+1-i :x-m-i]=?/                #put the / on the perimeter of the hexagon into the array          
      a[y+i][i>0?x-m-1+i :x+m+i]='\\'              #and the \ also.
      a[y+n][x+i]=a[y-n][x+i]=a[y][x-d*(n+i-1)]=?_ #plot all three lines of _ overwriting the / and \ on the top line    
      a[y+i+(i>>9&1)][x+d*i.abs]='\_/'[(0<=>i*d)+1]#plot the internal / and \ overwriting unwanted _
    }
  end
}
puts a
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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