independent project
Recursive Backtracking Maze Generator
An early Java terminal game that generates perfect mazes by randomized recursive backtracking, expanded into a browser laboratory for comparing spanning-tree algorithms.
Interactive algorithm laboratory
Maze generation visualizer
Animate a deterministic perfect-maze algorithm. The finished maze is a spanning tree: connected, with exactly one path between any two cells.
Tracking legend
- Pale cell - the previous cell in this event (soft red when a wall is placed).
- Connecting line - the edge just carved, visited, or walled in this event.
- Solid / outlined dot - the current cell. Amber for traversal; red when recursive division adds a wall.
0 / 190 events
Long corridors, few dead ends; a fast depth-first carve. Reduced-motion preferences skip animation and jump to the finished maze.
Basic explanation
A perfect maze is a grid in which every cell is reachable and there is exactly one simple path between any pair of cells. Graph-theoretically that means the passages form a spanning tree of the cell graph: edges on vertices, connected and acyclic.
The original project grew out of a Java ASCII game suite. The generator builds an odd-sized wall grid, carves passages by randomized recursive backtracking (depth-first search with a stack), then lets the player move 0 toward X. The browser work below reconstructs that generator and places it beside other classical spanning-tree maze algorithms so structural bias can be measured, not merely eyeballed.
Interactive laboratory
Use the visualizer to animate carving on a fixed seed, step event-by-event, and read the tracking legend: the pale cell is the previous position, the line is the edge being processed, and the outlined dot is the current cell.
Spanning trees and generation
View cells as vertices of a grid graph with edges to orthogonal neighbours. A perfect maze selects a subset of those edges forming a tree. Different algorithms correspond to different random processes on that graph:
- Recursive backtracking - randomized DFS. Follow unvisited neighbours, carve, and push history; at a dead end, pop until a branch remains. Typically produces long corridors and few junctions. Work is essentially linear in the number of cells once adjacency is fixed.
- Randomized Prim - grow from a frontier of walls bordering the maze. Repeatedly open a random frontier edge into an unvisited cell. Tends toward short branches and many dead ends because growth hugs the existing region.
- Kruskal - shuffle potential walls and join cells with a disjoint-set structure whenever they lie in different components. Growth is delocalized across the grid; the result is an unbiased random spanning tree among Kruskal’s sampling measure.
- Recursive division - start from open space and insert long walls with single gaps. Creates room-like blocks and strong axis-aligned texture; operation count tracks recursive chamber splits.
- Wilson’s algorithm - loop-erased random walks from unvisited cells until they hit the tree. Samples spanning trees uniformly (among all spanning trees), at the cost of discarded loops.
- Aldous-Broder - a simple random walk that adds an edge the first time it enters a new cell. Also uniform over spanning trees in the limit, but can revisit cells many times; operation counts are often highest here.
Uniformity matters theoretically: Wilson and Aldous-Broder target the uniform measure on spanning trees, while DFS and Prim induce visibly biased ensembles. The comparison panel later on this page reports dead ends, junctions, mean straight-run length, diameter, and operation counts on a shared seed - metrics that survive browser timing noise.
From the original Java game
The terminal demo preserves the early playable loop: odd dimensions, ASCII walls, move counting, and high scores. Source for Maze, Game, and Main remains downloadable above.
Original Java experience
ASCII maze game
Play the odd-grid terminal draft: generate a perfect maze, then guide 0 to X with W, A, S, and D.
Original Java game reconstruction
Play the terminal maze
+-+-+-+-+-+-+-+-+-+-+ | | | | | + + + + + + +-+-+ +-+ | | | | | |0 | X| + +-+ + + + + + +-+ + | | | | | | | +-+ +-+-+-+-+-+-+ + + | | | | | | + +-+-+-+ +-+ +-+ + + | | | | | + +-+ +-+-+ +-+ +-+ + | | | | | | | +-+ + + + +-+ + + + + | | | | | | +-+-+-+-+-+-+-+-+-+-+
Focus terminal · W/A/S/D to move 0 toward X
Comparing algorithmic bias in detail
Hold width, height, and seed fixed. Diameter is exact on a tree (longest shortest path). Operation counts expose algorithmic work - frontier checks, union-find probes, or random-walk steps - rather than animation speed. Read the notes under each card against the theory above: long corridors versus bushy dead ends, localized versus global growth, biased versus (asymptotically) uniform spanning trees.
Structural bias laboratory
Algorithm comparison
Every algorithm below builds a perfect maze (a spanning tree), but dead ends, junctions, corridor length, and diameter expose different sampling biases. Fix width, height, and seed to compare fairly.
Same 12 × 8 grid and seed for every algorithm. Diameter is the longest shortest path on the tree; operations count edge checks or random-walk steps inside the generator - not browser frame time.
Recursive backtracker
- Dead ends
- 13
- Junctions
- 11
- Avg straight run
- 1.61
- Diameter
- 61
- Operations
- 191
Long corridors, few dead ends; a fast depth-first carve.
Randomized Prim
- Dead ends
- 32
- Junctions
- 24
- Avg straight run
- 1.94
- Diameter
- 22
- Operations
- 172
Bushier growth with many short branches and dead ends.
Kruskal
- Dead ends
- 31
- Junctions
- 24
- Avg straight run
- 1.73
- Diameter
- 21
- Operations
- 121
Evenly distributed passages; edge sorting adds bookkeeping.
Recursive division
- Dead ends
- 22
- Junctions
- 20
- Avg straight run
- 1.79
- Diameter
- 34
- Operations
- 159
Strong wall-aligned structure and long straight boundaries.
Wilson
- Dead ends
- 31
- Junctions
- 26
- Avg straight run
- 2.02
- Diameter
- 33
- Operations
- 311
Unbiased spanning trees; loop erasure costs extra operations.
Aldous-Broder
- Dead ends
- 31
- Junctions
- 28
- Avg straight run
- 1.86
- Diameter
- 35
- Operations
- 1,725
Unbiased but inefficient: random walks revisit many cells.
Engineering notes
The Java version validates odd sizes and shares a small Game lifecycle with the wider ASCII suite. The TypeScript laboratory factors generation into pure functions returning event traces, so animation is a replay of discrete edge operations rather than a live mutation of a single opaque grid. That separation is what makes fair side-by-side comparison possible.