Building a Train Track Puzzle Solver: A Complete Tutorial
“@ProfByteCodes How would you go about solving these programmatically?” — @SteveHuwJohn, referencing puzzlemadness.co.uk/traintracks
In this three‑part series, we took on the challenge of building a train track puzzle solver from scratch. Below is a quick tour of our journey:
Part 1: Representing the Puzzle
In our first post, we introduced the problem domain and laid out a brute‑force strategy that explores every possible path through the grid. We covered:
- Grid representation: How to model the puzzle board as nodes and edges.
- Backtracking approach: Recursively extending paths until a complete loop is found.
- Basic serialization: Loading puzzles from file, easier than hard-coding!
Read more → Train Tracks Solver Part 1
Part 2: Priority-Queue Based Solver
In the second installment, we implemented a complete solver which checks we have a continuous path. We also discuss how we optimized it, and ended up with something which wasn’t uterly terrible!
- Directional Logic: Defining how track pieces connect.
- Validating Placement: Ensure we only place pieces which connect correctly.
- Ensuring a Connected Path: Making sure we solve the problem!
- Optimization Techniques: How we sped it up, and resulted in sub-second solving capabilities with a Priority Queue solver!
Read more → Train Tracks Solver Part 2
Part 3: Rethinking our Solver for the win!
Our final post we rethought our strategy and implemented a Path Based solver which was quicker and more efficient.
Read more → Train Tracks Solver Part 3
What’s Next?
Since publishing the series, we’ve:
- Implemented a third solver: Based on the /posts/2025/04/17/a-star/ algorithm, can it beat the path solver?
- Added a Generator: Produce random puzzles of configurable size and complexity.
- Benchmark Mode: Compare solve times across different heuristics and implementations.
- Community Collaboration: Helped @SteveHuwJohn debug his own solver and collected feedback to improve ours.
Solver Leaderboard
Below is a placeholder for our current leaderboard of best solve times.
These were generated on a 2025 M4 MacBook Pro using the following command:
dotnet run -c Release -- -p Puzzles.json -b -m path,astar --timeout 1000
# | Rows x Cols | Solver | Iterations | Time (ms) | Solved |
---|---|---|---|---|---|
1 | 12x12 | path | 30,547,824 | 14,143 | Yes |
1 | 12x12 | astar | 44,096,048 | 343,795 | Yes |
2 | 11x12 | path | 3,848,513 | 1,829 | Yes |
2 | 11x12 | astar | 7,886,810 | 56,305 | Yes |
3 | 11x11 | path | 604,317 | 287 | Yes |
3 | 11x11 | astar | 433,134 | 3,033 | Yes |
4 | 12x11 | path | 1,402,056 | 641 | Yes |
4 | 12x11 | astar | 1,177,883 | 8,123 | Yes |
5 | 6x6 | path | 210 | 0 | Yes |
5 | 6x6 | astar | 322 | 0 | Yes |
6 | 6x6 | path | 119 | 0 | Yes |
6 | 6x6 | astar | 159 | 0 | Yes |
7 | 9x9 | path | 208,978 | 89 | Yes |
7 | 9x9 | astar | 158,632 | 693 | Yes |
8 | 9x9 | path | 690 | 0 | Yes |
8 | 9x9 | astar | 395 | 1 | Yes |
9 | 10x10 | path | 1,621 | 0 | Yes |
9 | 10x10 | astar | 2,016 | 11 | Yes |
10 | 11x12 | path | 583,354 | 277 | Yes |
10 | 11x12 | astar | 557,723 | 4,125 | Yes |
11 | 6x7 | path | 964 | 0 | Yes |
11 | 6x7 | astar | 744 | 2 | Yes |
12 | 12x11 | path | 254,592 | 126 | Yes |
12 | 12x11 | astar | 694,030 | 5,538 | Yes |
13 | 12x12 | path | 1,068,800,127 | 496,306 | Yes |
13 | 12x12 | astar | 870,157,161 | 39,626,338 | Yes |
Source Code and Repository
All of the code—solvers, benchmarks, and the puzzle generator—is available on GitHub:
https://github.com/professorbyte-codequest/train-track-solver/tree/main
Feel free to clone, experiment, and contribute! Happy coding, and may your tracks always connect.