Train dispatcher party game for Roblox — railway interlocking as pure property-tested laws: exclusive atomic block reservations, route-locked switch levers, exact color-match routing with boundary-exact on-time verdicts.
- Trains spawn on a shrinking-but-floored interval, each colored Red or Blue, each due on a schedule.
- Flip the SW1/SW2 levers (physical prompts or HUD buttons) to route trains down the upper/lower approach and out the Red/Blue exit lane.
- Match every train to its color's platform before it's late.
- Levers LOCK while a train is on the route — you can't yank a switch under a moving consist (the interlock).
- Reservation law (the crash-prevention interlock): a block grants
iff free; re-reserving by the holder is IDEMPOTENT and mutates
nothing (verified by table identity); distinct trains get
held_by_otherverbatim; release by anyone but the HOLDER is a no-op — nobody can free someone else's interlock. Across 200 seeded random walks of reserve/release over six blocks, no two distinct trains EVER co-held a block. - Switch law: flips are denied exactly while any active route locks
that lever (
locked_by_train); other switches stay flippable. - Routing law: arrivals are correct EXACTLY on color match; on-time iff arrival ≤ dueAt — boundary tested at ±0.001s around the deadline.
- Spawn law: intervals shrink geometrically (×0.96 per dispatch) monotonically toward an exact FLOOR of MinSpawnIntervalSeconds — the game speeds up forever but never becomes unplayable; train speed is monotone in difficulty with defensive clamping below zero.
Three server functions were referenced before their locals existed
(BLOCK_POINT_FOR, spawnPending, resolveArrival) — they'd resolve
to nil globals and error mid-game. Caught in review: forward
declarations + assignment conversion before shipping.
src/shared/ Config + YardMath (reservation/switch/routing/spawn laws)
src/server/ Remotes + YardService (interlocking owner, waypoint
movement, signal-waiting trains)
src/client/ Network + score/inbound-board/HUD-levers
tests/ lune headless specs (12) incl. 200-walk exclusivity sweep
lune run tests/run_tests.luau # unit tests
lune run tests/syntax_check.luau # compile-check all sources
rojo build -o switchyard-scramble.rbxlx # validate project mapping
rojo serve # live-sync into StudioCI runs tests, syntax checks, and a Rojo build on every push.
- Path reservations are whole-route locks (OpenTTD-style per-path PBS sharing through junctions is the roadmap)
- No crashes yet — conflicting entries WAIT at signals rather than collide (safer, but less dramatic)
- No Studio playtest yet (human dependency)