Status: I am making a major refactor of this library to be fully based on the Boost Graph Library, and have the base type be a maze over a graph (with the vertices dictating the cells and the edges the possible passages) instead of the current implementation, which assumes a grid. This will allow a much more diverse collection of mazes, such as with triangular cells, hexagonal cells, polar coordinates, masks, etc.
As I didn't want to clutter up spelunker until the refactor was complete, I am performing the refactor in another repository at the present time. See https://github.com/sraaphorst/spelunker_graphmaze.
spelunker
is a work-in-progress C++17 library for generating and solving different types of mazes:
Other subjects of interest:
- The library offers a number of typeclasses, such as
Show
andHomomorphism
. - Here are the requirements to compile
spelunker
. - Lists (possibly incomplete) of intended future work can be found here and, with more detail, in the issues section.
- References
If you have any interest in contributing to spelunker
, please contact me at [email protected].
A Maze
, in spelunker, is a grid of width w
and height h
of cells, where walls are represented as lines between cells.
In addition to Maze
, spelunker offers a GraphMaze
, which is a generalization of a Maze
.
-
maze: An internal representation where a cell
(x,y)
and a cardinal directiond
are mapped to walls, and those walls are either recorded to be present or absent. -
graphmaze: A purely graph-theoretic representation of mazes using the Boost.Graph library.
Here is an example of a width 50, height 40 maze generated by the randomized DFS algorithm:
Right now, the Maze
library can generate perfect mazes using the following 12 algorithms. Further details on any algorithm are available via the link. If you know of any other algorithms for grid mazes, please contact me at [email protected]
Furthermore, mazes can be used to construct unicursal mazes - more commonly known as labyrinths - which are mazes without branches, i.e. one long, twisting passageway.
Via a simple transformation of bisecting all passages, a w
by h
maze will generate a 2w
by 2h
unicursal maze.
Here is a 15 x 10 maze created with randomized depth-first search:
Here is its 30 x 20 unicursalization, which we get from bisecting the passages:
And here is a unicursalization of the unicursalization, resulting in a labyrinth of size 60 x 40:
A ThickMaze
is a maze where the walls are not simply cell dividers, but actually take up a cell themselves, i.e. each entry in the ThickMaze
is either FLOOR
or WALL
. Detailed notes about thick mazes and generation algorithms are available here.
Here is an example of a 50 by 40 thick maze:
ThickMaze
s can be generated using any of the standard Maze
generation algorithms through a homomorphism (see typeclasses below for more information), and additionally, the following algorithms can be used to uniquely generate ThickMaze
s:
Most of the algorithms in this library produce perfect mazes, i.e. mazes where given any two points in the maze, there is exactly one path between them (without retracing your steps). These mazes are mathematically interesting, because they are called trees, and are a well-studied area of graph theory, which is why there are many algorithms to construct them.
Perfect mazes, however, are generally easier to solve than non-perfect mazes, which are mazes with loops. A maze with no dead ends must, by definition, contain at least one loop (e.g. many unicursal mazes can be thought of as one long, winding loop).
A braid maze is a maze with no dead ends. For both Maze
s and ThickMaze
s, we have algorithms that can take in a maze and generate braid mazes, either by eliminating all dead ends, or eliminating dead ends with a user-specified probability. A dead end is a cell with three walls, and a dead end is eliminated by removing one of the three walls, either randomly or according to some strategy (e.g. give preference to removing walls that will also eliminate another dead end).
Here is an example of a 50 x 40 maze generated by the randomized depth-first search algorithm:
Here is the same maze after being fully braided:
Spelunker also defines several typeclasses for the various types of mazes, namely:
-
Show
, which can be used to create string representations of mazes, thick mazes, coordinates, positions, etc. for text output; and -
Homomorphism
, which is a structure-preserving mapping from mazes of one type to another.
Here is an example of the string representations of a Maze
produced with Show
:
┌───────┬───────────────────┬─────────┬─────┬─────┐
│ ╶─┬─┐ └───────────╴ ┌───╴ │ ╶─────┐ ╵ ╶─┐ ╵ ╶─┐ │
│ ╷ │ └─╴ ┌─────┐ ┌───┤ ╶─┬─┘ ┌─────┤ ┌───┼───┬─┘ │
├─┘ ├─────┘ ┌─┐ └─┘ ╷ └─┐ └───┘ ┌─┐ └─┤ ╷ ╵ ╷ ╵ ╷ │
│ ╶─┤ ╶─────┘ └─┬───┴─┐ ├───┬───┘ └─┐ │ ├───┴─┬─┘ │
│ ╷ └─────┬───╴ │ ╷ ┌─┘ │ ╶─┘ ╶─┐ ╶─┘ ╵ │ ┌─╴ │ ╶─┤
│ │ ╶─┬─╴ │ ╶───┤ │ │ ╶─┴─┬─────┼───┬───┤ └─┬─┴─┐ │
│ ├─┐ │ ┌─┴───┐ │ │ └───┐ │ ╶─┐ ╵ ╷ └─┐ ╵ ╷ │ ╷ ╵ │
│ │ │ │ ╵ ┌─╴ │ ╵ ├─╴ ┌─┘ └─┐ ├───┴─┐ └───┤ ╵ ├───┤
│ ╵ │ ├─┬─┘ ┌─┴───┤ ┌─┤ ┌───┘ │ ┌───┴───╴ │ ┌─┴─┐ │
├───┘ │ ╵ ╶─┴───╴ │ ╵ │ │ ╶───┘ │ ┌─┬─────┤ ╵ ╷ ╵ │
│ ┌───┴───────┬─╴ │ ┌─┘ ├───┬─╴ │ ╵ ├───╴ ├───┴─╴ │
│ ╵ ╶───┬───╴ ├───┘ │ ╷ ╵ ┌─┘ ┌─┴─┬─┘ ╶───┘ ┌─────┤
├───┬───┘ ┌─╴ │ ┌───┤ ├─╴ │ ┌─┘ ┌─┘ ┌───┬───┴─╴ ╷ │
│ ╷ ╵ ┌───┤ ┌─┘ │ ╷ │ └───┘ │ ╶─┘ ┌─┘ ╷ ╵ ╷ ┌───┤ │
│ └─┬─┘ ╷ ╵ │ ┌─┘ │ └───────┤ ╶───┴───┤ ╶─┤ │ ╶─┤ │
│ ╷ └─┬─┴─┐ │ │ ┌─┴───────╴ ├───────┐ │ ┌─┘ ├─╴ │ │
│ ├─┐ │ ╷ └─┤ ├─┘ ┌─╴ ┌───┐ │ ┌─┐ ╶─┘ ├─┘ ┌─┘ ╷ │ │
│ ╵ │ ╵ ├─╴ │ │ ╷ ├───┘ ╷ └─┤ ╵ └─────┘ ┌─┴─╴ │ │ │
├─╴ └───┤ ╶─┘ │ └─┘ ╶───┴─┐ └───────────┘ ╷ ╶─┴─┘ │
└───────┴─────┴───────────┴───────────────┴───────┘
And here is an example of the string representation of a ThickMaze
produced with Show
:
-
A C++17 capable compiler. I have tested with both clang with llvm5 (preferable) and GCC 7.
-
Boost 1.31 or higher (for
boost/pending/disjoint_sets.hpp
andboost/graph
; -
CMake 3.0 or higher; and
-
Optionally, Qt 5.9 or higher if you want the Qt widgets to draw mazes, and the Qt application to generate and solve mazes with visuals in real time.
Here is a list of features that are currently lacking of incomplete that will be in spelunker
:
-
Include more maze generation algorithms.I'm not sure there are any more well-known algorithms. Suggestions are welcome. -
Add the ability for listeners to subcribe to
MazeGenerator
instances to receive events when a maze is extended. This will allow, say, drawing of a maze as it is being generated to show how the algorithms work. -
Generate extensive statistics about each maze.
-
Add various maze solvers, and also make them subscribable so that it is possible to draw, step-by-step, the path taken through the maze.
-
Write a Qt UI (as a binary independent of the library) to display all these features (maze step-by-step generation, maze step-by-step solving, etc) in a visually pleasant way.
-
Be able to export mazes in JSON using https://github.com/nlohmann/json.
-
Implement other maze types, such as mazes on surfaces, such as cylinders and toruses, as well as circular mazes. This may require a substantial refactoring.
-
Sparsifying mazes.
-
Jamis Buck. Mazes for Programmers. The Pragmatic Programmers, July 2015. http://mazesforprogrammers.com
-
http://jeffe.cs.illinois.edu/teaching/algorithms/notes/22-apsp.pdf