“@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