v0.7.2 — a leg that is part of the row, the deck the sheet prints, regions not miles per hour, and a Division you read left to right
Gitea#17 — a 45° leg is an end of the west-to-east row, so backing into a cut through a curve's south leg no longer couples it back to front. The same assumption left a crew's own cut standing when it pulled out through a leg, which is the "cars left behind" report we had failed to reproduce. Gitea#14 — every count is docs/Deck cards5.xlsx. Track halved, and the Q12 office doubling and Gap 12 industry tripling both come out with it: they were measured against a deck with twice the track, and keeping them at the sheet's track count wipes out the reefer chain entirely. 84 rows now match card for card; the ten Safety, Event and Inspection cards it adds are not built and are held out. Cards the sheet no longer lists are dealt zero copies rather than deleted, so their rules stay implemented. Gitea#15 — RAR reversed it: a rail may stop dead against its neighbour and the placement is legal. What must hold is that no train crosses the gap, which was already true and is now pinned against the reported board. Gitea#3 — the printed speeds are scenery. A card costs one Stage per printed region and where a train STARTS is what varies; Fast/Slow is read on Hilly alone. Entering a one-region card behind another is a collision now, which is what ABS exists to prevent, and ABS no longer holds trains silently. Gitea#18 — the Division draws as one row, west to east, with no office-area detail. East is finally always to the right. Closes #3 Closes #14 Closes #15 Closes #17 Closes #18
This commit is contained in:
+22
-8
@@ -228,14 +228,28 @@ describe('the game conserves Rolling Stock', () => {
|
||||
for (let t = 0; t < 50_000; t++) {
|
||||
const before = census(s);
|
||||
const pumped = pump(s);
|
||||
// §10 — a collision destroys both trains and everything they were carrying. That is the one
|
||||
// legitimate way the count falls, so the expectation follows it down.
|
||||
for (const e of pumped) {
|
||||
if (e.type === 'trainsDestroyed') for (const tr of e.trains) expected -= tr.consist.length;
|
||||
}
|
||||
assert.ok(
|
||||
census(s) === before || pumped.some((e) => e.type === 'trainsDestroyed'),
|
||||
`seed ${seed}: the engine changed the census by ${census(s) - before} outside a collision`,
|
||||
/**
|
||||
* A COLLISION DESTROYS NO CAR, and this used to assume it destroyed all of them.
|
||||
*
|
||||
* The subtraction that stood here — `expected -= tr.consist.length` for every train in a
|
||||
* `trainsDestroyed` event — describes a rule the engine does not have. Gap 2c (`advance.ts`,
|
||||
* "TAKE THE WRECK OFF THE CARD") sends the wreck's cabooses back to the Division Yard and
|
||||
* everything else to Classification, so the stock is conserved through a collision like any
|
||||
* other move. The train is destroyed; its cars are not.
|
||||
*
|
||||
* It passed for as long as it did because none of the six seeds below ever collided, so the
|
||||
* branch never ran. Changing the deck to the sheet's counts (Gitea#14) moved the deals, seed
|
||||
* 24757 collided, and the test failed claiming the engine had CONJURED three cars — the
|
||||
* exact opposite of what had happened.
|
||||
*
|
||||
* So the census is now held flat, unconditionally, which is both the real invariant and a
|
||||
* stronger test than the one it replaces: there is no longer any event that excuses a
|
||||
* change, and `expected` cannot drift away from the supply it was dealt.
|
||||
*/
|
||||
assert.equal(
|
||||
census(s),
|
||||
before,
|
||||
`seed ${seed}: the engine changed the census by ${census(s) - before} while pumping`,
|
||||
);
|
||||
if (s.status === 'finished') break;
|
||||
const actor = s.clock.pendingDecision !== null ? s.clock.superintendent : s.clock.currentActor;
|
||||
|
||||
Reference in New Issue
Block a user