v0.6.1 — five of six playtest bugs: one button per train, and a load that has to go somewhere
Gameplay testing on 0.4.9d returned six reports. Five are fixed; the sixth could not be reproduced and is written up in TODO.md with the two questions that would pin it down. TWO TRAINS AT ONE PLATFORM ANSWERED TO ONE BUTTON. `porter.board` and `porter.detrain` carried no tray, so there was one button per platform however many trains stood at it and the reducer filled the first empty coach on the A/D tracks. `check` and the reducer were not even asking the same question: `check` skipped a train whose card refuses passenger work and the reducer did not. Both intents now carry an optional `trayId`, one function resolves the train and the coach for check/execute/reduce alike, `legal.ts` offers one candidate per train, and the label names it. A LOAD COULD BE MADE AND BROKEN WITHOUT GOING ANYWHERE. A Freight House could unload the boxcar it had just loaded; a platform could detrain the passengers it had just boarded. Full Revenue at both ends for a movement that never happened. Jesse's rule: a load made anywhere in an Office Area may not be broken anywhere in that Office Area, ever — it has to be carried to another district. The load carries the seat that made it (`RollingStock.origin`), stripped by `pooled` at every yard push. Measured at -0.60 +/- 0.10 Revenue a game (t = -6.1) over 400 paired deals: 78 worse, 3 better, 319 unchanged — free Revenue coming off the board, not a nerf. THE GROCER'S WAREHOUSE SHIPPED AND THE REFINERY RECEIVED. Both were `flow: 'both'` on the reading that "Freight House" was a collective term for exactly those two, and therefore what §9.3 described. The engine has dealt a Freight House CARD since before v0.4.9, so §9.3 names it and the argument goes. The card set agrees: all three Refinery modifiers grant +1 outbound. Refinery outbound-only, Grocer's inbound-only, Freight House the one two-way industry — which leaves exactly the one same-district pairing the rule above refuses. NOT REPRODUCED: cars left behind when backing up over them. Five layouts tried, including cars spotted at an industry; every one couples the lot. Three are pinned in `apply.test.ts`. One way to create such cars was closed anyway — `flyingSwitch` wrote its cut past `carsOn`. Both published replays that had gone dead were re-recorded; a rules change retires a save, and `harness.test.ts` is what catches it. The same change ships as v0.4.9e on the 0.4.9 line, branched from the v0.4.9d commit — the engine files these fixes touch are identical across the two lines, so the patch applied cleanly both ways. Also carries the two "Queued 2026-08-22, from playing on StartOS" TODO items that were staged before this work started (Games in Progress readability, and getting back into a game after losing a browser). They are notes, and items 9-12 below them are numbered against them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011nbvwWMef8CuEP6t5cgkTv
This commit is contained in:
co-authored by
Claude Opus 5
parent
40f07b0710
commit
83a5450866
+41
-1
@@ -55,7 +55,47 @@ export function coordKey(c: GridCoord): string {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** §2.2 — a coloured car is loaded, a white car is empty. */
|
||||
export type RollingStock = { type: CarType; loaded: boolean };
|
||||
export type RollingStock = {
|
||||
type: CarType;
|
||||
loaded: boolean;
|
||||
/**
|
||||
* WHICH OFFICE AREA MADE THIS LOAD — the physical game's chip turned upside down in the tray.
|
||||
*
|
||||
* Reported from playtesting v0.4.9d as two bugs with one cause: a boxcar loaded at a Freight
|
||||
* House could be unloaded at that same Freight House on the next Laborer action, and passengers
|
||||
* who had just boarded could be detrained again before the train turned a wheel. Both paid full
|
||||
* Revenue at each end for a load that never went anywhere.
|
||||
*
|
||||
* Jesse's rule (v0.4.9e): freight or passengers loaded anywhere in an Office Area may not be
|
||||
* unloaded ANYWHERE in that same Office Area — not at another facility, not in a later Stage.
|
||||
* They have to be carried by a train to a different Office Area. So the stamp is the SEAT, which
|
||||
* is what an Office Area belongs to (Employee Rotation moves players between chairs; the district
|
||||
* stays with the chair), and it never expires.
|
||||
*
|
||||
* A SEAT, NOT A PLAYER, and undefined rather than -1 for "no origin": the Division Yard opens with
|
||||
* loaded cars and loaded coaches that were made up off-Division (`ROLLING_STOCK_SUPPLY`), and
|
||||
* those are exactly the inbound traffic a solitaire district lives on. A sentinel inside
|
||||
* `SeatIndex`'s own value range is not a sentinel — see `card.play`'s `node` in intents.ts.
|
||||
*
|
||||
* Stripped by `pooled` whenever a car goes back to a yard: the stamp belongs to the LOAD, and a
|
||||
* car returning to the common supply is carrying nothing.
|
||||
*/
|
||||
origin?: SeatIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* A car returning to the common pool — the Division or Classification Yard — with its load's origin
|
||||
* stamp taken off.
|
||||
*
|
||||
* Every yard push goes through this. A loaded car CAN reach a yard still loaded (a train retires at
|
||||
* a Division Point with freight aboard, `advance.ts`), and without this it would carry a stamp from
|
||||
* a district it left several Days ago into whatever train is made up from it next.
|
||||
*/
|
||||
export function pooled(car: RollingStock): RollingStock {
|
||||
if (car.origin === undefined) return car;
|
||||
const { origin: _origin, ...rest } = car;
|
||||
return rest;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Track and Office Area
|
||||
|
||||
Reference in New Issue
Block a user