Late this July, have written a computer program which solves these, in a language called TXR Lisp. This is not packaged into anything nice with a GUI; it's just a function we can call from the interactive listener.
The code isn't graph-based; it isn't using graph representations to solve a Hamiltonian circuit. Rather it models the actual rotation and positioning of the pieces to form different foldings of the snake, and performs bounding box and shape checks to detect the solution or non-solutions.
Run the interpreter from the system prompt, loading the snake-cube.tl
file which is hosted in a git repository here:
$ txr -i snake-cube.tl
Then use the solve
function. Let's try an example in which we have a trivial snake: four straight pieces, solving for a 1x1x4 box:
1> (solve '(s s s s) 1 1 4)
(((s 0 0 0) (s 0 0 1) (s 0 0 2) (s 0 0 3)))
The input symbol s
is a shorthand denoting a straight piece: a cube whose next face is attached to the face opposite to the incoming face. The other type of piece is e
: elbow: the 90 degree bend.
The first and last cube of a snake don't attach to anything, and so it doesn't matter whether we specify them as s
or e
:
1> (solve '(s s s s) 1 1 4)
(((s 0 0 0) (s 0 0 1) (s 0 0 2) (s 0 0 3)))
Here, the program is telling us that starting with the straight piece at location <0, 0, 0>, facing upward (along the Z axis), there is exactly one solution for this trivial snake, which can only express a straight tetromino and nothing else. The numbers give the coordinates of the pieces in 3D space. The next piece is at <0, 0, 1>, and so we are stacking the cubes up the Z axis.
The output is a list of lists of (s ...)
and (e ...)
items. If there are multiple solutions, there are multiple lists.
Multiple solutions include symmetric solutions: rotations and reflections. If we have eight pieces, six of which are elbows, we can fold them into a 2x2x2 cube, and 24 ways are found of doing this:
3> (solve '(s e e e e e e e) 2 2 2)
(((s 0 0 0) (e 0 0 1) (e 0 1 1) (e 0 1 0) (e 1 1 0) (e 1 1 1) (e 1 0 1)
(e 1 0 0))
((s 0 0 0) (e 0 0 1) (e 0 1 1) (e 0 1 0) (e 1 1 0) (e 1 0 0) (e 1 0 1)
(e 1 1 1))
((s 0 0 0) (e 0 0 1) (e 0 1 1) (e 0 1 0) (e -1 1 0) (e -1 1 1)
(e -1 0 1) (e -1 0 0))
((s 0 0 0) (e 0 0 1) (e 0 1 1) (e 0 1 0) (e -1 1 0) (e -1 0 0)
(e -1 0 1) (e -1 1 1))
((s 0 0 0) (e 0 0 1) (e 0 1 1) (e 1 1 1) (e 1 0 1) (e 1 0 0) (e 1 1 0)
(e 0 1 0))
((s 0 0 0) (e 0 0 1) (e 0 1 1) (e -1 1 1) (e -1 0 1) (e -1 0 0)
(e -1 1 0) (e 0 1 0))
((s 0 0 0) (e 0 0 1) (e 0 -1 1) (e 0 -1 0) (e 1 -1 0) (e 1 -1 1)
(e 1 0 1) (e 1 0 0))
((s 0 0 0) (e 0 0 1) (e 0 -1 1) (e 0 -1 0) (e 1 -1 0) (e 1 0 0)
(e 1 0 1) (e 1 -1 1))
((s 0 0 0) (e 0 0 1) (e 0 -1 1) (e 0 -1 0) (e -1 -1 0) (e -1 -1 1)
(e -1 0 1) (e -1 0 0))
((s 0 0 0) (e 0 0 1) (e 0 -1 1) (e 0 -1 0) (e -1 -1 0) (e -1 0 0)
(e -1 0 1) (e -1 -1 1))
((s 0 0 0) (e 0 0 1) (e 0 -1 1) (e 1 -1 1) (e 1 0 1) (e 1 0 0)
(e 1 -1 0) (e 0 -1 0))
((s 0 0 0) (e 0 0 1) (e 0 -1 1) (e -1 -1 1) (e -1 0 1) (e -1 0 0)
(e -1 -1 0) (e 0 -1 0))
((s 0 0 0) (e 0 0 1) (e 1 0 1) (e 1 0 0) (e 1 1 0) (e 1 1 1) (e 0 1 1)
(e 0 1 0))
((s 0 0 0) (e 0 0 1) (e 1 0 1) (e 1 0 0) (e 1 1 0) (e 0 1 0) (e 0 1 1)
(e 1 1 1))
((s 0 0 0) (e 0 0 1) (e 1 0 1) (e 1 0 0) (e 1 -1 0) (e 1 -1 1)
(e 0 -1 1) (e 0 -1 0))
((s 0 0 0) (e 0 0 1) (e 1 0 1) (e 1 0 0) (e 1 -1 0) (e 0 -1 0)
(e 0 -1 1) (e 1 -1 1))
((s 0 0 0) (e 0 0 1) (e 1 0 1) (e 1 1 1) (e 0 1 1) (e 0 1 0) (e 1 1 0)
(e 1 0 0))
((s 0 0 0) (e 0 0 1) (e 1 0 1) (e 1 -1 1) (e 0 -1 1) (e 0 -1 0)
(e 1 -1 0) (e 1 0 0))
((s 0 0 0) (e 0 0 1) (e -1 0 1) (e -1 0 0) (e -1 1 0) (e -1 1 1)
(e 0 1 1) (e 0 1 0))
((s 0 0 0) (e 0 0 1) (e -1 0 1) (e -1 0 0) (e -1 1 0) (e 0 1 0)
(e 0 1 1) (e -1 1 1))
((s 0 0 0) (e 0 0 1) (e -1 0 1) (e -1 0 0) (e -1 -1 0) (e -1 -1 1)
(e 0 -1 1) (e 0 -1 0))
((s 0 0 0) (e 0 0 1) (e -1 0 1) (e -1 0 0) (e -1 -1 0) (e 0 -1 0)
(e 0 -1 1) (e -1 -1 1))
((s 0 0 0) (e 0 0 1) (e -1 0 1) (e -1 1 1) (e 0 1 1) (e 0 1 0)
(e -1 1 0) (e -1 0 0))
((s 0 0 0) (e 0 0 1) (e -1 0 1) (e -1 -1 1) (e 0 -1 1) (e 0 -1 0)
(e -1 -1 0) (e -1 0 0)))
And so here is the first of eight solutions for one 3x3 snake that I have:
5> (first (solve '(s s e s e s e s e e e e s e s e e e s e e s e e e s s) 3 3 3))
((s 0 0 0) (s 0 0 1) (e 0 0 2) (s 0 1 2) (e 0 2 2) (s 0 2 1) (e 0 2 0)
(s 1 2 0) (e 2 2 0) (e 2 2 1) (e 1 2 1) (e 1 2 2) (s 1 1 2) (e 1 0 2)
(s 1 0 1) (e 1 0 0) (e 2 0 0) (e 2 1 0) (s 1 1 0) (e 0 1 0) (e 0 1 1)
(s 1 1 1) (e 2 1 1) (e 2 0 1) (e 2 0 2) (s 2 1 2) (s 2 2 2))
Finally, let's try yours. From the image, I will try to transcribe the 64 pieces, starting with the bottom and going clockwise. Because this is slow, let us compile the code:
1> (compile-file "snake-cube")
t
The pprof
macro wrapped around the calculation will give us the execution time and memory allocation stats:
4> (pprof (solve '(s s e e s e e e s s e e s e e s e e s e e e e e e e e e s e s e e e e e e s e s s e e e e s s e e s e e e e e e e e e e s s e s) 4 4 4))
malloc bytes: 5303878935384
gc heap bytes: 7484828731840
total: 12788707667224
milliseconds: 49936965
(((s 0 0 0) (s 0 0 1) (e 0 0 2) (e 0 1 2) (s 0 1 1) (e 0 1 0) (e 0 2 0)
(e 0 2 1) (s 1 2 1) (s 2 2 1) (e 3 2 1) (e 3 2 0) (s 2 2 0) (e 1 2 0)
(e 1 1 0) (s 2 1 0) (e 3 1 0) (e 3 1 1) (s 2 1 1) (e 1 1 1) (e 1 1 2)
(e 1 2 2) (e 0 2 2) (e 0 2 3) (e 1 2 3) (e 1 1 3) (e 0 1 3) (e 0 0 3)
(s 1 0 3) (e 2 0 3) (s 2 1 3) (e 2 2 3) (e 2 2 2) (e 2 1 2) (e 3 1 2)
(e 3 2 2) (e 3 2 3) (s 3 1 3) (e 3 0 3) (s 3 0 2) (s 3 0 1) (e 3 0 0)
(e 2 0 0) (e 2 -1 0) (e 3 -1 0) (s 3 -1 1) (s 3 -1 2) (e 3 -1 3)
(e 2 -1 3) (s 2 -1 2) (e 2 -1 1) (e 2 0 1) (e 2 0 2) (e 1 0 2)
(e 1 -1 2) (e 1 -1 1) (e 1 0 1) (e 1 0 0) (e 1 -1 0) (e 0 -1 0)
(s 0 -1 1) (s 0 -1 2) (e 0 -1 3) (s 1 -1 3))
[ ... snip 31 more solutions ])
It took almost 14 hours, and burned through over 11 Tb of garbage-collected memory allocations. The VM footprint stayed below 29 Mb the entire time.
Here is that one of the other solutions which contains only non-negative XYZ coordinates:
((s 0 0 0) (s 0 0 1) (e 0 0 2) (e 1 0 2) (s 1 0 1) (e 1 0 0) (e 1 1 0)
(e 0 1 0) (s 0 1 1) (s 0 1 2) (e 0 1 3) (e 0 0 3) (s 1 0 3) (e 2 0 3)
(e 2 0 2) (s 2 1 2) (e 2 2 2) (e 2 2 1) (s 2 1 1) (e 2 0 1) (e 2 0 0)
(e 3 0 0) (e 3 0 1) (e 3 1 1) (e 3 1 2) (e 3 0 2) (e 3 0 3) (e 3 1 3)
(s 2 1 3) (e 1 1 3) (s 1 1 2) (e 1 1 1) (e 1 2 1) (e 1 2 0) (e 2 2 0)
(e 2 1 0) (e 3 1 0) (s 3 2 0) (e 3 3 0) (s 2 3 0) (s 1 3 0) (e 0 3 0)
(e 0 2 0) (e 0 2 1) (e 0 3 1) (s 1 3 1) (s 2 3 1) (e 3 3 1) (e 3 2 1)
(s 3 2 2) (e 3 2 3) (e 2 2 3) (e 2 3 3) (e 1 3 3) (e 1 2 3) (e 1 2 2)
(e 0 2 2) (e 0 2 3) (e 0 3 3) (e 0 3 2) (s 1 3 2) (s 2 3 2) (e 3 3 2)
(s 3 3 3))
Following these coordinates while twisting the cubes into place is still a bit of a mental challenge.