← Back
Play

Pipe Runner

A grid-based pipe routing puzzle game where players route building systems (plumbing, HVAC, electrical, fire suppression) through 2D building cross-sections. Teaches VDC (Virtual Design and Construction) coordination concepts using real MEP (Mechanical, Electrical, Plumbing) terminology. Features crossing penalties, material budgets, routing priority order, and a hint system to create genuine optimization puzzles. 32-level progression across four difficulty tiers.

Game Flow

Menu → Level Selection → Level Intro → Countdown → Playing ⇄ Paused → Results
  • Menu: Hero screen with how-to-play instructions and a Play button
  • Level Selection: 32 levels grouped by difficulty (Easy/Medium/Hard/Expert). Shows stars, best score, best time
  • Level Intro: Shows level title, description, scenario card (building context), grid size, system count, par time. Hard/Expert levels show a warning that all systems are unlocked but out-of-order routing incurs penalties
  • Countdown: 3-2-1-GO via shared Countdown component
  • Playing: Grid displays with system endpoints. Player selects a system from the palette, then taps/drags to route a path through the grid connecting the two endpoints. Eye toggles control per-system layer visibility. Hint button reveals the optimal path for the active system (once per level, with penalty)
  • Results: Full score breakdown with crossings, budget, rework, out-of-order, and hint penalties; efficiency bonus; grade and star rating

Unlock Logic

  • Level 1 always unlocked
  • Completing any level unlocks the next (linear progression)

Core Mechanics

Crossing Penalties

Pipes can cross other system paths at different elevations (like real VDC coordination), but each crossing incurs a -40 point penalty. This creates optimization puzzles where the challenge is finding routes that minimize or eliminate crossings.

At crossing points, the "under" pipe renders shorter with reduced opacity, and a dark separator dot marks the elevation change visually.

Material Budget

Each system can have a budget (max cells). Going over budget doesn't prevent completion but applies a -15 pts/cell penalty. This represents material cost and teaches students about efficient routing. Easy levels have no budgets; Medium+ levels have increasingly tight budgets.

Routing Priority Order

Systems can have a priority number (1 = first, 2 = second, etc.). Priority behavior varies by difficulty:

  • Easy/Medium: Priority is enforced — players must complete higher-priority systems before lower-priority ones unlock. The SystemPalette shows priority badges and lock icons for locked systems.
  • Hard/Expert: Priority is recommended — all systems are unlocked from the start, but completing a system before its predecessors incurs a -30 pts/violation out-of-order penalty. Priority badges display in amber with "recommended order" tooltips instead of locks.

This mirrors real VDC coordination: gravity-dependent waste lines route first, then large ducts, then smaller pipes. On harder levels, students experience deadlocks and learn why order matters organically.

Hint System

Players can use a hint once per level. The hint button (lightbulb icon) uses crossing-aware pathfinding (Dijkstra) on the current live grid state to find and auto-place the best path for the active system. This costs -100 points.

  • Available once per level per attempt (button disables after use)
  • Uses findSmartPath — a budget-aware Dijkstra algorithm that minimizes crossings, not just path length (see Pathfinding below)
  • If no valid path exists (other pipes completely block the route), the hint shows a "Path blocked — clear a system first" message and is not consumed
  • If the hint completes the last remaining system, the timer freezes immediately and a 1.5-second delay shows the solution before transitioning to results

Pathfinding

Two pathfinding algorithms serve different purposes:

AlgorithmFunctionUsed For
Plain BFSfindOptimalPathLevel validation, efficiency bonus check, budget slack calculation — finds the geometric shortest path on the static grid
DijkstrafindSmartPathHint system — finds the score-optimal path considering existing pipes on the live board

Crossing-aware cost model (findSmartPath):

Each cell traversed costs 1. Each cell occupied by another system's pipe costs 1 + effectiveCrossingCost. The effective cost is budget-aware:

effectiveCrossingCost = min(budgetSlack + floor(crossingPenalty / overBudgetPerCell), 10)
                      = min(budgetSlack + floor(40 / 15), 10)
                      = min(budgetSlack + 2, 10)

This models: "I have S cells of budget slack (free detour) plus floor(40/15) = 2 more cells that are still cheaper than a crossing (-30 pts < -40 pts)."

