36
\$\begingroup\$

Task description:

Draw a cube in ASCII art in roughly a cabinet projection.

Monospaced fonts often have characters that are about twice as high as they are wide. Since the input is the length of the vertical lines (excluding the corners), horizontal lines are drawn with twice as many characters so that the resulting image is really roughly a cube. The receding lines are drawn at half the length as mandated by a cabinet projection.

Corners of the cube are represented by +, horizontal lines by -, vertical lines by | and diagonal ones use /.

Summarizing: Let the input be n, then

  • A horizontal edge of the cube are drawn with - and consists of 2 n characters.
  • A vertical edge of the cube are drawn with | and consists of n characters.
  • A diagonal edge of the cube are drawn with / and consists of n/2 characters.
  • Corners of the cube are drawn with +. Corners are not counted for the length of an edge as detailed above (see examples below as well).

Input:

The input, given on standard input, is a single positive, even number n (2 ≤ n ≤ 30) that gives the length of the vertical lines of the cube. It is followed by a single line break.

Output:

The output is a cube on standard output following above rules. Trailing whitespace on the lines is ignored.

Sample input 1:

2

Sample output 1:

  +----+
 /    /|
+----+ |
|    | +
|    |/
+----+

Sample input 2:

4

Sample output 2:

   +--------+
  /        /|
 /        / |
+--------+  |
|        |  |
|        |  +
|        | /
|        |/
+--------+

ETA: I now accepted the shortest solution. I will update the accepted answer when a shorter one comes along.

Since some people asked how long the entries of our contestants were:

227 – Python
240 – Perl
310 – C
315 – C
326 – VB.NET
459 – C

As well as our own solutions (not ranked with the others):

140 – Golfscript
172 – Ruby
183 – PowerShell

\$\endgroup\$
4
  • \$\begingroup\$ can you tell as a little about the best solutions you had? How many chars did the smallest have? \$\endgroup\$
    – Champo
    Commented Jan 29, 2011 at 20:17
  • \$\begingroup\$ @Juan: Added the requested info \$\endgroup\$
    – Joey
    Commented Feb 6, 2011 at 15:59
  • 1
    \$\begingroup\$ Amusingly enough, C++ can use similar drawings as "analog literals": hostilefork.com/2009/08/29/tweakinganalog-literals-humor \$\endgroup\$ Commented Feb 6, 2011 at 16:26
  • \$\begingroup\$ @Hostile: Yep, that one was nice, if a bit evil ;-) \$\endgroup\$
    – Joey
    Commented Feb 6, 2011 at 16:35

16 Answers 16

17
\$\begingroup\$

Python - 248 243 230 227 191

Slightly messy but it basically prints the cube line by line(using a string buffer).

t=v=h=input()/2
s,p,b,f,n=" +|/\n"
l=p+"-"*t*4+p;S=s*4*t;k=s*h;K=b+S+b
r=s*t+s+l+n
while t:r+=s*t+f+S+f+s*(h-t)+b+n;t-=1
r+=l+k+b+n+(K+k+b+n)*(v-1)+K+k+p+n
while v:v-=1;r+=K+s*v+f+n
print r+l

Thanks to @marcog, for pointing out the first line, @ThomasO for pointing out the second line and to @Juan for making me realise I can combine lines.

\$\endgroup\$
3
  • 4
    \$\begingroup\$ To save some more space change s=" ";p="+";b="|";f="/";n="\n" to s,p,b,f,n=" +|/\n". \$\endgroup\$
    – Thomas O
    Commented Jan 29, 2011 at 18:59
  • 1
    \$\begingroup\$ One up vote is not enough. You've pushing me to improve my solution to limits that I thought impossible, thanks :D \$\endgroup\$
    – Champo
    Commented Jan 29, 2011 at 20:24
  • \$\begingroup\$ :) now to see if better is possible. \$\endgroup\$
    – JPvdMerwe
    Commented Jan 29, 2011 at 20:54
10
\$\begingroup\$

Golfscript - 96 chars

~:<2/:$){' '*}:s~'++'<'--'**:^n$,{.$\-s'//'2s<*:&*@s'|':|n}%^$[$s|n|&|]*$s'+'n$,{n'/'@s|&|}%-1%^

Most of the compactness comes from aggressively storing almost everything to a variable (unless you include being written in golfscript).

<    n
$    n/2
s    {' '*}     # top of the stack becomes a string of that many spaces
^    '+------+'
&    '      '   # 2n spaces, i.e. 2s<* or <s2*
|    '|'

