knossos/maze/algorithms/
recursive_backtracking.rs

1use super::Algorithm;
2use crate::maze::grid::{Grid, cell::Cell};
3use crate::utils::types::Coords;
4
5use rand::prelude::*;
6
7/// The "Recursive Backtracking" algorithm for generating mazes
8///
9/// This algorithm quite effectively creates narrow passages with multiple dead-ends which makes it
10/// easy to get lost, thus eventually making it hard to solve the maze.
11///
12/// In most cases, this algorithm is fast. However, due to its recursive nature, it requires stack
13/// space proportional to the longest acyclic path, which is, in the worst case, the entire maze. So
14/// for exceptionally large mazes this algorithm can be fairly inefficient.
15pub struct RecursiveBacktracking;
16
17/// An implementation of the "Recursive Backtracking" algorithm for generating mazes
18///
19/// Here is how it works:
20///
21/// 1. Chooses a starting point in the field.
22///
23/// 2. Randomly chooses a wall at that point and carves a passage through to the adjacent cell,
24///    but only if the adjacent cell has not been visited yet. This becomes the new current cell.
25///
26/// 3. If all adjacent cells have been visited, backs up to the last cell that has uncarved walls
27///    and repeats.
28///
29/// 4. The algorithm ends when the process has backed all the way up to the starting
30///    point.
31impl Algorithm for RecursiveBacktracking {
32    fn generate(&mut self, grid: &mut Grid, rng: &mut StdRng) {
33        let start_coords = (0, 0);
34        carve_passages_from(start_coords, grid, rng);
35    }
36}
37
38fn carve_passages_from<R: Rng>(coords: Coords, grid: &mut Grid, rng: &mut R) {
39    let mut dirs = [Cell::NORTH, Cell::SOUTH, Cell::WEST, Cell::EAST];
40    dirs.shuffle(rng);
41
42    for dir in dirs {
43        let next = match grid.get_next_cell_coords(coords, dir) {
44            Ok(next) => next,
45            Err(_) => continue,
46        };
47
48        if grid.is_cell_visited(next) {
49            continue;
50        }
51
52        if let Ok(next) = grid.carve_passage(coords, dir) {
53            carve_passages_from(next, grid, rng);
54        }
55    }
56}