v0.4.3 fixed load/unload bugs. empty cars must be staged prior to loading. rolling stock is now conserved.

This commit is contained in:
Jesse
2026-08-14 08:33:32 -04:00
parent 393a669418
commit d1314066bd
13 changed files with 2258 additions and 3822 deletions
+15 -18
View File
@@ -122,24 +122,21 @@ Ordered within each section by how much it is currently costing us.
present it that way — the supply has to change, not the player.** Options: give track its own
hand or yard the way the prototype did, raise the hand limit for track specifically, or print a
siding as a single card. Nothing else reaches it.
- [ ] **CLEARING AN INBOUND BOX MINTS A CAR — measured at 1.29 a game against a supply of 80.**
Answered, and it is duplication after all. The two directions are not symmetrical:
- **Outbound is paid for.** `stockToOutbound` SPLICES a loaded car out of the Division Yard to
become the load, and `loadCompleted` swaps the emptied car into the Classification Yard as
the loaded one takes its place on the industry track. Objects in, objects out.
- **Inbound is not.** `unloadBegan` (apply.ts) turns one loaded car into an empty car on the
track **plus** a load on MEN|AT|WORK, and `passengersDetrained` does the same to a coach —
one loaded coach becomes an empty coach in the train plus an object in the red box. Then
`inboundCleared` pushes that object into the **Classification Yard as a car**. The cargo
becomes rolling stock, while the car it came out of is already back in service.
Measured over 200 games: cars at the end minus 80, plus collision losses, equals the
`inboundCleared` count in 71/200 games exactly and 258 against 279 in total — the rest is
loads still in flight at the final whistle. So the supply inflates by about 1.3 cars a game.
That is the number `ROLLING_STOCK_SUPPLY` is supposed to control, so **no supply figure below
can be tuned until this is settled**. The fix is a decision, not a patch: either a load stops
being a `RollingStock` and becomes its own type, or `inboundCleared` discards rather than
banking. Found by a conservation audit, not by a failing test.
- [x] **~~CLEARING AN INBOUND BOX MINTS A CAR — measured at 1.29 a game against a supply of 80.~~
Re-audited in v0.4.3: rolling stock is EXACTLY CONSERVED, 100 games out of 100, range 0..0.**
The old audit's premise was right — the two directions were not symmetrical — but the asymmetry
has since been closed from the other end. `unloadBegan` and `passengersDetrained` now take their
replacement empty OUT of the Division Yard rather than conjuring it, so a load is a car that
moved rather than a car that appeared: one leaves the yard, one arrives in Classification.
Jesse's description of the tabletop procedure confirms this is the intended model — the token
you push along the MEN|AT|WORK sign IS the car, fetched from the yard by the Freight Agent and
swapped onto the industry track at the end.
Both conjuring fallbacks now **throw** rather than minting, so the leak cannot silently return;
neither fired across the suite or 100 audited games.
**`ROLLING_STOCK_SUPPLY` is therefore unblocked** — it was waiting on this and can now be tuned
in the rebalance pass. (The first audit's own arithmetic was off in the same way mine was on the
first attempt: cars set out on a card, in `card.standing`, are easy to leave out of the count and
make a conserved game look like a leaking one.)
- [x] **`state = fold(events)` was not true, and the docs said it was. Settled: the INTENTS are
canonical.** Measured before deciding — `advance.ts` never calls `reduce`, so **14 of the 46
event types are never reduced**: the clock, and the entire Mainline phase, which is every train