This activity has been created as part of the 42 curriculum by nsadoune, hdavid.
A-Maze-ing is a Python maze generator that reads a configuration file, generates a maze (perfect or imperfect), and writes it to an output file using a hexadecimal wall representation. The program also provides a visual representation of the maze with interactive controls.
A perfect maze has exactly one path between any two cells (no loops, no isolated areas). An imperfect maze introduces a small number of extra openings (~5% of cells) to create loops and alternative paths.
The maze also embeds a visible "42" pattern made of fully closed cells as a visual signature.
- Python 3.10 or later
- Dependencies listed in
requirements.txt
make installmake runmake debugmake lintmake cleanpython3 a_maze_ing.py config.txtconfig.txt is the only argument. It defines all maze generation options (see Configuration below).
The configuration file uses one KEY=VALUE pair per line. Lines starting with # are treated as comments and ignored.
| Key | Description | Example |
|---|---|---|
WIDTH |
Maze width (number of column) | WIDTH=20 |
HEIGHT |
Maze height (number of line) | HEIGHT=15 |
ENTRY |
Entry coordinates (x,y) | ENTRY=0,0 |
EXIT |
Exit coordinates (x,y) | EXIT=19,14 |
OUTPUT_FILE |
Output filename | OUTPUT_FILE=maze.txt |
PERFECT |
Generate a perfect maze? | PERFECT=True |
# A-Maze-ing configuration
WIDTH=20
HEIGHT=15
ENTRY=0,0
EXIT=19,14
OUTPUT_FILE=maze.txt
PERFECT=True
The perfect maze is generated using a zone-merging (Union-Find) approach:
- Each cell starts in its own unique zone.
- At each step, a random cell and a random direction are picked.
- If the neighboring cell belongs to a different zone, the wall between them is removed and both zones are merged into one.
- This repeats until all non-locked cells belong to a single zone, guaranteeing a spanning tree β i.e., exactly one path between any two cells.
- It naturally guarantees a perfect maze (no loops, full connectivity) by construction β once all cells share the same zone, the maze is complete.
- It is straightforward to extend to imperfect mazes: after generating the perfect maze, a second pass (
create_imperfect_maze) randomly breaks ~5% of remaining walls to introduce loops. - It is easy to implement with reproducible randomness via a seed.
- Wall coherence (symmetric walls between neighbors) is enforced by always updating both sides of a wall simultaneously.
Solving uses a three-step approach:
-
Graph construction (
create_dict) β builds an adjacency dict from the maze grid. For each cell, it checks all 4 directions; if a wall is open (orientation[d] is False), the neighbor's position is added as a reachable node. -
BFS (
bfs) β runs a standard breadth-first search from the entry, assigning a distance level to each visited node. Returns a dict of{position: level}in reverse order (from exit back toward entry). -
Path reconstruction (
resolver) β starting from the exit, greedily follows neighbors whose BFS level is exactlycurrent_level - 1, walking back to the entry. The result is reversed to give the final path from entry to exit.
This approach correctly finds the shortest path for both perfect and imperfect mazes.
One hexadecimal digit per cell encodes which walls are closed (bit = 1) or open (bit = 0):
| Bit | Direction |
|---|---|
| 0 (LSB) | North |
| 1 | East |
| 2 | South |
| 3 | West |
Cells are written row by row, one row per line. After an empty line, the file contains:
- Entry coordinates
- Exit coordinates
- Shortest path from entry to exit (using
N,E,S,W)
The program supports the following display modes:
- Terminal ASCII rendering β colored block-based display in the terminal using
cursesor ANSI colors.
- Using
simple_term_menufor interactive menu
The "42" pattern is rendered as a group of fully closed (walled) cells visible in the maze.
The maze generation logic is packaged as a standalone, importable Python module.
Copy the directory dist in your new project to have access to the exposed tools in mazegen package that can be seen in the __init__.py.
from mazegen import *
interface()You can change parameter of the config.txt from the program that reinitialise automaticaly gracefully this line:
os.execl(sys.executable, sys.executable, *sys.argv)maze.gridβ 2D list ofCellobjects- Each
Cellhas anorientationdict:{"N": bool, "S": bool, "E": bool, "W": bool}whereTruemeans wall is present metadatais a dictionnary that contain all the main informations that is needed to solve and display the maze:
metadata = {
"maze"= conatain the grid of the maze
"entry"= start of the maze
"exit"= end of the maze
"path"= shortest way to solve the maze
"output_file" = name of the output file
"logo" = information if the logo need to be displayed
}Note: the internal structure uses
Cellobjects and boolean wall flags β this is not the same format as the hexadecimal output file.
- Maze generation algorithms - johan.sgura.fr
- Breadth-First Search - datacamp.com
- Git usage
- Library python
AI (Claude) was used for the following tasks during this project:
- Drafting this README
| Login | Role |
|---|---|
nsadoune |
Maze generation logic, path solving, Makefile |
hdavid |
Visual rendering, reusable module, |
Initial plan:
- generate the perfect maze
- display
- perfect maze solver
- generate imperfect maze
- imperfect maze solver
How it evolved:
- We both started by implementing the maze generation separately: nsadoune with Kruskal's algorithm, hdavid with a recursive backtracker. After a while, the recursive approach hit Python's recursion limit on larger mazes, so we switched to Kruskal's.
- For path solving, we initially used backtracking recursion on the perfect maze, but hit the same recursion depth issue with imperfect mazes. nsadoune then implemented BFS, which we kept for both perfect and imperfect mazes.
- The Union-Find approach made it easy to guarantee maze validity by construction.
- Splitting generation logic into a separate module from the start made packaging straightforward.
- add music
- 3D
- display path without animation
- add possibility to the menue (choosing algorithm)
- VS Code β main editor
- mypy + flake8 β static analysis and linting
