v0.4.9h — a leg that is part of the row, the deck the sheet prints, and regions not miles per hour

The 0.4.9 line's port of v0.7.2, minus Gitea#18: the Division map redraw is a
multiplayer redesign and does not belong in a build people are mid-playtest on.

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, and a crew
pulling out through a leg takes its own cut with it.

Gitea#14 — every count is docs/Deck cards5.xlsx, including removing the Q12
office doubling and the Gap 12 industry tripling.

Gitea#15 — RAR reversed it: the placement is legal; what must hold is that no
train crosses the gap.

Gitea#3 — the printed speeds are scenery. One Stage per printed region, and
where a train starts is what varies.
This commit is contained in:
Jesse.Markowitz
2026-08-26 15:21:49 -04:00
parent f684c60f2e
commit e9683cc8c5
22 changed files with 4079 additions and 3055 deletions
+27 -12
View File
@@ -115,19 +115,34 @@ describe('the Fedora goes round the table', () => {
* decides who sits where, the sequence of player indices is a permutation while the sequence of
* seats is the plain 0, 1, 2, … that the rule describes.
*/
/**
* THE SEED IS SEARCHED FOR, because a game that ends on Day 1 cannot show a rotation.
*
* This took seed 4242 flat. Gitea#14's deck counts re-dealt it, and the three-player game now
* loses on `collisionFloor` at Day 1 Stage 10 — so the Fedora legitimately moves twice and the
* test failed reporting a rotation bug that is not there. Whether a particular deal ends early on
* collisions has nothing to do with whether the Fedora passes to the next seat.
*
* So the fixture asks for what it actually needs: a game that runs far enough to watch. It takes
* the first seed whose game reaches Day 3, which is four shift changes at minimum, and reports
* honestly if no seed in range does.
*/
const superintendentsSeen = (players: number): number[] => {
const s = game(players);
const seen: number[] = [];
const spy = {
name: 'spy',
choose(st: GameState, p: PlayerIndex, opts: Parameters<typeof developerBot.choose>[2]) {
const who = seatOf(st, st.clock.superintendent);
if (seen[seen.length - 1] !== who) seen.push(who);
return developerBot.choose(st, p, opts);
},
};
playGame(s, spy as never, pump);
return seen;
for (let seed = 4242; seed < 4342; seed++) {
const s = game(players, seed);
const seen: number[] = [];
const spy = {
name: 'spy',
choose(st: GameState, p: PlayerIndex, opts: Parameters<typeof developerBot.choose>[2]) {
const who = seatOf(st, st.clock.superintendent);
if (seen[seen.length - 1] !== who) seen.push(who);
return developerBot.choose(st, p, opts);
},
};
playGame(s, spy as never, pump);
if (s.clock.day >= 3) return seen;
}
assert.fail(`${players}p: no seed in range produced a game that reached Day 3`);
};
it('passes the Superintendent to the next seat, wrapping round the table', () => {