Budget SlackEffective CostMax Detour to Avoid 1 Crossing
No budget (Easy)1010 cells
6 (generous)88 cells
3 (tight)55 cells
1 (very tight)33 cells

Building Scenarios

Every level includes a scenario field that describes the building context (e.g., "Hospital wing mechanical room" or "Data center build-out"). Scenarios are displayed on the Level Intro screen and progressively introduce trade terminology:

  • CW (Cold Water) — Level 1
  • DWV (Drain-Waste-Vent) — Level 2
  • SA (Supply Air) — Level 3
  • EMT (Electrical Metallic Tubing) — Level 4
  • FP (Fire Protection) — Level 5
  • RA (Return Air) — Level 7
  • MEP (Mechanical, Electrical, Plumbing) — Level 14
  • VDC (Virtual Design and Construction) — Level 28
  • BIM (Building Information Modeling) — Level 32

System Types

TypeDisplay NameColorSymbolDescription
plumbing-supplyCopper Supply (CW)BlueCWCold water supply piping
plumbing-wastePVC Waste (DWV)GreenDWVDrain/waste/vent piping
hvac-supplySupply Duct (SA)RedSASupply air ductwork
hvac-returnReturn Duct (RA)CyanRAReturn air ductwork
electricalEMT ConduitYellowEElectrical metallic tubing
fire-suppressionSprinkler Main (FP)CrimsonFPFire protection piping

Grid Mechanics

Cell Types

  • Empty: Available for routing
  • Wall: Impassable solid obstacle (hatched pattern)
  • Structural: Labeled obstacle (W-Flange, Bar Joist, CMU Wall, Column)
  • Endpoint: System connection point (start or end of a system route)

Interaction

Supports both tap and drag:

  • Tap: Click/tap a system endpoint to start routing, then tap adjacent cells to extend the path
  • Drag: Press and drag across cells to paint a path in one motion
  • Retract: Tap the last cell in a path to remove it; drag backward to undo
  • Clear: Per-system clear button in the SystemPalette (counts as a reroute)

Path Validation

Each cell placement is validated:

  • Must be adjacent (cardinal) to the previous path cell
  • Cannot be a wall or structural element
  • Endpoints only accept their own system
  • A pipe cannot cross itself (self-overlap prevented)
  • Crossing another system's path is allowed but penalized in scoring

Layer Visibility

Show/hide individual system layers using eye toggle buttons integrated into the SystemPalette. Helps plan routes by isolating visual layers — mirrors real BIM coordination workflows.

Difficulty Scaling

SettingEasyMediumHardExpert
Grid size6×68×810×812×10
Systems1-22-33-44-5
Par time (sec)60120180300
PriorityNoEnforced (locked)Recommended (penalty)Recommended (penalty)
Material budgetNoGenerous (+6 slack)Tight (+4 slack)Very tight (+1-3 slack)
Out-of-order penaltyN/AN/A-30 pts/violation-30 pts/violation
FeaturesOpen spaceCorridors, priorityPre-placed obstaclesAll system types

Scoring

Config (single source of truth)

All values are centralized in SCORING_CONFIG in config.ts:

SCORING_CONFIG = {
  maxScore: 1000,
  timePenaltyAtPar: 120,
  crossingPenalty: 40,
  reworkPenalty: 20,
  overBudgetPenaltyPerCell: 15,
  excessPipePenaltyPerCell: 10,
  outOfOrderPenalty: 30,
  hintPenalty: 200,
  efficiencyBonus: 50,
}

Formula

baseScore = max(0, 1000 - (elapsedSeconds / parTime) × 120)
crossingPenalty = crossingCount × 40
reworkPenalty = rerouteCount × 20
budgetPenalty = totalCellsOverBudget × 15       (systems with budgets)
excessPipePenalty = totalCellsBeyondOptimal × 10 (systems without budgets)
outOfOrderPenalty = outOfOrderCount × 30         (Hard/Expert only)
hintPenalty = hintUsed ? 200 : 0                 (once per level)
efficiencyBonus = allPathsOptimal ? 50 : 0

finalScore = max(0, baseScore - crossingPenalty - reworkPenalty - budgetPenalty
                  - excessPipePenalty - outOfOrderPenalty - hintPenalty + efficiencyBonus)

Time penalty scales with par time — at par the cost is 120pts regardless of difficulty. Excess pipe counts cells beyond optimal+2 for systems that have no material budget. A path is "optimal" if its length is within 2 cells of the BFS shortest path.

