Train Tracks Puzzle Part 1: Solving Logic with Code
“@ProfByteCodes How would you go about solving these programmatically?”
— @SteveHuwJohn, referencing puzzlemadness.co.uk/traintracks
Solving Train Tracks with Code: A Step-by-Step C# Tutorial
This tutorial will walk you through writing a C# program to solve a logic puzzle known as Train Tracks, where you connect a valid train path across a grid, constrained by track counts for each row and column. It’s a great problem to explore backtracking, grid traversal, and constraint checking.
🧩 What is a Train Tracks Puzzle?
You’re given:
- A grid (e.g., 5×5)
- Numbers on the sides: how many track pieces go in each row and column
- Pre-placed fixed peices on the grid, which cannot be moved and must be connected
- A set of possible track pieces: straight, corner, and optionally crossings
- A goal: form a single, continuous path of track pieces that fits the constraints
This tutorial is inspired by the daily puzzle at PuzzleMadness.
🧱 Step 1: Represent the Pieces
We define the possible piece types:
public enum PieceType {
Empty, Horizontal, Vertical, CornerNE, CornerNW, CornerSE, CornerSW
}
Each piece will later correspond to how it connects to neighboring cells.
🗺️ Step 2: Represent the Grid
We create a Grid
class that stores:
- The board state (a 2D array of
PieceType
) - Row and column track counts
- Functions to validate placement
See the full code for details — we also include a method to print the board using ASCII art.
📏 Step 3: Apply Constraints
Before placing a track piece, we check:
- Is the cell within bounds?
- Is the cell already filled?
- Will this piece violate the row or column count?
More constraints like directional continuity will be added in later steps.
🔁 Step 4: Use Backtracking to Explore Possibilities
Our Solver
class uses recursive backtracking:
- Try placing each valid piece at the current cell
- If valid, continue recursively
- If stuck, backtrack by removing the piece
✅ Step 5: Check for Completion
We check that each row and column has the exact number of track pieces. (We’ll add continuous path checking later.)
💾 Step 6: Define a Puzzle Format
Many Train Tracks puzzles come with pre-placed track pieces. To support loading and saving puzzles, we need a simple format that includes:
- The dimensions of the grid
- Row and column counts
- Any fixed track pieces with their positions and types
Here’s an example in a human-readable text format:
# Puzzle 5x5
ROWS: 2 1 3 1 2
COLS: 2 2 1 2 2
FIXED:
1,0: Vertical
2,2: CornerSE
4,4: Horizontal
This format means:
- Row 0 has 2 track pieces, row 1 has 1, etc.
- Column 0 has 2 track pieces, column 1 has 2, etc.
- Cell (1,0) contains a fixed vertical track
- Cell (2,2) contains a fixed southeast corner
- Cell (4,4) contains a horizontal track
You can now write a simple parser in your Grid
class to load this format and set up the board before solving.
🧪 Step 7: Run a Sample Puzzle
Try solving this:
int[] rowCounts = { 2, 1, 3, 1, 2 };
int[] colCounts = { 2, 2, 1, 2, 2 };
Run the program and it will either print a solution, or report failure.
🚀 Next Steps
Now that we have a working skeleton:
- Add support for directional constraints (track connections must match!)
- Enforce a single continuous path (entry-to-exit)
- Add more track types (T-junctions, crossings)
- Visualize it with colors or a simple UI
Want to try this puzzle yourself? Play it here
💬 Have your own puzzle or coding challenge to feature? Tag @ProfByteCodes on Twitter and we might turn it into a tutorial!
👉 Full source code on GitHub