Books · Solving Puzzles through Mathematical Programming
Chapter 4
The Bedlam Cube
The Bedlam Cube is a polycube dissection. Thirteen pieces — twelve pentacubes and one tetracube, totalling unit cubes — must be assembled into a solid cube of side four. The piece set was chosen so that no piece repeats (as with the related Soma Cube), and most of the pieces have little symmetry: of the thirteen, seven have the full twenty-four rotational orientations, five (including the lone tetracube) have twelve, and only the cross-shaped piece, with its fourfold symmetry, has as few as three. The resulting puzzle is far harder than its flat relatives: not because the pieces are subtle, but because the cell count is large, each piece has up to twenty-four rotational orientations, and the interactions between adjacent pieces propagate along three axes at once.
The cube has distinct solutions, a count first published by Scott Kurowski. For any given tiling, the thirteen pieces can be identified by their canonical letter names (Little Corner, Spikey Zag, Pokey Corner Bit, Right Angles Everywhere, Middle Zig, Pokey Z, Corner Joist, Dented L, F in Therapy, Flat M, Cross, Confused F, T-Bone) and located in the cube by their occupied coordinates. None of the pieces is a repeat of another, so pairs of the form “which copy of piece ” do not arise; the twelve pentacubes are twelve of the twenty-nine distinct pentacubes, and the one tetracube is one of the eight distinct tetracubes.
Exact cover is again the natural formulation. The cube has cells, each piece must be placed exactly once, and every cell must be covered exactly once. What distinguishes this chapter from the two that preceded it is simply the move to three dimensions: the orientation group has elements rather than , and placements are enumerated over translations in three axes rather than two.
The pieces
Each of the thirteen pieces is a connected polycube in the cubic lattice. The canonical coordinates, taken from Scott Kurowski’s enumeration, are:
| Piece | Name | Coordinates |
|---|---|---|
| A | Little Corner | |
| B | Spikey Zag | |
| C | Pokey Corner Bit | |
| D | Right Angles Everywhere | |
| E | Middle Zig | |
| F | Pokey Z | |
| G | Corner Joist | |
| H | Dented L | |
| I | F in Therapy | |
| J | Flat M | |
| K | Cross | |
| L | Confused F | |
| M | T-Bone |
Piece A is the one tetracube; B through M are the twelve pentacubes. Three of the pentacubes are planar (they fit into a single layer: pieces J, K, and L are planar; B, C, D, E, F, G, H, I, M all have cells at two different -coordinates and so are genuinely three-dimensional).
The programming model
Orientation group
In three dimensions, the rotation group of the cube has elements. Given a piece’s canonical coordinate list, enumerate its distinct orientations by applying the group generators and de-duplicating; this collapses to fewer than orientations for pieces with rotational symmetry, but most Bedlam pieces produce the full count. Reflections are not included: the Bedlam Cube is the chiral puzzle, and adding mirror images would introduce extra “pieces” that do not exist in the physical set.
Placements
For each piece and each orientation , enumerate every translation such that all cells of the translated orientation lie inside the cube. Each surviving (piece, orientation, translation) is a placement.
Placement variables
For each placement of piece , introduce a Boolean variable with meaning “this placement is selected.”
Constraints
Each piece is placed exactly once:
Each of the cells is covered exactly once:
A solver in eighty lines
from ortools.sat.python import cp_model
PIECES = {
"A": [(0,0,0),(0,0,1),(1,0,0),(1,1,0)],
"B": [(0,0,1),(0,1,0),(0,1,1),(1,1,0),(1,2,0)],
"C": [(0,0,0),(0,1,0),(1,1,0),(0,1,1),(0,2,0)],
"D": [(0,0,0),(0,0,1),(0,1,0),(0,2,0),(1,2,0)],
"E": [(0,0,0),(0,1,0),(0,1,1),(1,1,0),(1,2,0)],
"F": [(0,0,0),(0,0,1),(0,1,0),(1,1,0),(1,2,0)],
"G": [(0,0,0),(1,0,0),(0,0,1),(0,1,0),(0,2,0)],
"H": [(0,0,0),(1,0,0),(1,0,1),(0,1,0),(0,2,0)],
"I": [(0,0,0),(0,0,1),(0,1,0),(1,1,0),(0,2,0)],
"J": [(0,1,0),(1,0,0),(2,0,0),(1,1,0),(0,2,0)],
"K": [(0,1,0),(1,0,0),(1,1,0),(2,1,0),(1,2,0)],
"L": [(0,0,0),(1,0,0),(1,1,0),(2,1,0),(1,2,0)],
"M": [(0,1,0),(1,0,0),(0,1,1),(1,1,0),(1,2,0)],
}
N = 4
def norm(p):
mx = min(x for x,_,_ in p)
my = min(y for _,y,_ in p)
mz = min(z for _,_,z in p)
return tuple(sorted((x-mx, y-my, z-mz)
for x,y,z in p))
def rotX(p): return [(x,-z,y) for x,y,z in p]
def rotY(p): return [(z,y,-x) for x,y,z in p]
def rotZ(p): return [(-y,x,z) for x,y,z in p]
def orients(p):
pool = {tuple(norm(p))}
frontier = set(pool)
while frontier:
nxt = set()
for q in frontier:
for g in (rotX, rotY, rotZ):
r = tuple(norm(g(list(q))))
if r not in pool:
pool.add(r); nxt.add(r)
frontier = nxt
return [list(o) for o in pool]
def solve():
m = cp_model.CpModel()
pls = []
for name, shape in PIECES.items():
seen = set()
for o in orients(shape):
mx = max(x for x,_,_ in o)
my = max(y for _,y,_ in o)
mz = max(z for _,_,z in o)
for dx in range(N - mx):
for dy in range(N - my):
for dz in range(N - mz):
cells = frozenset(
(x+dx, y+dy, z+dz)
for x,y,z in o)
if cells in seen: continue
seen.add(cells)
pls.append((name, cells))
v = [m.NewBoolVar("") for _ in pls]
by = {}
for i,(n,_) in enumerate(pls):
by.setdefault(n, []).append(i)
for n, idxs in by.items():
m.Add(sum(v[i] for i in idxs) == 1)
cov = {}
for i,(_,cs) in enumerate(pls):
for c in cs:
cov.setdefault(c, []).append(i)
for idxs in cov.values():
m.Add(sum(v[i] for i in idxs) == 1)
s = cp_model.CpSolver()
s.Solve(m)
return [pls[i] for i,x in enumerate(v)
if s.Value(x)]
The enumeration produces about placements; CP-SAT returns a single solution in just over one second.
Reading the solution
The solution is displayed three ways. Figure 4.1 shows four axonometric views of the whole cube, obtained by rotating the cube through , and about its vertical axis: the four views together expose all six outer faces. Figure 4.2 shows each of the thirteen pieces individually in the same coordinate frame, with the outer cube rendered as a thin wireframe and only the focus piece drawn in full colour; this makes the spatial position of every piece explicit, including the pieces buried in the interior. Figure 4.3 gives the information-complete view: four -slices from (bottom) to (top), each cell labelled by its piece letter.
Sources. The Bedlam Cube was invented by the British designer Bruce Bedlam, who developed it in the early s; it reached wide commercial release in . It is a larger, non-repeating cousin of Piet Hein’s Soma Cube (). The solution count , counting tilings up to the -fold rotational symmetry of the outer cube, is due to Scott Kurowski, whose enumeration was the first complete census; because the piece set is chiral, reflecting a solved cube never produces another packing of the same thirteen pieces, so rotations alone generate the symmetry classes and the rotations-only census is the natural one. The solver of the preceding section recovers a single tiling by default. The CP model does not break the outer cube’s rotational symmetry, so setting and iterating over feasible models enumerates tilings; dividing by the rotations of the assembled cube recovers Kurowski’s count of . Chlond presents the same polycube-packing integer program, with the Bedlam Cube as its principal worked instance, in the INFORMS Transactions on Education Puzzles column (“Box-Packing Puzzles,” 5(3): 70–72, 2005).