maze generation
algorithms
computer science
procedural generation
coding techniques

What's a good algorithm to generate a maze?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

A good maze-generation algorithm depends on what kind of maze you want. If you want a classic "perfect maze" with exactly one path between any two cells, the recursive backtracker, which is depth-first search with randomized neighbor selection, is one of the simplest and most effective choices.

What Makes A Maze "Good"

Before choosing an algorithm, decide which properties matter:

  • one unique path between any two cells
  • long corridors versus many short branches
  • speed of generation
  • ease of implementation

Many game and puzzle projects want a perfect maze because it is easy to solve and has no isolated regions. The recursive backtracker produces that kind of maze with compact code and visually pleasing results.

The Recursive Backtracker Approach

Treat the maze as a grid of cells with walls between neighboring cells. Start from one cell, mark it visited, choose a random unvisited neighbor, remove the wall between them, and continue. When you reach a dead end, backtrack until another unvisited neighbor exists.

That is depth-first search with randomness added to the neighbor choice.

A Runnable Python Example

python
1import random
2
3
4def generate_maze(width: int, height: int):
5    visited = [[False] * width for _ in range(height)]
6    walls = {
7        (x, y): {"N": True, "S": True, "E": True, "W": True}
8        for y in range(height)
9        for x in range(width)
10    }
11
12    directions = {
13        "N": (0, -1, "S"),
14        "S": (0, 1, "N"),
15        "E": (1, 0, "W"),
16        "W": (-1, 0, "E"),
17    }
18
19    stack = [(0, 0)]
20    visited[0][0] = True
21
22    while stack:
23        x, y = stack[-1]
24        neighbors = []
25
26        for name, (dx, dy, opposite) in directions.items():
27            nx, ny = x + dx, y + dy
28            if 0 <= nx < width and 0 <= ny < height and not visited[ny][nx]:
29                neighbors.append((name, nx, ny, opposite))
30
31        if not neighbors:
32            stack.pop()
33            continue
34
35        direction, nx, ny, opposite = random.choice(neighbors)
36        walls[(x, y)][direction] = False
37        walls[(nx, ny)][opposite] = False
38        visited[ny][nx] = True
39        stack.append((nx, ny))
40
41    return walls
42
43
44maze = generate_maze(4, 4)
45print(maze[(0, 0)])

This code builds the wall structure. Rendering it as ASCII or tiles is a separate step.

The recursive backtracker is popular because it has a strong balance of simplicity and output quality. It tends to create long winding corridors with occasional deep branches, which feels maze-like to people even without any post-processing.

It also runs in linear time relative to the number of cells because each cell is visited once.

Iterative Version Versus Recursion

The name "recursive backtracker" comes from the recursive presentation, but an explicit stack is often better in real code because it avoids recursion-depth limits.

The example above uses a stack for exactly that reason. It behaves like the recursive version but is safer for larger mazes.

Other Good Algorithms

The recursive backtracker is not the only valid answer.

Prim-style randomized maze generation tends to produce more branching. Kruskal-style generation is also useful when you think naturally in terms of sets and walls. Wilson's algorithm produces uniform spanning trees, which is mathematically attractive but usually more complex than needed for a first implementation.

If you are asking for a "good" algorithm rather than the theoretically most uniform one, recursive backtracking is often the best starting point.

Rendering The Maze

The algorithm above stores which walls remain around each cell. A renderer can then translate those walls into text, images, or game tiles. Keeping generation and rendering separate is a good design choice because it lets you reuse the generator across CLI tools, web apps, and games.

Common Pitfalls

A common mistake is forgetting to mark cells as visited before exploring neighbors, which can produce loops or repeated work. Another is removing only one side of a wall and leaving the opposite side intact, which breaks the maze representation. Developers also sometimes use recursive implementations on large grids and then hit recursion limits. Finally, it helps to decide early whether you want a perfect maze; if you later want loops or rooms, you will need extra post-processing beyond the basic algorithm.

Summary

  • A recursive backtracker is a strong default algorithm for perfect maze generation.
  • It is randomized depth-first search with wall removal and backtracking.
  • An explicit stack is usually safer than deep recursion in production code.
  • The algorithm is fast, simple, and produces long corridor-heavy mazes.
  • Other algorithms exist, but this one is often the best first implementation.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.