v0.8.2 — every district opens on a Depot, and the docs are pages now
A second-digit bump for a playtest read back against the save file. Nine questions were asked of one three-Day game; three were bugs, three were the rules working and undocumented, three were decisions. Every save on the test server was replayed against this build BEFORE release, which is how the cost of each rule was known before it was chosen rather than discovered after. EVERY DISTRICT OPENS ON A DEPOT. A Whistle Post has one A/D track and is not a Passenger Facility, so the opening of every game was spent unable to work a passenger and one arrival away from a collision. Two A/D tracks and passengers from Stage 1 now; "Players start with Whistle Posts, not Depots" is the harder game, set when the game is created. The deck follows the choice — starting on Depots the four Depot upgrade cards are left out, because an upgrade must be to the next tier and a Depot card at a table of Depots is a dead draw. How much easier it is showed up as a test failure rather than an argument: the cue-coverage pool needed widening from 24 seeded games to 60 before it held one collision. NO SAVE WAS STRANDED BY IT, which took care. This is the one house rule that changes how a game is DEALT rather than how it plays, so replaying a save under the wrong opening is a different railroad from intent one — silently, with no error. `withSavedOpening` fills it on the replay paths ONLY. Putting it in the resolver instead made a fresh Cutthroat game deal Whistle Posts and read as Custom, which is how the distinction was found. THREE BUGS, ALL REPORTED FROM ONE GAME AND ALL CONFIRMED ON ITS SAVE. An Office held TWO TRAINS ON ONE A/D TRACK. The capacity test passed with nothing standing, the train the Interlocking had been holding at the Limits was moved into the free slot, and the arriving train was pushed in after it without anyone asking again whether there was room — so the collision §8.3 calls for never happened. The held train keeps priority; the newcomer now takes the consequence it would have met had the held train arrived first. THE HISTORY FROZE, permanently, and the log cap was not really the cause. Each seat's "what have I sent you" bookmark was an INDEX into an array the game trims, so once a seat's bookmark reached the limit the slice returned nothing for the rest of the game — at a different moment per seat, because each holds its own. That game's log ended at exactly the cap. Lines carry a sequence number now, which survives trimming; proven by pushing twice the cap through a simulated seat. §8.1 ASKED THE WRONG QUESTION TWICE. "Trains may pass" returned `clear` before the Subdivision was looked at, so a train entering a Double Track was released however busy the rest of it was — that, not anything about Control Points, is what let Train 8 out with no ruling. And a train standing at an Office was invisible to the scan, so one about to re-enter the very Subdivision being entered counted for nothing. Capacity is the test, not presence: a Depot with a track free is not in the way; a Whistle Post with its one track taken is. THINGS THAT HAPPENED SILENTLY NOW SAY SO — a train held against a facing one, a train released from the Limits (a side effect of somebody else's arrival, so it simply appeared at the Office), and the train an Interlocking is holding, whose explanatory tooltip has existed since #99 with NO renderer ever reading the flag. WHERE A MOVE IS REFUSED, AND WHY. `exploreMoves` decides where the rails go and the pick-up restrictions are enforced afterwards in `check`, so a square the rails reached and the card forbade was reachable, un-offered, and absent from the block list with nothing said. Those squares are blocked with the rule that blocks them now, and the reasons are got by ASKING `check` rather than re-deriving: a second implementation of the rules is exactly the failure the block list exists to avoid. A train may also always recover its own caboose — X13 prints "may drop but not pick up anything", and a train needs its caboose to be made up, so one that parted with it could never legally leave again. RULES DECIDED IN SEPTEMBER AND APPLIED HERE. A Modifier must sit square against its host, no diagonals. A passenger Modifier may not be played at a Whistle Post. Both were built, measured, held back for a fortnight so a playtest could finish, and applied now. A Second Section costs its card: `SECOND_SECTION` was declared in content.ts and never dealt, so the action was free and the bot ordered 26 accidental ones in a measured round. The card is dealt and spent — gating on a card the deck never holds would have deleted the mechanic rather than fixed it. THE DOCUMENTATION IS A SET OF PAGES, not five text files served as text/plain — a card reference is mostly tables, and as plain text a table is rows of pipes. Markdown is still the one copy; the build renders it, and publishes the .md beside each page. No Markdown library: this project has no runtime dependencies and one would be a poor first. The pages add what Markdown cannot carry without drifting — a nav across the set, a contents list built from the headings actually rendered, an anchor on every heading, a 70-character measure, and tables that are tables. They print as ink on paper. The references caught up with the rules, checked rather than assumed: two statements had gone from stale to misleading (the Quickstart told a new player to "get a Depot down as soon as one appears"), and four rules nobody could look up are written down — the Office tier table, §8.1 in practice, what the Circus Train pays for, and that a Realignment can be a card with no legal target. Adding one card to the deck reshuffles every seeded deal, which broke five fixtures. Each was a seed meaning "a game like this" — TODO #84, exactly — so seeds moved and pools widened rather than assertions weakening, and the clearance fixture pins its terrain the way `enhancements.test.ts` already does. The three published replays were re-recorded. Closes TODO #40, #42a, #108, #109 and #110. 1046 fast tests and 35 sim tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUizFYCMHRWhbWwXhp7WPR
This commit is contained in:
co-authored by
Claude Opus 5
parent
517238a727
commit
3befc420da
+184
@@ -19,6 +19,190 @@ page as `v0.1.0 · <sha> · <date>`, so what is deployed can always be identifie
|
||||
|
||||
---
|
||||
|
||||
## 0.8.2 — 2026-09-23
|
||||
|
||||
A playtest read back against the save file, and the rules that came out of it. Nine questions were
|
||||
asked of one three-Day game; three were bugs, three were the rules working and undocumented, and
|
||||
three were decisions. **Every save on the test server was replayed against this build before
|
||||
release**, which is how the cost of each rule was known before it was chosen.
|
||||
|
||||
### Every district opens on a Depot
|
||||
|
||||
**The single biggest change.** A Whistle Post has one A/D track and is not a Passenger Facility, so
|
||||
the opening of every game was spent unable to work a passenger and one arrival away from a
|
||||
collision. Every district opens on a **Depot** now: two A/D tracks, passengers from Stage 1.
|
||||
|
||||
**"Players start with Whistle Posts, not Depots"** is a setting for a table that wants the harder
|
||||
game. The deck follows the choice — starting on Depots, the four **Depot upgrade cards are left out**,
|
||||
because an upgrade must be to the next tier and a Depot card at a table of Depots is a dead draw.
|
||||
|
||||
How much easier it is showed up in a test rather than an argument: the cue-coverage pool needed
|
||||
widening from 24 seeded games to 60 before it contained a single collision.
|
||||
|
||||
**NO SAVE WAS STRANDED BY IT**, which took care. This is the one house rule that changes how a game
|
||||
is DEALT rather than how it plays, so replaying a save under the wrong opening is a different
|
||||
railroad from intent one — silently. Saves written before the setting existed name the rules that
|
||||
existed then and cannot name this one, so `withSavedOpening` fills it on the replay paths only.
|
||||
Putting it in the resolver instead made a fresh Cutthroat game deal Whistle Posts and read as
|
||||
Custom, which is how the distinction was found.
|
||||
|
||||
### An Office held two trains on one A/D track
|
||||
|
||||
Reported from the table and confirmed on the save: at Day 2 Stage 9 a Whistle Post with one A/D
|
||||
track held Trains 8 and 19 at once.
|
||||
|
||||
An ordering fault in `arriveAtOffice`. The capacity test passed with nothing standing, the train the
|
||||
Interlocking had been holding at the Limits was then moved into the free slot, and the arriving
|
||||
train was pushed in after it — without anyone asking again whether there was room. **So the
|
||||
collision §8.3 calls for never happened.**
|
||||
|
||||
The held train keeps its priority, because it has been waiting. The NEWCOMER takes the consequence,
|
||||
and it is the same one it would have met had the held train arrived first: held at its own Limits
|
||||
where there is an Interlocking, a collision where there is not.
|
||||
|
||||
### The history froze, permanently, and the log cap was not really the cause
|
||||
|
||||
One player's history stopped gaining lines at Day 2 Stage 8 and the other's at Day 2 Stage 4, in a
|
||||
two-player game that never reached Day 5.
|
||||
|
||||
The log is trimmed to a limit — but each seat's "what have I sent you" bookmark was an **index** into
|
||||
that array. Once a seat's bookmark reached the limit the array never grew past it again, so the
|
||||
slice returned nothing for the rest of the game. Different moments per seat because each holds its
|
||||
own bookmark. That game's log ended at exactly the cap.
|
||||
|
||||
Raising the cap only delays it. Lines carry a **sequence number** now, which survives trimming, so
|
||||
the bookmark stays meaningful however much is dropped. Proven by pushing twice the cap through a
|
||||
simulated seat and asserting all of it arrives. The limit is also much larger, on its own merits: a
|
||||
four-player game over ten Days is several times the game that first hit it.
|
||||
|
||||
### §8.1 asked the wrong question twice
|
||||
|
||||
**"Trains may pass" was short-circuiting the whole Subdivision.** The check returned `clear` before
|
||||
the Subdivision was looked at, so a train entering a Double Track was released however busy the rest
|
||||
of it was — including against a train coming the other way three cards deeper in. That is what let
|
||||
Train 8 out of the Western Division Point with no ruling asked, and the reason had nothing to do
|
||||
with Control Points. The card prints that TWO TRAINS MAY SHARE IT, so it excuses occupants on that
|
||||
card and nothing else.
|
||||
|
||||
**A train standing at an Office was invisible to it.** Train 19 was released from the Eastern
|
||||
Division Point towards Train 14 and nobody was asked — because at that moment Train 14 was not in
|
||||
transit at all, it was standing in a district. §8.1 was only ever reading trains on Mainline cards,
|
||||
so a train about to re-enter the very Subdivision being entered counted for nothing.
|
||||
|
||||
**Capacity is the test, not presence** (Jesse's reasoning exactly): at a Whistle Post, one A/D track
|
||||
with a train on it means there is nowhere for the two to pass and no choice to be made. At a Depot
|
||||
or a Terminal with a track still free, the train at the Office is not in the way.
|
||||
|
||||
### Things that happened silently now say so
|
||||
|
||||
**A train held against a facing one.** An absolute bar that returned without a word — the train
|
||||
simply did not depart, Stage after Stage. Only the ABS Signals case announced itself, and it had
|
||||
been given a line for exactly this reason. The line names the train that is coming and says there is
|
||||
no Control Point between them to pass at.
|
||||
|
||||
**A train released from the Limits.** It happened as a side effect of somebody else's arrival, so
|
||||
the held train appeared at the Office with nothing said — "wasn't clear what changed and why train 8
|
||||
was suddenly released". The line names the train whose arrival freed the track, because *why now* is
|
||||
the whole question.
|
||||
|
||||
**A train the Interlocking is holding.** The tooltip explaining it has existed since #99 and **no
|
||||
renderer ever read the flag**, so the train drew like any other crew and nothing told a player to
|
||||
hover. It is drawn held now — red and dashed, the same "stopped, and not by choice" the Red Flag
|
||||
means elsewhere on the map.
|
||||
|
||||
### Where a move is refused, and why
|
||||
|
||||
Asked directly: *"how does a user know what rule is violated and why you can't go there?"* Nowhere,
|
||||
was the answer. `exploreMoves` decides where the rails go, and the pick-up restrictions are enforced
|
||||
afterwards in `check` — so a square the rails reached and the card forbade was reachable,
|
||||
un-offered, and absent from the block list with no reason given.
|
||||
|
||||
Those squares are blocked with the rule that blocks them now, as `cardRule`. The reasons are got by
|
||||
**asking `check`**, not by re-deriving the rules: a second implementation is exactly the failure the
|
||||
block list was built to avoid, and a reason that does not match the refusal is worse than none.
|
||||
|
||||
**And a train may always recover its own caboose.** X13 prints "may drop MTs but not pick up
|
||||
anything", and a train needs its caboose at the far end to be made up — so a train that parted with
|
||||
its caboose could never legally leave again. It stranded itself, permanently and silently. The
|
||||
caboose only, not "your own cars" generally: it is the one car whose absence stops the train
|
||||
departing.
|
||||
|
||||
### Two Modifier rules, decided in September and applied here
|
||||
|
||||
**A Modifier must sit square against its host** — north, south, east or west. No diagonals: touching
|
||||
at a corner is not touching. This reverses an earlier report in the other direction, and the test
|
||||
that asserted the old rule now asserts the new one on the very square that prompted it.
|
||||
|
||||
**A passenger Modifier may not be played at a Whistle Post.** A Waiting Area, Restaurant or Hotel
|
||||
needs an Office upgraded to at least a Depot. It reverses the ruling that let them stand dormant: a
|
||||
Whistle Post allows neither direction, so the outbound slot was discarded on the spot and only the
|
||||
porter landed, and a card that can be played to no effect is a trap however well it is labelled.
|
||||
The grant-recovery path in `officeUpgraded` is kept and is now unreachable by play.
|
||||
|
||||
Both were built, measured, backed out for a fortnight so a playtest could finish, and applied here.
|
||||
The cost is known rather than guessed: of the thirteen saves on the test server, the Whistle Post
|
||||
rule is what strands six of them.
|
||||
|
||||
### The documentation is a set of pages now, not five text files
|
||||
|
||||
They were served as `text/plain`, which is honest and unreadable: a card reference is mostly tables,
|
||||
and as plain text a table is rows of pipes. That was TODO #109, taken deliberately as the short
|
||||
version to get the references in front of testers for one round.
|
||||
|
||||
**Markdown is still the one copy.** `docs/*.md` is what is written and reviewed; the build renders
|
||||
it. A hand-written HTML twin drifts on the first edit, which is the whole lesson of #15a. The
|
||||
Markdown is published beside each page too — it costs nothing, it is what a reader wanting a diff
|
||||
actually wants, and it keeps every link handed out while the documents were text working.
|
||||
|
||||
**No Markdown library.** `scripts/markdown.ts` covers the subset these five documents use. This
|
||||
project has no runtime dependencies at all and one would be a poor first — and the renderer is
|
||||
ten tests' worth of behaviour, not a general-purpose parser: it escapes unconditionally, and there
|
||||
is no raw-HTML passthrough.
|
||||
|
||||
What the page adds over the text, each because the Markdown cannot carry it without drifting: a nav
|
||||
across the five documents; a contents list built from the headings actually rendered; an anchor on
|
||||
every heading, so a section can be linked in a bug report; a ~70-character measure, because long
|
||||
lines are the single biggest thing making long documents hard to read; and tables that are tables,
|
||||
with numeric columns right-aligned and wide ones scrolling inside the page rather than widening it
|
||||
on a phone. It prints as ink on paper, nav and contents dropped — a rules reference is a thing
|
||||
people print.
|
||||
|
||||
### The references caught up with the rules
|
||||
|
||||
Checked against this release rather than assumed, and two statements had gone from stale to
|
||||
misleading. The Quickstart told a new player they start on a **Whistle Post** and to "get a Depot
|
||||
down as soon as one appears" — advice for a game that no longer exists. Components listed "Whistle
|
||||
Post cards ×4" as what everyone starts on.
|
||||
|
||||
Also added, because the playtest showed each was a rule nobody could look up: the **Office tier
|
||||
table** (A/D tracks, Porters, passengers, Control Point) and what A/D tracks decide; **§8.1 in
|
||||
practice**, including that a card printing "trains may pass" excuses only that card and that an
|
||||
Office with no free A/D track occupies the Subdivision; the three conditions the **Circus Train**
|
||||
pays on; and that a **Realignment can be a card with no legal target**, which is exactly what
|
||||
happened at the table.
|
||||
|
||||
### Smaller, from the same session
|
||||
|
||||
**An industry may be built over a straight** off the Running Track, the way a turnout may upgrade
|
||||
one — so rail can go down before the industry that will serve it. Only a plain straight: a Facility
|
||||
carries east–west track, so the swap cannot break a neighbour's join, where a curve or turnout
|
||||
could.
|
||||
|
||||
**Revenue lines name the player who earned them.** The history prefixes the ACTOR, and revenue is
|
||||
not always the actor's — a train completing its run pays everybody with no actor at all, so those
|
||||
lines carried no name whatsoever.
|
||||
|
||||
**"Waiting on" flashes when it is your turn**, amber, reading the move on screen rather than the
|
||||
live one so it does not flash while your board is still catching up.
|
||||
|
||||
**The Circus Train says what it pays for** on its own line while it is being made up, instead of as a
|
||||
clause trailing the consist — it pays for STOPPING, once per district, and only if every car but the
|
||||
caboose is loaded. The history line says the same when the point lands.
|
||||
|
||||
**A Realignment says what it can convert.** "Convert one Mainline type to another" is true and
|
||||
useless when only four of the nine types convert at all and the Division may have dealt none of
|
||||
them — which is exactly what happened.
|
||||
|
||||
## 0.8.1.0 — 2026-09-22
|
||||
|
||||
A second-digit bump, and a deliberate one. **0.8.1 had been reserved for the seatless display
|
||||
|
||||
Reference in New Issue
Block a user