adding classification and division yard updates, train collisions, engine and car updates, turnout improvements.

This commit is contained in:
Jesse
2026-08-04 21:03:17 -04:00
parent 4bba991f41
commit 821d3cdffa
26 changed files with 6876 additions and 2638 deletions
+43 -17
View File
@@ -9,16 +9,45 @@ Ordered within each section by how much it is currently costing us.
## Next
- [ ] **A "load" is stored as a car, so rolling stock cannot be counted.** `outboundBox`,
`inboundBox` and `menAtWork` all hold `RollingStock`, and `freightAgent.stockOutbound` takes a
LOADED CAR out of the Division Yard to fill a green box. So a census of every holder comes to
92 against the 80 dealt at setup — not necessarily duplication, because some of those objects
are cargo in transit rather than cars, but there is no way to tell them apart. Until a load is
its own type, "is any stock being created or destroyed?" is an unanswerable question, and the
supply numbers below cannot be tuned with confidence.
- [ ] **Engines are not a SUPPLY yet, only a position.** `engineAt` now records where the engine
sits in the tray and the consist shows it, but an engine is still conjured with the tray
rather than drawn from the Division Yard and returned to it. The rules put engines in the
Division Yard alongside the cars, with a predefined number of them, so running out of engines
should be a second way trains get held — today only the Crew Tray count does that. Needs a
number to start from, then playtesting.
- [ ] **The yards are shown on the play page but not in either replay viewer.** The Frame carries
them, so it is a rendering job, not a modelling one.
- [ ] **The rolling stock supply is a guess.** `ROLLING_STOCK_SUPPLY` (coach 8+8, boxcar 10+10,
hopper 8+8, reefer 5+5, tank 6+6, caboose 6) is marked provisional in `content.ts` and was
scaled alongside the Gap 12 industry increase. Now that the Classification Yard returns stock
only when the Division Yard empties, these numbers set the real supply pressure. Adjust from
playtesting rather than theory, and watch whether industry density feels light or heavy at the
same time.
- [ ] **Heavy Grade orientation is rolled, not chosen.** The card prints "Player sets orientation",
but it is dealt during setup and setup has no decision point at all — `createGame` is a pure
function of the seed, which is also what makes a save portable. Rolled from the seed for now.
Revisit when setup gains an interactive phase; the orientation matters, because it decides
which direction climbs and therefore what Brakeman and Helpers are worth.
- [ ] **Measure with error bars from now on.** Revenue has a standard deviation of ~9, so a
100-game run carries about ±1.0 of noise — every single-change revenue claim in the changelog
before the Interlocking work is inside that. Use paired per-seed comparison (the harness deals
the same seeds either way) and 400+ games before calling a heuristic change good or bad. The
first attempt at the Running Track straight was read as a 0.6 REGRESSION on 100 games and is
a 0.7 improvement on 400.
- [ ] **Confirm the Classification Yard rule against the source.** Cars now return to the Division
Yard at the Day boundary — an ASSUMPTION, not a recovered rule. Gap 2c says everything but
cabooses goes to Classification and never says how it empties. It is worth +2.32 revenue a
game, so if the real rule differs the balance numbers move with it.
- [x] ~~**Confirm the Classification Yard rule against the source.**~~ Confirmed, and the guess was
wrong. The rule is: used Rolling Stock to the Classification Yard, used engines and cabooses
straight back to the Division Yard, and the Classification Yard empties ONLY when the Division
Yard is bare — then all at once. The Day-boundary version I had invented was far more generous
and worth **+2.42 revenue a game the game does not actually grant**. Corrected; revenue 9.67
→ 7.25.
- [x] ~~**Enhancements are placed but mostly do nothing.**~~ Measured: forbidding every Enhancement
except Interlocking is worth **-0.01 ± 0.41 (t = -0.04)** over 400 paired seeds. They neither
pay nor cost. Left alone. Unlocking the Running Track straight put
@@ -42,11 +71,8 @@ Ordered within each section by how much it is currently costing us.
needs a stocked green box AND a spotted car AND a free Laborer to line up in the same Stage.
Measure how many Stages have all three before changing any heuristic — the answer may be that
the economy, not the bot, is what caps freight.
- [ ] **`stats.ts` undercounts freight.** `rev.freightUnload` is assigned `eventCounts['unloadBegan']`
— unloads *begun*, not revenue *earned* — and `grossFreight` uses `freightLoad` alone, so
`freightShare` omits unload revenue entirely. The comment justifying it ("an unload scores
through the same event as a load completion") is wrong: `apply.ts:947` emits a distinct
`freightUnload` reason. This is why freight was recorded at 13–18% of gross.
- [x] ~~**`stats.ts` undercounts freight.**~~ Fixed: both halves counted, freight share 39% → 49%.
Worth revisiting the **industry density** decision below, which was taken on the old number.
---
@@ -54,13 +80,9 @@ Ordered within each section by how much it is currently costing us.
Blocked on a decision, not on work.
- [ ] **Q13 — rear-end collisions on a Mainline card.** §10 says a Mainline collision is the
Superintendent's fault and removes both trains, and ABS Signals exists to prevent rear-enders —
but §8.3's trigger list does not include one, and **none is implemented**. Granting clearance is
currently free: verified, both trains survive, no penalty. Three candidates: collide on entry
(clearance becomes a gamble), collide on catching up (rewards judging the gap), or accept that
clearance is safe and ABS Signals is worth less than it reads. The buttons currently describe
only what the engine does, so nothing promises a consequence that cannot happen.
- [x] ~~**Q13 — rear-end collisions on a Mainline card.**~~ Answered: collide on catching up.
Implemented, and not on cards that print "trains may pass". Invisible to a bot that always
denies clearance; a bot that always allows drops from 7.34 revenue to **-5.13**.
- [ ] **Poling.** The only card in the deck with no defined behaviour — the sheet records its effect
as "TBD in the source". A test asserts it stays TBD so nobody invents one.
- [ ] **Heavy Grade orientation at setup.** The card says "Player sets orientation", but `createGame`
@@ -165,7 +187,11 @@ target is settled and freight carries its intended share.
- [ ] **No way to start a fresh game from inside the page.** `?seed=` gives a reproducible deal and
"new game" only appears once a game has ended, so abandoning a bad opening means editing the URL.
- [ ] **Save/restore is not version-aware.** A save from an older ruleset stops replaying rather than
failing loudly, which is the safe direction but says little about what changed.
failing loudly, which is the safe direction but says little about what changed. **This has now
bitten once**: both published replays were dead — one got 42 intents into 360, the other 4 of
338 — and nothing said so; they simply ended early and looked like short games. A save should
carry a ruleset stamp and the page should say "this replay was recorded under an older
ruleset and stops at Stage N" rather than presenting a truncated game as a whole one.
---