A couple of other small tricks here.

  1. 'LR''str'* -> 'LstrR'.
  2. Since we need to reverse the order of lines in the last array, we opt to do this after generating the text instead of before. This allows us to save one character because the spaces before the '/' only needs to go past two stack elements (@) instead of 3 (@ .. \).
\$\endgroup\$
10
\$\begingroup\$

Python - 179

h=input()*2
j=d=h/4
q,e,u,p,k="| \n+/"
w=e*d
s=p+'-'*h+p
i=''
o=e+w+s+u
v=q+e*h+q
while j:o+=e*j+k+e*h+k+e*(d-j)+q+u;j-=1;i+=v+e*j+k+u
print o+s+w+q+u+(v+w+q+u)*(d-1)+v+w+p+u+i+s

I'd like to note that I took some ideas from JPvdMerwe (Using a string to print once, and the one-liner for that I didn't know was correct syntax in Python).

\$\endgroup\$
12
  • \$\begingroup\$ Line 3 is missing a 2 at the end, which, unfortunately, pushes up the count to 256. \$\endgroup\$
    – JPvdMerwe
    Commented Jan 29, 2011 at 17:10
  • \$\begingroup\$ @JPvdMerwe oops, thanks for catching that! \$\endgroup\$
    – Champo
    Commented Jan 29, 2011 at 17:31
  • 1
    \$\begingroup\$ Maybe you can try caching the results in a string like I did and only print once? \$\endgroup\$
    – JPvdMerwe
    Commented Jan 29, 2011 at 17:57
  • 1
    \$\begingroup\$ @Juan I think we should avoid keeping old copies in the post, unless the two versions are majorly different. They're viewable in the edit history if someone wants to read it. \$\endgroup\$
    – moinudin
    Commented Jan 29, 2011 at 18:54
  • 2
    \$\begingroup\$ As for the FAQ: I often include a length history in my posts ( here was an example that got too long to include, though). Don't know whether such a thing is helpful but it may help others to discover what tricks were used to keep it short. Although I also have a SVN history for that as well. \$\endgroup\$
    – Joey
    Commented Jan 29, 2011 at 19:04
8
\$\begingroup\$

fortran 77 -- 484 characters

      program z
      read(*,*) i
      g=f('+- ',i/2+1,i,0)
      do k=1,i/2
         g=f('/ |',i/2-k+1,i,k-1)
      end do
      g=f('+-|',0,i,i/2)
      do k=1,i/2-1
         g=f('| |',0,i,i/2)
      end do
      g=f('| +',0,i,i/2)
      do k=1,i/2
         g=f('| /',0,i,i/2-k)
      end do
      g=f('+- ',0,i,0)
      stop
      end
      real function f(c,l,m,n)
      character c(3)
      write(*,*)(' ',j=1,l),c(1),(c(2),j=1,2*m),c(1),(' ',j=1,n),c(3)
      return
      end

No real point in providing a "unobsfucated" version. And note that markdown doesn't get along well with the indent requirements.

I tried fortran because of the inline for loops provided by the write statement. Obviously they help but don't add up to enough to kill the wordiess of the language. It could be reduce by using freeform input.

Validation:

 $ wc cube_func_array.f
 22  41 484 cube_func_array.f
 $ gfortran cube_func_array.f
 $ echo 2 | ./a.out
   +----+ 
  /    /|
 +----+ |
 |    | +
 |    |/
 +----+ 
 $ echo 4 | ./a.out
    +--------+ 
   /        /|
  /        / |
 +--------+  |
 |        |  |
 |        |  +
 |        | /
 |        |/
 +--------+ 

Thankfully the spec doesn't say what size one should look like:

 $ echo 1 | ./a.out
  +--+ 
 +--+|
 |  |+
 +--+ 

but other odd sizes are reasonable:

 $ echo 3 | ./a.out
   +------+ 
  /      /|
 +------+ |
 |      | +
 |      |/
 +------+ 
\$\endgroup\$
2
  • \$\begingroup\$ Interesting choice of language :-). Well, size 1 doesn't look too bad. My solution throws up an endless loop. Different rounding behaviors were the reason to throw out odd sizes, if I remember correctly (and the upper bound of 30 for fitting into 80 characters width). \$\endgroup\$
    – Joey
    Commented Jan 29, 2011 at 22:08
  • 1
    \$\begingroup\$ @joey: I do fortran from time to time, and am happy if I'm less than a factor of 10 longer than the winner. \$\endgroup\$ Commented Jan 29, 2011 at 22:11
5
\$\begingroup\$

