A honeycomb with every wall intact, tunnelled through by a walk that never revisits a cell.
Start with everything closed
A maze generator does not build walls. It removes them. The grid begins as a
honeycomb in which every hexagon is closed on all six sides — no corridors, no
openings, nothing connected to anything.
Two and a half thousand closed cells. Adjacent hexagons share a wall, and the wall
is stored twice: each cell has a six-bit field, one bit per side, and knocking a
wall down means clearing a bit in both cells at once.
Grid[r, c] &= ~chosen.Item2.wall;
Grid[nr, nc] &= ~chosen.Item2.opposite;
The walk
var (r, c) = stack.Peek();
// collect unvisited neighbours
if (neighbors.Count > 0)
{
var chosen = neighbors[rand.Next(neighbors.Count)];
// knock down the shared wall, mark visited, push
}
else stack.Pop();
Stand on a cell. If any neighbour has not been visited, pick one at random, break
the wall between them, step across, and remember where you came from. If every
neighbour has been visited, step back to where you came from and try again from
there.
That is recursive backtracking, and it produces a spanning tree: every cell
reachable, no cell reachable twice, and therefore exactly one route between any two
cells. There are no loops anywhere in the finished maze, and no cell walled off.
The first tunnel. A depth-first walk does not spread out — it commits, running in one
direction until it is boxed in by cells it has already visited. This long initial
corridor is characteristic of the algorithm and is why these mazes feel like caves
rather than like grids.
Hexagons have six neighbours, and two kinds of row
On a square grid, a cell’s neighbours are the same four offsets everywhere. On a
staggered hex grid they are not: odd rows are shifted half a cell to the right, so
the diagonal neighbours of an even-row cell sit at different column offsets than
those of an odd-row cell.
var dirs = r % 2 == 0 ? evenDirs : oddDirs;
Two tables, six directions each, and each direction carries both the wall bit it
would break and the opposite bit in the neighbour. That pairing is what keeps the
two copies of every shared wall in agreement.
The walk has hit a dead end and reversed. It is now branching from cells it passed
through earlier, which is the second half of the algorithm’s character — the trunk
gets its branches on the way back, not on the way out.
Two thirds of the way. What remains are pockets that the first pass ran around
rather than into.
Complete. The stack has emptied, which means every cell has been visited and every
cell has been popped. The maze is a tree with 2,500 nodes and 2,499 corridors.
Every cell is drawn, always
The drawing loop has no idea a maze happened. It walks all rows and columns, builds
the six vertices of each hexagon, and draws whichever of the six edges still has its
bit set:
if ((cell & 2) != 0) canvas.DrawLine(vertices[0], vertices[1], paint);
if ((cell & 1) != 0) canvas.DrawLine(vertices[1], vertices[2], paint);
…
So shared walls are drawn twice, exactly on top of each other, and the maze is a
picture of a bit field rather than a picture of a path. Nothing traces a corridor;
corridors are what is left where two adjacent bits were cleared together.
Seven variants
| Rendering | Grid | Cell | Stroke | Seed | Ink |
|---|
| Cyber Cyan | 50×50 | 30 | 2px | 42 | #00ffff on black |
| Hot Pink | 40×40 | 35 | 1.5px | 137 | #ff1493 on #0a0a0a |
| Volt Gold | 60×60 | 25 | 2.5px | 271 | #ffd700 on #111111 |
| Ultra Violet | 20×20 | 68 | 5px | 17 | #8800ff on #04000c |
| Ash | 88×88 | 17 | 0.8px | 1701 | #afafaf on #0e0e0e |
| Scarlet | 48×48 | 29 | 2px | 509 | #ff3300 on #0d0100 |
| Museum | 50×50 | 30 | 3.5px | 42 | #00ffff on cream mat |
Grid size and cell size span the full density range. Ultra Violet is the coarsest:
twenty cells per side at 68px each, a few dozen corridors visible at once, drawn with
strokes that read as bold lines. Ash inverts that: eighty-eight cells per side at
17px each, the visible area packed with hairline walls that read as woven texture.
Each generative variant has its own seed, so the six are independent mazes rather than
one maze in six colours.
Museum presentation
The museum variant presents the lead artwork — cyber-cyan’s 50×50 maze — on a warm
cream mat with a 12% border on each side and a thin rule at the artwork boundary.
Where the other variants are full-bleed crops (the maze overfills the canvas and is
cropped symmetrically), the museum version scales the complete maze to fit within the
mat, so every corridor is visible.
The framing layer is separate from the generative design: Museum = true switches the
drawing path without changing the maze itself. The same seed produces the same maze;
only the presentation differs.
Determinism
One Random is created per generation, seeded from the preset, and consumed once per
step to choose among the available neighbours. The neighbour list is built in a fixed
direction order, so the same seed produces the same maze. The generator is
System.Random, which the project’s determinism policy flags as not guaranteed
stable across .NET versions.