v0.1.0 — undo, the crew's reach on the board, workers on the card, and four engine bugs

Fifteen items from two playtest sessions. Three that read as drawing faults were engine
bugs: cars could be added to a train that was not being made up (50 offers in 8 games),
the make-up panel merged two trains and could couple a car to the wrong one, and an
Office upgrade silently deleted what a Modifier had added. A fourth was a sentinel
inside a coordinate's own value range — a Mainline placement travelling as row -1, which
is an ordinary district row.

Trains are now drawn the way they stand: west on the left, nose toward the way the engine
faces, on both the district card and the Division chip. Undo steps back through the game
by replaying the save without its last intent. The switching walk keeps its rejections, so
the board can say why a square is not offered. Laborers and Porters are on the card, and
the rule that a district only grows outwards is finally written down.

Versions start here: third digit for fixes, second for a feature set, 1.0 for a release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgtkX8JnvKa8y2tuJ8aQf4
This commit is contained in:
Jesse
2026-08-08 05:23:17 -04:00
co-authored by Claude Opus 5
parent f4c0f49604
commit 5d825b97d2
20 changed files with 1604 additions and 191 deletions
+38 -2
View File
@@ -16,11 +16,20 @@ h2.big{font-size:15px;text-transform:none;letter-spacing:.01em;color:#cfe0f5;
margin:0 0 9px;padding-bottom:5px;border-bottom:1px solid #2c333d}
h3{font-size:11px;text-transform:uppercase;letter-spacing:.07em;color:var(--dim);margin:9px 0 4px}
.dim{color:var(--dim)}
header{position:sticky;top:0;z-index:5;background:var(--panel);border-bottom:1px solid var(--line);
/* THE TOP TWO ROWS TRAVEL TOGETHER.
The title bar was sticky and the turn chart under it was not, so scrolling down took away the one
thing you most need while reading the board — which Day and Stage it is, which phase is running,
and who it is waiting on. They are one block now, and it sticks as one. */
.topbar{position:sticky;top:0;z-index:5}
header{background:var(--panel);border-bottom:1px solid var(--line);
padding:9px 14px;display:flex;gap:20px;align-items:baseline;flex-wrap:wrap}
header b{font-size:16px}
header button{background:#2a3038;color:inherit;border:1px solid #2c333d;border-radius:5px;padding:3px 9px;cursor:pointer;font:inherit;font-size:12px}
header button:hover{border-color:#4d6fa8}
/* Nothing to take back yet. It keeps its place — a button that disappears reads as a bug — but it
stops inviting the press. */
header button:disabled{opacity:.45;cursor:not-allowed;border-color:#2c333d}
header button:disabled:hover{border-color:#2c333d}
.build{margin-left:auto;font-size:11px;opacity:.7}
.home{color:inherit;text-decoration:none;border-bottom:1px dotted #5f6b7a}
.home:hover{color:#5aa9e6}
@@ -106,6 +115,12 @@ button.ghost{background:#222831;border:1px solid #4a5361;color:#c6ccd6;font-size
padding:2px 9px;margin-left:10px;text-transform:none;letter-spacing:0;vertical-align:middle}
button.ghost:hover{border-color:#4d6fa8;color:var(--fg)}
#district.folded #grid{display:none}
#district.folded .districtrule{display:none}
/* Said once, quietly, beside the thing it governs — a rule a player needs on their first district
and never again, so it must not shout. */
.districtrule{font-size:11px;line-height:1.4;color:#8b94a3;margin:0 0 7px;padding:5px 8px;
border-left:2px solid #39424e;background:#171c23;border-radius:0 4px 4px 0}
.districtrule b{color:#cfd6e0}
#district.folded #districtsummary{display:block;padding:2px 0 1px;font-size:12px}
#districtsummary{display:none}
/* An action you cannot take yet keeps its place but drops its light — the amber means "press me",
@@ -142,6 +157,7 @@ ul.blocked li{padding:2px 0}
</head>
<body>
<div class="topbar">
<header>
<b><a href="./index.html" class="home">Station Master</a></b>
<span>Revenue <b id="revenue">0</b></span>
@@ -149,8 +165,9 @@ ul.blocked li{padding:2px 0}
<span class="dim">seed <span id="seed">—</span></span>
<span class="dim">saved in this browser · add ?seed=1234 for a set deal</span>
<button id="sound" title="Whistle at the end of each Stage, the crossing bell at the end of each Day, and the conductor when a train is built. Currently synthesised, not recorded.">🔇 muted</button>
<button id="undo" title="Take the last action back. The save is the seed plus the moves made, so this replays the game without the last one — as far back as you like.">Undo</button>
<button id="savefile" title="Download this game as a save file you can replay or share">Save replay</button>
<button id="newgame" title="Forget the game saved in this browser and deal a fresh one. There is no undo — download the replay first if you want to keep it.">New game</button>
<button id="newgame" title="Forget the game saved in this browser and deal a fresh one. Undo steps back one action at a time; this throws the whole game away, so download the replay first if you want to keep it.">New game</button>
<a class="home" href="./replays.html" style="font-size:12px">replays</a>
<span class="dim build" title="what is actually deployed">__BUILD__</span>
</header>
@@ -160,6 +177,7 @@ ul.blocked li{padding:2px 0}
<!-- Filled by `turnChartHtml`, shared with both replay viewers so all three screens report where
you are in the Day identically. -->
<div id="turnchart"></div>
</div><!-- /topbar -->
<!-- Announces a phase CHANGE. The turn chart says where you are; this says that it moved, which is
what you miss when the automatic phases run between two clicks. -->
<div id="phasenote"></div>
@@ -173,6 +191,15 @@ ul.blocked li{padding:2px 0}
<button id="districttoggle" class="ghost" title="Auto-hide keeps the district open during Local Operations and Cargo — the phases that change it — and folds it otherwise. Click to pin it open or hidden instead.">auto-hide: on</button>
</h2>
<div id="districtsummary" class="dim"></div>
<!-- THE RULE THAT SHAPES EVERY DISTRICT, said once where the district is.
It is enforced everywhere and was stated nowhere: the Limits signs are the only growth
point on the Running Track and they move outward as you build, and no card anywhere can
be inserted between two cards already down. -->
<div class="districtrule">A district only ever grows <b>outwards</b>. On the Running Track the
Limits signs are the growth point — lay a card on a sign and the sign moves one further out;
nothing may be built beyond it. Everywhere else a card needs an empty square that its rails
reach. <b>No card is ever inserted between two cards already down</b>, so nothing you have
built is pushed along by something laid later.</div>
<div id="grid"></div>
</section>
<section><h2>History</h2><div id="log"></div></section>
@@ -192,6 +219,15 @@ ul.blocked li{padding:2px 0}
<div class="dim" id="yardnote"></div>
</section>
<section><h2>Timetable — who is due out, and when</h2><div id="timetable"></div>
<!-- WHAT THE COLOURS MEAN. Asked directly in playtesting — "what do green and purple mean?"
Violet is "where you are" everywhere on this page, and the green is a flash rather than a
state, which is exactly why it needed saying. -->
<div class="tt-key">
<span class="k-due">Train due</span>
<span class="k-now">this Stage</span>
<span class="k-past">already gone</span>
<span class="k-fresh">just rolled</span>
</div>
<div class="dim" style="font-size:11px;margin-top:5px">A train's number is its seniority and direction — odd runs west, even runs east. The Stage it departs is set by a 1D12 roll when its card is played, and it then runs at that time every Day.</div>
</section>
<section><h2>Blocked — why nothing is moving</h2><ul class="blocked" id="blocked"></ul></section>