PostScript, 237

[/n(%stdin)(r)file token()/p{print}/r{repeat}([){{( )p}r}/N{n 2 mul}(]){n 2 idiv}/l{N(+)p{(-)p}r(+)p}/I{(|)p}/X{][p}>>begin
( )X l()=]-1 1{dup[(/)p N[(/)p]exch sub[(|)=}for
l(|
)X]1 sub{I N[I(|
)X}r
I N[I(+
)X]-1 1{I N[I 1 sub[(/)=}for
l

History:

  • 2011-03-01 01:54 (427) First attempt.
  • 2011-03-01 02:01 (342) defed a few more things that appeared often.
  • 2011-03-01 02:24 (283) Even more defs.
  • 2011-03-01 02:42 (281) Aaand another def that saves two more bytes.
  • 2011-03-01 03:01 (260) [ and ] have nice properties when used as variables :-). Thanks to KirarinSnow.
  • 2011-03-01 03:12 (246) Inline line breaks, using a dict instead of numerous defs. Thansk again :-).
  • 2011-03-01 03:26 (237) More thanks to KirarinSnow.
\$\endgroup\$
4
\$\begingroup\$

My own solution, since it has already been beaten to death by Python:

Windows PowerShell, 183

$t=($w=($s=' ')*($o=($n="$input")/2))*4
$r="|$t|"
$s*($a=$o+1)+($q='+'+'--'*$n+'+')
$o..1|%{$s*--$a+"/$t/$($s*$b++)|"}
"$q$w|"
for(;$o-++$x){"$r$w|"}"$r$w+"
--$b..0|%{$r+$s*$_+'/'}
$q
\$\endgroup\$
2
  • \$\begingroup\$ Ah...languages that let you "multiple" strings by a scalar help for this... \$\endgroup\$ Commented Feb 4, 2011 at 17:46
  • \$\begingroup\$ Well, it's still far behind Ventero's Ruby or Golfscript – as usual ;) \$\endgroup\$
    – Joey
    Commented Feb 4, 2011 at 18:44
3
\$\begingroup\$

Ruby 1.9, 172 165 162 characters

w=(s=?\s)*o=(n=gets.to_i)/2;r=(z=?|)+w*4+z
puts s*(o+1)+q=?++?-*2*n+?+,(l=0...o).map{|u|[s*(o-u),w*4,s*u+z]*?/},q+w+z,[r+w+z]*o-=1,r+w+?+,l.map{|u|r+s*(o-u)+?/},q
\$\endgroup\$
0
2
\$\begingroup\$

Ruby - 423 characters

Really don't want to share this since it's such a horrible count, but since I've written it might as well.

n=$<.read.to_i
a=(q=Array).new(n+n/2+3){q.new(2*n+n/2+3,' ')<<"\n"}
a[n+1][2*n+n/2+2]=a[0][n/2+1]=a[0][2*n+n/2+1]=a[n/2+1][0]=a[n/2+1][2*n]=a[n+n/2+2][0]=a[n+n/2+2][2*n]=:+
a[0][n/2+2,n*2-1]=a[n/2+1][1,n*2-1]=a[n+n/2+2][1,n*2-1]=[:-]*2*n
a[n/2+2,n].each{|b|b[0]=b[2*n+1]=:|}
a[1,n].each{|b|b[2*n+n/2+2]=:|}
c=n/2
a[1,n/2].each{|b|b[c]=b[2+2*n+c-=1]=:/}
c=n/2
a[n+2,n/2].each{|b|b[2+2*n+c-=1]=:/}
a.flatten.each{|g|print g}

Could probably be reduced by quite a bit but I doubt this brute-force approach is going to come anywhere near a decent number of characters so I can't be bothered.

\$\endgroup\$
2
\$\begingroup\$

Vyxal J, 61 bytes

½:→›\+?-m:£꘍←ƛ\/?꘍m꘍;Ṙ?ɾv\|?꘍vm¥pJ←ʁ←›v←ȮṘJJ꘍?\|*\++←\/*+f+⁋¥

Try it Online!

This was harder than I thought it'd be. -4 thanks to @AaroneousMiller. Woo, beating ascii-art langs!

\$\endgroup\$
1
2
\$\begingroup\$

Vyxal, 40 bytes

½"‛|/f*?d¤-JǏz:Yf:@2+$¤p\+jǏ»×g=↔F»8τ„ø∧

Try it Online!

     f*                                  # Repeat each character of
  ‛|/                                    # "|/"
½"                                       # by each of [input, input/2]
          -                              # Append 
       ?d                                # 2*input hyphens
         ¤                               # To the empty string
           J                             # Add that to the list
            Ǐz                           # Get all pairs
              :Yf                        # Double each pair
                                      ø∧ # Draw on canvas with
                 :@                      # Lengths: length of each of that
                   2+                    # + 2
                     $                   # String: That
                        \+j              # Joined by "+"
                      ¤p   Ǐ             # With a + at the start and end
                            »×g=↔F»      # Directions: compressed number 
                                   8τ„   # Converted from base 8 to [4, 1, 0, 5, 1, 6, 5, 2, 6, 4, 2, 0]
\$\endgroup\$
1
\$\begingroup\$

PHP, 401 392 382 363 characters:

<? $h=fgets(STDIN);$s="str_repeat";$w=$h*2;$d=$h/2;$b=$h;$c=" ";echo$s($c,$h/2+1)."+".$s("-",$w)."+\n";for($i=1;$i<=$d;$i++,$a=--$b){echo$s($c,($h/2+1)-$i)."/".$s($c,$w)."/".$s($c,$i-1)."|\n";}echo"+".$s("-",$w)."+".$s($c,$d)."|\n";for($i=1;$i<=$h;$i++){echo"|".$s($c,$w)."|";echo $a-->0?$s($c,$b).($a>0?"|":"+")."\n":$s($c,$h-$i)."/\n";}echo"+".$s("-",$w)."+\n";

I originally did this to see how short I could manage to do this in PHP, as I knew that it would be pretty long. I'm sure it could be reduced, but not by much considering PHP doesn't have many shortcuts.

Validation:
http://codepad.viper-7.com/ftYYz9.php53

Ungolfed Version: http://codepad.viper-7.com/4D3kIA

\$\endgroup\$
3
  • \$\begingroup\$ Just modified it to read from stdin, missed that in the question. Doesn't need the function anymore because of that. \$\endgroup\$ Commented Jan 30, 2011 at 0:18
  • \$\begingroup\$ Modified the code so that it reads from stdin correctly. Also golfed it a little more to reduce the size. \$\endgroup\$ Commented Jan 30, 2011 at 1:55
  • \$\begingroup\$ The lower right diagonal line isn't there and instead an offset vertical line appears. Unless I'm doing something wrong in invoking it, though. \$\endgroup\$
    – Joey
    Commented Jan 30, 2011 at 9:10
1
\$\begingroup\$

Perl, 163

$d=<>/2;$s=$"x$d;$H=$s x4;$f="|$H|";$t.=$"
x$d--."/$H/".$"x$_."|\n",$m.="$f$s|\n",$b
=$f.$"x$_."/\n$b"for 0..$d-1;$_="+$H+";
y/ /-/;say" $s$_\n$t$_$s|\n$m$f$s+\n$b$_"

Perl 5.10 or later, run with perl -E '<code here>'

Respaced version:

$d = <> / 2;
$s = $" x $d;
$H = $s x 4;
$f = "|$H|";

$t .= $" x $d-- . "/$H/" . $"x$_ . "|\n",
$m .= "$f$s|\n",
$b = $f . $" x $_ . "/\n$b"
  for 0 .. $d-1;

$_ = "+$H+";
y/ /-/;
say " $s$_\n$t$_$s|\n$m$f$s+\n$b$_"
\$\endgroup\$
1
\$\begingroup\$

Perl, 269 269 262 256 245 244 237 226 228 224 217 chars

sub p{y/xS/+\//;print;y/+\//xS/}$b=/2;$a=$b;$_=" xx\n";s/ x/  x----/while($a--);until(/^S/){p;s/ [xS]/S /g;s/-x/S|/;y/-/ /}s/ (?= *S)/-/g;y/S/x/;p;y/-x/ |/;p while(--$b);s/.$/x/;while(/ \|/){p;s/..$/S/}y/|S /++-/;p

The basic idea is to do everything with regex substitutions. Because two of the characters used (+ and /) are special characters and turn up a lot in the regexes, it's worthwhile using other characters and substituting them to print.

Slightly more legible version:

# Subroutine to substitute, print, and unsubstitute as described above
sub p{y/xS/+\//;print;y/+\//xS/}
# Read from stdin and set up the initial line
$b=<>/2;$a=$b;$_=" xx\n";
s/ x/  x----/ while($a--);
# Print the top face
until(/^S/) {
  p;
  s/ [xS]/S /g; # First time round: left + -> /; subsequent times move / left
  s/-x/S|/; # Only relevant first time round the loop
  y/-/ / # Only relevant first time round the loop
}
# Prepare and print the line containing second horizontal line
s/ (?= *S)/-/g;
y/S/x/;
p;
# Now print (n-1)/2 identical lines
y/-x/ |/;
p while (--$b);
# Bring the right edge in
s/.$/x/;
while(/ \|/)
{
  p;
  s/..$/S/
}
# Final line
y/|S /++-/;
p

In a sense I'm cheating by using $b as a counter in the intermediate loop - I could instead append whitespace in the loop over $a and then use regex replaces for that loop too - but I'm going to allow that slight deviation from a pure-regex solution.

No doubt some scary person can turn this into a much shorter sed script.

\$\endgroup\$
2
  • \$\begingroup\$ "Slightly more legible version" - gotta love that Perl only becomes slightly more readable when newlines and whitespace are included. :) \$\endgroup\$
    – Steve
    Commented Feb 12, 2011 at 18:25
  • \$\begingroup\$ @Steve, even with the comments you have to know a bit of Perl to understand it. Using y for tr isn't obvious, and as for the way "while" can go before or after... \$\endgroup\$ Commented Feb 12, 2011 at 18:57
1
\$\begingroup\$

Lua, 294 302 292 bytes

Golfed:

n=(...)p="+"d=2*n s=" "S=s:rep(d)h=n/2 T=s:rep(h)L="\n"o="/"v="|"a=p..("-"):rep(d)..p r=T..s..a..L for i=0,h-1 do r=r..s:rep(h-i)..o..S..o..s:rep(i)..v..L end r=r..a..T..v for i=1,h do r=r..L..v..S..v..T..(i==h and p or v) end for i=h-1,0,-1 do r=r..L..v..S..v..s:rep(i)..o end print(r..L..a)

Ungolfed:

n        = n or io.read() or 6
plus     = "+"
doubled  = 2*n
space    = " "
Space    = space:rep(doubled)
halved   = n/2
T        = space:rep(halved)
Line     = "\n"
or_sign  = "/"
vertical = "|"
a        = plus..("-"):rep(doubled)..plus
result   = T..space..a..Line

for i=0,halved-1 do
    result = result .. space:rep(halved-i) .. or_sign .. Space .. or_sign .. space:rep(i) .. vertical .. Line
end

result = result..a..T..vertical

for i=1,halved do
    result = result .. Line .. vertical .. Space .. vertical .. T .. (i==halved and plus or vertical)
end

for i=halved-1,0,-1 do
    result = result .. Line .. vertical .. Space .. vertical .. space:rep(i) .. or_sign
end

print(result .. Line .. a)
\$\endgroup\$
3
  • \$\begingroup\$ Input is given on the standard input stream. This doesn't seem to work here. \$\endgroup\$
    – Joey
    Commented Mar 29, 2016 at 18:40
  • \$\begingroup\$ You can also leave out the or 6 after the read() call, which saves four bytes :-) \$\endgroup\$
    – Joey
    Commented Mar 30, 2016 at 18:19
  • \$\begingroup\$ Hm, now with the (...) it doesn't work for me anymore on Lua 5.1.4. \$\endgroup\$
    – Joey
    Commented Mar 30, 2016 at 19:11
1
\$\begingroup\$

Canvas, 63 bytes

╴»
±╵⁷/╋╴«3+⁷
-×+×║⌐1╴├╋;⁷├⁷±╋2⁷⁸⁸├⁷/╋12╴«├2⁷5×3+⁷±╵⁰2n{┤╴|*+∔╋

Try it here!

\$\endgroup\$
0
\$\begingroup\$

sed 4.2.2, 229 bytes

s/./--/g
h
s/..../ /g
s/$/ +/
G
s//+/
s/\n//p
y/-/ /
s%.+%/%
s%+%/|%p
:
s% /%/ %g
/^\//ta
p
t
:a
y% /%-+%
x
s/..../ /g
x
G
s/+-*|\n/+/
s/$/|/p
y/+-/| /
x
s/ //
x
tb
:c
p
:b
x
s///
x
tc
s/|/+/3p
:d
s% [/+]%/ %p
td
y/ |/-+/
s%/-*%%

Try it online! (w/ sample inputs 2, 4, 6, 8, and 10)

sed is almost certainly not the right tool for this, but we got there in the end. we take input as unary spaces and build the cube one line at a time. we only need two loops: the first finishes when the / hits the wall, and the other uses up the variable saved to the hold space to count down.

\$\endgroup\$

Your Answer

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

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