How Mazes Work

An interactive guide to maze generation

Contents

  1. Mazes Are Graphs
  2. Building the Grid Graph
  3. Spanning Trees = Perfect Mazes
  4. Kruskal's Algorithm
  5. Finding the Solution
  6. Beyond Rectangles
  7. Try It Yourself
1

Mazes Are Graphs

A graph is a set of vertices (points) connected by edges (links). Every cell in a maze is a vertex, and every possible passage between cells is an edge.

A graph: 6 vertices connected by 7 edges

A 4×3 grid has 12 vertices, each connected to its neighbors. If every connection is open, there are no walls. A maze is what you get when you selectively block some of those connections. Blocked edges = walls. Open edges = passages.

2

Building the Grid Graph

Each cell gets a number: vertex = row * width + col. We store the graph as an adjacency list — for each vertex, a list of its neighbors and the wall segment (LineBorder) between them:

// Vertex 5 in a 4-wide grid:
adjacencylist[5] = [
  [1, LineBorder(1,1, 2,1)],  // up
  [9, LineBorder(1,2, 2,2)],  // down
  [4, LineBorder(1,1, 1,2)],  // left
  [6, LineBorder(2,1, 2,2)]   // right
]

Two neighboring cells share the same border object. Outer walls use vertex -1 to mean "outside," with gaps left for entry and exit.

3

Spanning Trees = Perfect Mazes

A spanning tree is a subset of edges that connects every vertex with no cycles. It always has exactly V − 1 edges. Since there are no cycles, there's exactly one path between any two vertices — which is exactly what makes a perfect maze.

The key equivalence
Random spanning tree of the grid graph = random perfect maze. Edges in the tree become passages; edges not in the tree become walls.

Click through to see the same 4×3 grid as a full graph, then its spanning tree, then the resulting maze:

 
4

Kruskal's Algorithm

To find a random spanning tree: shuffle all edges, then add each edge unless it would create a cycle. The cycle check uses Union-Find: each vertex starts in its own set; adding an edge merges two sets. If both vertices are already in the same set, skip it.

// Shuffle all edges, then:
for (edge of shuffledEdges) {
  rootA = find(edge[0])
  rootB = find(edge[1])
  if (rootA !== rootB) {     // different components
    parent[rootA] = rootB  // merge them
    tree.push(edge)         // keep this edge
  }
}

Watch below: each cell is colored by its connected component. When two components merge, their cells unify into one color and the wall between them disappears.

Components: 30
Cells colored by connected component — watch them merge
5

Finding the Solution

Since a spanning tree has exactly one path between any two vertices, we find it with DFS (depth-first search). Starting from the entrance, DFS records each vertex's parent. Then we trace backwards from the exit:

// After DFS fills parent[]:
path = []
u = exitVertex
while (parent[u] !== u) {
  path.push(u)
  u = parent[u]
}
// path = [exit, ..., start] reversed

To draw the solution, we connect the centers of adjacent cells along the path. Each maze type defines getCellCenter(vertex) — for a rectangle it's (col + 0.5, row + 0.5); for hexagons it's a trigonometric calculation in the sector's coordinate frame.

6

Beyond Rectangles

The generation algorithm only sees vertices and edges — it doesn't care about geometry. To make a hexagonal maze, we just define a different graph with different border shapes.

Our hexagonal maze divides a hexagon into 6 sectors, each containing triangular cells. Each sector is rotated 60° from the previous one:

6 triangular sectors, each rotated 60°

Cells are addressed as (sector, updown, row, col) and positioned using rotated basis vectors. But the algorithm is identical — Kruskal's doesn't know or care that these are triangles arranged in a hexagon.

Same algorithm, completely different geometry
7

Try It Yourself

The whole system: graph → spanning tree → remove walls → draw borders. The abstraction lets any geometry — rectangles, hexagons, circles — reuse the same algorithm by changing only how vertices and borders are defined.

Explore further

Try the full generator with animation to watch walls fall in real time, or play the game.