# Draw Reutersvärd's Triangle

Draw your own version according to these rules:

• Exactly 9 cubes total
• Each side is made up of exactly 4 cubes
• The cubes appear to overlap such that the rendered object is in fact an impossible object
• 4 distinct colours are used - one for the background and 3 for each of the faces of the cubes
• In bitmap output, the full triangle should be at least 100 pixels tall and at least 100 pixels wide
• Aspect ratio: the width and height of the full triangle should not be off by more than a factor of 2
• The triangle may be rotated by any amount relative to the above image
• The triangle may or may not be reflected relative to the above image

# Mathematica, 237 bytes

n={-1,1}#&;c_~g~s_:=Polygon[c+s#&/@{k={12,9},m=n@k,t={0,-12}}];p={#,#2~g~1,#3~g~-1}&;a=p[Cyan,#-k,#+m]&;b=p[Blue,#-t,#+k]&;c=p[Red,#-m,#+t]&;Graphics@{{a@#,b@#,c@#}&/@{j=4k,s=4{4,9},n@s,4m,r={-32,8},q=-4{4,5},4t,n@q,n@r},a@j,b@s,c@j,c@s}


1  n = {-1, 1} # &;
2  c_~g~s_ := Polygon[c + s # & /@ {k = {12, 9}, m = n@k, t = {0, -12}}];
3  p = {#, #2~g~1, #3~g~-1} &;
4  a = p[Cyan, # - k, # + m] &;
5  b = p[Blue, # - t, # + k] &;
6  c = p[Red, # - m, # + t] &;
7  Graphics@{
8    {a@#, b@#, c@#} & /@
9      {j = 4 k, s = 4{4, 9}, n@s, 4 m, r = {-32, 8},
10       q = -4{4, 5}, 4 t, n@q, n@r},
11   a@j, b@s, c@j, c@s}


Line 1 defines a function n that negates the first coordinate of an ordered pair. Line 2 defines a function g that produces a (roughly) equilateral triangle centered at the point c, and pointing down or up depending on whether s is 1 or -1. Line 3 defines p to be a parallelogram template consisting of a color and two triangles, and lines 4–6 define a, b, and c to be the three different specific types of parallelograms that appear in the cubes.

Line 8 defines a function {a@#, b@#, c@#}& that draws an entire cube centered at the point #; lines 9 and 10 apply that to the nine points needed to make the larger triangle. This produces nine cubes, starting at the upper right and going counterclockwise, where the later ones cover up parts of the earlier ones. Finally, line 11 redraws four parallelograms (in the upper right of the picture) so that they end up covering the later cubes as they're supposed to. The output is below:

• Wait what Mathematica doesn't have a built-in for this? Jan 15 '17 at 3:09
• Impressive work! +1 Aug 8 '20 at 16:24
• @DigitalTrauma Always in the next version. :) Aug 9 '20 at 12:01

# Brain-Flak, 487810327722 75564 + 1 = 75565 bytes

PasteBin

Try it Online

With the -A flag this outputs an ASCII ppm file that looks as follows:

# Explanation

You may have already guessed I did not write this by hand. So here's how I did it:

I first made the image you see above from the image provided by the challenge. It has the distinction of having no color channel that is at any value other than 255 or 0 this way we can wrap it up into a smaller file with the max color channel set to 1. I then wrote a python script to golf a Brain-Flak program that solves this using a module I wrote myself that can be found here. Its not very polished its just a hack I threw together for things like this. push is a function that returns efficient Brain-Flak code to push a number to the stack and kolmo is a very simple Kolmogorov complexity solving program that attempts to find an efficient way to push a particular string to the stack.

from value import push,kolmo

def group(a, n):
return zip(*[a[i::n]for i in range(n)])

f=open("R.ppm")
f.close()

def hardcode(string):
result = push(ord("\n")).join("(<>({})<>"+{"0":"","1":"()"}[x]+")"for x in string)
return result

last = ""
acc = 0
result = push(ord("0"))+"<>"
for x in a+[""]:
if x != last:
string = ("" if not last else kolmo("\n")+hardcode(last))
result += min([push(acc)+"{({}[()]<%s>)}{}"%string,acc*string],key=len)
acc=1
else:
acc += 1
last = x
print result+kolmo("P3 100 100 ")


This was quite fun and I hope to improve my answer

• You legitimately golfed away 160088 bytes? Is that a record?
– Neil
Jan 14 '17 at 10:49
• Maybe some unary answers can beat that Jan 14 '17 at 14:34
• Jan 14 '17 at 15:04
• Your Python code intrigues me. What's value? (Not this module on PyPI, I'm guessing?) What's kolmo? Jan 14 '17 at 17:46
• @TimPederick Sorry about that. Thats a module a wrote myself for golfing Brain-Flak. I will include a link in the body. Jan 14 '17 at 17:49

# HTML + CSS 3D (855 866 bytes)

HTML 117 bytes + CSS 738 bytes

To keep the z-indexes in order was a bit tricky. ;)

/* CSS */
p{position:absolute;left:110px;top:0;width:50px;height:50px;transform-style:preserve-3d;transform:rotateX(-45deg)rotateY(21deg)rotateZ(20deg)}
p+p{left:140px;top:50px}
p+p+p{left:170px;top:100px}
p+p+p+p{left:200px;top:150px}
p+p+p+p+p{left:140px;top:150px}
p+p+p+p+p+p{left:80px;top:150px}
p+p+p+p+p+p+p{left:20px;top:150px}
p:nth-child(8){z-index:1;left:50px;top:100px}
p:nth-child(9){z-index:-1;left:80px;top:50px}
p:nth-child(10){z-index:1;left:67px;top:59px;transform:rotateX(-45deg)rotateY(21deg)rotateZ(20deg)scale(0.6)}
a{position:absolute;width:50px;height:50px;background:red;transform:rotateY(0deg)translateZ(25px)}
a+a{background:tan;transform:rotateY(-90deg)translateZ(25px)}
a+a+a{background:navy;transform:rotateX(90deg)translateZ(25px
<!-- HTML -->
<p><a><a><a><p><a><a><a><p><a><a><a><p><a><a><a><p><a><a><a><p><a><a><a><p><a><a><a><p><a><a><a><p><a><a><a><p><a><a>

I've kept the new lines for better readability. Maybe somebody spots potential for more golfing. However, they are not included in the byte count.

## jsFiddle Demo

Try it yourself

Use Goole Chrome. Other browsers may have problems with the z-indexes.

## Edit

• Saved 2 bytes by removing the duplicate a-selector, thanks to ETHproductions.
• Saved 9 bytes by removing an unnecessary margin:0 on the a-element.
• Is it still valid HTML without HEAD/BODY and closing tags? I know browsers tend to ignore syntax errors and try to display whatever you throw at them, but I don't think this code follows the specs. (That said, great solution!) Jan 15 '17 at 8:59
• @FedericoPoloni Thank you. Regarding the html|head|body-elements: They can be omitted. Take a look at "Optional Tags", you'll be surprised how many elements and closing tags can be omitted as well. What is not in the specs but works too is to omit the closing tags of the a-elements. They are however closed correctly by the browser, because you can't nest them. Also from the Code Golf point of view: Everything that "compiles" and has the correct output is valid. ;) Jan 15 '17 at 15:03

# BBC BASIC, 147 bytes

tokenised filesize 129 bytes

t=PI/1.5x=500y=x:FORi=0TO28b=i MOD3GCOL0,b:b*=t:a=i DIV9*t:IFb=0x-=99*COSa:y-=99*SINa
MOVEx,y:VDU25;66*COSb;66*SINb;28953;66*COS(b-t);66*SIN(b-t);:NEXT


2 bytes saved by using an absolute coordinate specification (MOVE) and two relative specifications per parallelogram, instead of changing the origin in order to be able to use all absolute specifications. 1 byte of unnecessary whitespace eliminated.

# BBC BASIC, 150 bytes

tokenised filesize 127 bytes

t=PI/1.5x=500y=x:F.i=0TO28b=i MOD3GCOL0,b:b*=t:a=i DIV9*t:IFb=0 x-=99*COSa:y-=99*SINa:ORIGINx,y
L.66*COSb,66*SINb,0,0PLOT117,66*COS(b-t),66*SIN(b-t)N.


Explanation

We start with the coordinates at top right and plot rhombuses in groups of 3. Before each group of 3 we move the origin (West, West, West, SE, SE SE, NE, NE NE.) That means that the group of 3 at top right is the last complete group to be plotted, bringing the origin back to its original location. We then continue and plot the black and red (but not the green) of the first group again, a total of 29 rhombuses.

Ungolfed

  t=PI/1.5                                 :REM 120 deg
x=500                                    :REM coordinates of top right corner
y=x
FORi=0TO28
b=i MOD3:GCOL0,b                       :REM set colour 0=black,1=red,2=green
b*=t                                   :REM convert b to a multiple of 120deg
a=i DIV9*t
IFb=0 x-=99*COSa:y-=99*SINa:ORIGINx,y  :REM before each group of 3 rhombs move the graphics origin
LINE66*COSb,66*SINb,0,0                :REM define one side of a rhombus
PLOT117,66*COS(b-t),66*SIN(b-t)        :REM define one further corner and plot the rhombus
NEXT


Output

# HTML + JavaScript (ES6), 351 374 384

<canvas id=C></canvas><script>c=C.getContext("2d");133124222162184253104213162164244191224182133191064104222093164253122224284151284.match(/.../g).map((v,i)=>(c.fillStyle=['#fc0','#f04','#08a'][a=i%3],c.beginPath(),c[l='lineTo'](x=5*~~v/10,y=v%10*25),c[l](x-10,y+(--a+!a)*17),a&&c[l](x-30,y+a*17),c[l](x-20,y),!a&&c[l](x-10,y-17),c.fill()))</script>


Less golfed

<canvas id=C></canvas>
<script>
c=C.getContext("2d");
[133,124,222,162,184,253,104,213,162,164,244,191,224,182,133,191,64,104,222,93,164,253,122,224,284,151,284]
.map((v,i)=>(
a = i % 3,
x = 5 * ~~ v / 10,
y = v % 10 * 25,
c.fillStyle = ['#fc0','#f04','#0a8'][a],
c.beginPath(),
--a,
c[l='lineTo'](x, y),
c[l]( x -10, y + (a+!a) * 17),
a&&c[l](x - 30, y + a * 17),
c[l](x - 20, y),
!a&&c[l](x - 10, y - 17),
c.fill()
))
</script>


Test

<canvas id=C></canvas><script>c=C.getContext("2d");133124222162184253104213162164244191224182133191064104222093164253122224284151284.match(/.../g).map((v,i)=>(c.fillStyle=['#fc0','#f04','#08a'][a=i%3],c.beginPath(),c[l='lineTo'](x=5*~~v/10,y=v%10*25),c[l](x-10,y+(--a+!a)*17),a&&c[l](x-30,y+a*17),c[l](x-20,y),!a&&c[l](x-10,y-17),c.fill()))</script>

## JavaScript(ES6)/SVG(HTML5), 350 312 bytes

document.write(<svg width=390 height=338>)
a=195,52;240,130;285,208;330,286;240,286;150,286;60,286;105,208;150,130;
a=(a+a).split;
for(i=9;i--;)document.write(<path fill=#FD0 d=M${a[i]}h60l-30,-52h-60z /><path fill=#088 d=M${a[i+3]}h60l-30,52h-60z /><path fill=#F64 d=M\${a[i+6]}l-30,-52l-30,52l30,52z />)

# SVG, 562540520504487 473 bytes

This is my first time golfing SVG (or any markup, in fact); be gentle!

The assumed viewing environment is an SVG-capable web browser with anything like a typical window size. I tested it in Firefox 50 and in Chrome 55.

The viewBox is necessary to meet the 100-pixel requirement; blowing up all measurements by a suitable factor would also work but would take more bytes. Incidentally, it is possible to save another byte by removing the space in 0 -5 in the viewBox value, but Firefox won't accept this as valid (whereas Chrome will).

The aspect ratio is 1:1 instead of the true 0.866:1. I'm not sure exactly how the "factor of 2" rule is meant to be interpreted (I think it means that exaggeration as extreme as 0.433:1 or 1.732:1 is acceptable), but I'm pretty sure this meets the requirement anyway.

## SVG

<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 -5 26 26"><g
id="a"><path d="m7,9H3V5h6z"/><g
id="f"><path fill="red" d="m9,5H3V1h4z"/><path
fill="blue" d="m3,1l2,4L3,9l-2-4z"/></g></g><use
l:href="#a" x="3" y="6"/><use
l:href="#e" x="12"/><g
id="e"><use l:href="#a" x="-6" y="12"/><use l:href="#a" x="-12" y="12"/></g><use
l:href="#a" x="-9" y="6"/><use
l:href="#a" x="-6"/><use
l:href="#a" x="-3" y="-6"/><use
l:href="#f"/></svg>


## Result

• I think you could probably delete the newlines here, couldn't you? IIRC, XML disregards whitespace in most contexts.
– user62131
Jan 14 '17 at 15:49
• @ais523: Yeah, I forgot to do that before posting it. facepalm It's done now, though. There's still newlines, keeping it vaguely readable, but only in places (namely between tag names and attributes) where a space of some sort is needed anyway. Jan 14 '17 at 15:52
• Your interpretation of the aspect ratio rule is correct Jan 15 '17 at 3:12

# Python 3 + Turtle, 389 bytes

from turtle import*
L=lt;F=fd;B=begin_fill;E=end_fill
Z=0,1,2
pu()
ht()
speed(0)
for m in Z:L(120);F(30);L(60);F(30);L(60);[[[[color(a[j-m]),B(),[F(20)==L((5-i%2)*60)for i in(1,)+Z],E(),L(120)]for j in Z],k%2and[L(180),F(30),L(180)]or[L(300),F(30),L(60)]]for k in Z]
L(120)
F(30)
L(60)
F(10)
for k in[2,1]:color(a[k]);B();[F(20)==L(240)for i in Z];E();L(60)


From Bubbler's golf of -131 bytes! Try it online!

# Python 3 + Turtle, 537 520 bytes

from turtle import*
pu()
ht()
speed(0)
for m in range(3):
lt(120);fd(30);lt(60);fd(30);lt(60)
for k in range(3):
for j in range(3):
color(a[j]);begin_fill()
for i in range(4):fd(20);rt(120 if i%2==0 else 60)
end_fill();lt(120)
if k==1:rt(180);fd(30);lt(180)
else:rt(60);fd(30);lt(60)
a=a[2:]+a[:2]
lt(120)
fd(30)
lt(60)
fd(10)
a=[a[2],a[1]]
for k in range(2):
color(a[k]);begin_fill()
for i in range(3):fd(20);rt(120)
end_fill();lt(60)
turtle.Screen().exitonclick()


-17 bytes from Dion, after removing unnecessary whitespace.

Try it online!