Stars (difficulty-scaled)

StarsEasyMediumHardExpert
3≤par, 0 reroutes, 0 crossings, optimal≤par, 0 reroutes, ≤1 crossing, optimal≤par, 0 reroutes, ≤2 crossings, optimal≤par, ≤1 reroute, ≤3 crossings, optimal
2≤1.5×par, ≤1 crossing≤1.5×par, ≤2 crossings≤1.5×par, ≤4 crossings≤1.5×par, ≤5 crossings
1CompletedCompletedCompletedCompleted

Accuracy (difficulty-scaled)

accuracy = max(0, 100 - rerouteCount × reworkWeight - crossingCount × crossingWeight)
DifficultyRework weightCrossing weight
Easy157
Medium135
Hard115
Expert103

Grade Thresholds (difficulty-scaled)

Both minimum score AND minimum accuracy must be met:

GradeEasyMediumHardExpert
S900 pts / 95% acc875 pts / 90% acc825 pts / 85% acc750 pts / 75% acc
A750 pts / 80% acc725 pts / 80% acc675 pts / 70% acc600 pts / 60% acc
B550 pts / 70% acc550 pts / 60% acc500 pts / 50% acc425 pts / 40% acc
C350 pts / 50% acc325 pts / 40% acc300 pts / 30% acc250 pts / 20% acc
D0 pts / 0% acc0 pts / 0% acc0 pts / 0% acc0 pts / 0% acc

In-Game HUD

During gameplay the HUD displays:

  • Live score: Updates every tick based on current time + all penalties
  • Crossing count: Color-coded badge with point deduction shown (0=green, 1-2=yellow, 3+=red)
  • Budget status: Cells used vs total budget with over-budget deduction shown (green/yellow/red)
  • Rework count: Badge with point deduction shown (appears when > 0)
  • Out-of-order count: Amber badge with point deduction shown (Hard/Expert, appears when > 0)
  • Hint used: Amber badge with -100 deduction shown (appears after use)
  • Hint button: Lightbulb icon, amber colored, disabled after use or when no active system
  • Scoring legend: Expandable "?" panel showing all penalty and bonus values, including out-of-order and hint costs
  • System palette: System buttons with eye toggles, per-system clear, priority badges (locked on Easy/Medium, recommended on Hard/Expert), budget tracker

Platform Score

Uses the cumulative score model. After each level, the sum of best scores across all completed levels is posted. TNW_COMPLETE fires once on the player's first-ever level completion.

Level Design Guidelines

When adding or editing levels:

  1. Easy levels (1-10): No priority, no budget. 1-2 systems max. Levels 1-5 introduce each system type solo (CW, DWV, SA, E, FP). Levels 6-7 introduce multi-system coordination with zero crossings (including RA intro at level 7). Levels 8-10 introduce crossings with ascending density (2→3→4).
  2. Medium levels (11-20): Add priority (waste/largest first). Set budget = optimal + ~5. Priority is enforced (locked). 2-3 systems. L11 is 2-system bridge from Easy; L12-20 are 3-system with crossings ascending 4→5→6→6→7→7→7→8→8.
  3. Hard levels (21-27): Priority is recommended (not locked). Budget = optimal + ~3. All systems unlocked — out-of-order routing penalized. L21-22 are 3-system transition; L23-27 are 4-system. Scenario references real building contexts.
  4. Expert levels (28-32): Full priority chain (4-5 systems). Budget = optimal + 1-3. All unlocked with out-of-order penalties. Crossings ascend 16→19→20→22→28. BIM Coordination Final is level 32 (true capstone).

Always verify (tests in tests/games/pipe-runner/):

  • levels.test.ts: Level solvability, budget slack, priority deadlocks, scoring math
  • grid.test.ts: findOptimalPath (BFS), findSmartPath (Dijkstra crossing avoidance), isCellAvailable, isPathComplete

Solver Analysis (auto-generated via solveLevel)

Easy (6×6, no budget/priority)

LvlTitleSystemsOpt. LenCrossingsPhase
1First Pipe Run1 (CW)110Solo intro
2Laundry Drain Line1 (DWV)90Solo intro
3Classroom Ductwork1 (SA)80Solo intro
4Garage Wiring Run1 (E)110Solo intro
5Sprinkler Loop1 (FP)80Solo intro
6Dual Line Rough-In2 (CW+DWV)140Multi, no cross
7Server Room Returns2 (RA+CW)140Multi, no cross (RA intro)
8Utility Closet Connect2 (CW+SA)202Crossings
9Break Room HVAC2 (RA+E)223Crossings
10Small Office Fit-Out2 (SA+E)224Crossings

