Maze generation and pathfinding in Go, with two frontends over one core: a Fyne desktop app and a Gin web server.
The interesting part is that the same maze gets solved two ways — once by breadth-first search, which always finds the shortest path, and once by a Q-learning agent that has to discover the route by trial and error. Watching the two disagree is the point.
- Generates perfect mazes with Eller's algorithm — one row at a time, constant memory in the number of rows
- Solves them with BFS (shortest path, guaranteed) or with a Q-learning agent (learned policy, not guaranteed)
- Loads and saves mazes as plain text, so generated mazes can be replayed later
- Renders in a desktop window or in the browser, from the same core packages
Requires Go 1.25 or newer. The desktop app needs the Fyne prerequisites for your platform; the web server has no system dependencies.
git clone https://github.com/whiterage/MazeGo.git
cd MazeGo
make runFor the browser version:
make run-webThen open http://localhost:8080.
Other targets:
make build # desktop binary into ./build
make build-web # web server binary into ./build
make install # copy the desktop binary to ~/.local/bin
make test # run all tests
make test-coverage # run tests with coverage
make fmt vet # format and vet
make clean # remove build outputBreadth-first search treats the maze as an unweighted graph and expands outward from the start until it reaches the end. Because every step costs the same, the first time it touches the target it has already found a shortest path. It is fast, deterministic, and always correct.
Q-learning knows nothing about the maze up front. The agent repeatedly walks from start to end, receives a reward at the goal, and updates a table of state-action values. Over enough episodes the table converges on a policy that walks the maze without searching.
| BFS | Q-learning | |
|---|---|---|
| Needs training | no | yes |
| Shortest path | always | usually, not guaranteed |
| Knows the maze in advance | yes | no |
| Cost | one pass | many episodes |
The agent runs with alpha = 0.15 (learning rate), gamma = 0.8 (discount), and epsilon = 0.5
decaying by 0.995 per episode — so it explores heavily at first and increasingly trusts what it
has learned.
Comparing them on the same maze is what makes the trade-off concrete: BFS wins on every practical measure here, and the value of the Q-learning path is that it works without ever being handed the map.
Plain text. The first line is the row and column count. Then two matrices separated by a blank line:
right walls, then bottom walls. 1 means the wall is present.
4 4
0 0 0 1
1 0 1 1
0 1 0 1
0 0 0 1
1 0 1 0
0 0 1 0
1 1 0 1
1 1 1 1
Sample mazes live in internal/assets/.
The web server exposes three endpoints, and serves the browser frontend from web/ at the root.
| Method | Path | Request | Returns |
|---|---|---|---|
POST |
/api/maze/load |
multipart file upload | the parsed maze |
POST |
/api/maze/generate |
{ "rows": 15, "cols": 15 } |
a generated maze |
POST |
/api/maze/solve |
{ "maze": …, "start": …, "end": … } |
the path between the two cells |
A maze comes back as its two wall matrices, and a solution as an ordered list of cells:
{ "rows": 4, "cols": 4, "right": [[0,0,0,1]], "bottom": [[1,0,1,0]] }
{ "path": [{ "row": 0, "col": 0 }, { "row": 1, "col": 0 }] }The HTTP API solves with BFS only. The Q-learning agent runs in the desktop app, where the training can be watched.
cmd/maze/ desktop entry point (Fyne)
cmd/web/ web server entry point (Gin)
internal/
domain/model/ the Maze type — dimensions plus right and bottom wall matrices
generator/ Eller's algorithm
solver/ shared point and direction types, BFS
solver/rl/ Q-learning agent, Q-table, training loop, path extraction
loader/ reading mazes from file
writer/ writing mazes to file
ui/ Fyne widgets and rendering
api/ HTTP handlers, routes, DTOs
assets/ sample mazes
web/ browser frontend (static HTML, CSS, JS)
Keeping generation and solving in internal/ with no UI imports is what lets the desktop app and
the web server share them without either one knowing about the other.
make test-coverage| Package | Coverage |
|---|---|
internal/solver/rl |
97.6% |
internal/generator |
97.1% |
internal/solver |
92.6% |
internal/writer |
69.0% |