v0.4.0 — multiplayer Phases 0 and 1: seat and player split apart, turn state per player, the page behind a Session, and eight seat/player mix-ups fixed with tests that fail without them

This commit is contained in:
Jesse
2026-08-13 14:06:02 -04:00
parent 216006b091
commit 49f8504b05
34 changed files with 1743 additions and 526 deletions
+8 -8
View File
@@ -15,7 +15,7 @@ import type {
TrackCard,
TurnoutOrientation,
} from '../src/engine/state.ts';
import { coordKey, isOperationalRail } from '../src/engine/state.ts';
import { coordKey, isOperationalRail, turnOf } from '../src/engine/state.ts';
import { createGame } from '../src/engine/setup.ts';
import { applyIntent, areaOf } from '../src/engine/apply.ts';
import type { MoveContext, Occupancy, Port } from '../src/engine/track.ts';
@@ -134,7 +134,7 @@ function areaFrom(cards: Record<string, TrackCard>, officeCoord: GridCoord): Off
const grid = new Map<string, TrackCard>();
for (const [k, v] of Object.entries(cards)) grid.set(k, v);
return {
owner: 0,
seat: 0,
tier: 'whistlePost',
grid,
officeCoord,
@@ -682,12 +682,12 @@ describe('coupling lifts only the cars the crew ran over', () => {
// A local CREW, which is what this test is about — `trainNumber: null`. It used to say train 1,
// the Crack Limited, which prints "no switching" (§7) and may not make this move at all.
id: 'crew', trainNumber: null, trainIsExtra: false, engineAt: 0, consist: [],
direction: 'west', facing: 'w', position: { at: 'grid', owner: 0, coord: { row: 0, col: 0 } }, movesUsed: 0,
direction: 'west', facing: 'w', position: { at: 'grid', seat: 0, coord: { row: 0, col: 0 } }, movesUsed: 0,
});
s.clock.phase = 'localOps';
s.clock.currentActor = 0;
s.turn.option = 'switch';
s.turn.movesRemaining = 6;
turnOf(s, 0).option = 'switch';
turnOf(s, 0).movesRemaining = 6;
const r = applyIntent(s, 0, { type: 'switch.move', trayId: 'crew', to: { row: 0, col: -1 }, reverse: false });
assert.ok(r.ok, 'the move should be legal');
@@ -927,12 +927,12 @@ describe('backing up does not turn the train around', () => {
s.trays.set(id, {
id, trainNumber: null, trainIsExtra: false, engineAt: 0,
consist: [], direction: 'east', facing: 'e',
position: { at: 'grid', owner: 0, coord: at(0, 1) }, movesUsed: 0,
position: { at: 'grid', seat: 0, coord: at(0, 1) }, movesUsed: 0,
} as never);
s.clock.phase = 'localOps';
s.clock.currentActor = 0;
s.turn.option = 'switch';
s.turn.movesRemaining = 6;
turnOf(s, 0).option = 'switch';
turnOf(s, 0).movesRemaining = 6;
// Running forward, east: the engine leads, so it still points east.
assert.ok(applyIntent(s, 0, { type: 'switch.move', trayId: id, to: at(0, 2), reverse: false }).ok);