Medium (8×8, enforced priority, generous budget)

LvlTitleSysCrossingsMin SlackOpt. Len
11Bathroom Stack Coordination24627
12Apartment Riser34440
13Kitchen Mechanical Chase35638
14First Floor MEP Rough-In36638
15Hotel Guest Room36633
16Retail Sprinkler Install37538
17Medical Clinic37438
18Warehouse Mezzanine37441
19Restaurant Exhaust38538
20Library Reading Room38337

Hard (10×8, recommended priority, tight budget)

LvlTitleSysCrossingsMin SlackOpt. Len
21Ceiling Plenum Stack-Up38444
22Corridor Crossover38245
23Mechanical Room Coordination411452
24School Gymnasium410453
25Hospital Corridor414456
26Clean Room Prep414355
27Parking Garage MEP412259

Expert (12×10, recommended priority, very tight budget)

LvlTitleSysCrossingsMin SlackOpt. Len
28Full Floor Coordination516176
29Riser Shaft Coordination519383
30Operating Suite520279
31Industrial Process Plant522386
32BIM Coordination Final528186

Technical Architecture

Key Files

AreaFilePurpose
Typesgames/pipe-runner/types.tsGamePhase, CellType, SystemType, GridCell, SystemDef (with budget and priority), LevelConfig (with scenario), LevelProgress
Configgames/pipe-runner/config.tsSCORING_CONFIG, calculateFinalScore(), calculateStars(), calculateAccuracy(), calculateOverBudget(), isPriorityEnforced(), per-difficulty grade thresholds/star limits/accuracy weights
Storegames/pipe-runner/store.tsZustand store: grid state, routing paths, system selection, layer visibility, timer, crossing/rework/outOfOrder counts, hintUsed, priority enforcement (difficulty-aware), hint action with crossing-aware pathfinding
Gridgames/pipe-runner/grid.tsPure grid utility functions: createGrid, isCellAvailable, isAdjacent, getPathConnections, isPathComplete, calculateOptimalLength, findOptimalPath (BFS), findSmartPath (Dijkstra), solveLevel
Systemsgames/pipe-runner/content/systems.tsSystem type definitions with colors, symbols, display names
Levelsgames/pipe-runner/content/levels.ts32 level definitions with grids, walls, structural elements, system endpoints, budgets, priorities, scenario text
Hookgames/pipe-runner/hooks/use-game-actions.tsupdateLevelProgress(), getProgress(), getCumulativeScore()
Audiogames/pipe-runner/hooks/use-game-audio.tsPhase-aware SFX management (synthwave BGM)
Barrelgames/pipe-runner/index.tsPublic exports for all config, store selectors, and types

Components

ComponentPurpose
game-grid.tsxMain grid container with pointer event handling (tap + drag routing), multi-layer pipe rendering
grid-cell.tsxIndividual cell rendering: wall, structural, endpoint (with active pulse), pipe segments, crossover visuals
system-palette.tsxSystem selection bar with eye toggles, priority badges (locked or recommended), per-system clear, completion status, budget tracker
hud.tsxLive score, timer, hint button, crossing/rework/out-of-order/hint badges with deductions, budget indicator, scoring legend, system palette, completion delay for hint
level-list.tsx32-level grid with difficulty grouping, progress stats, cumulative score
how-to-play.tsxInstructions for the menu screen (references SCORING_CONFIG values)

Uses shared components: LevelCard, LevelIntro, Countdown, PauseMenu, ResultsScreen, GameError, LoadingScreen, GameShell, GameHeader.

Routing

/pipe-runner/              → Menu (hero + Play button)
/pipe-runner/levels/       → Level selection (32 levels)
/pipe-runner/play/[level]/ → Gameplay (level = 1-32)

Persistence

Storage key: pipe-runner-state

Save data:

{
  levelProgress: {
    level_1: { bestTimeMs, stars, unlocked, completedAt, attempts, bestScore, bestAccuracy },
    level_2: { ... },
    ...
  },
  lastPlayedLevel
}