user card Hand becomes the action surface, the board shows the train, and a reverse stops turning it around, other bug fixes too.

This commit is contained in:
Jesse
2026-08-05 19:18:26 -04:00
parent 1c321d4cea
commit 543584a2ca
20 changed files with 1851 additions and 320 deletions
+16 -5
View File
@@ -254,8 +254,12 @@ describe('replay HTML', () => {
() => run(stub, () => 0, () => undefined),
'the page threw while rendering its first frame',
);
// render() ran, so the clock element should have been filled in.
assert.match(String(els.get('when')?.textContent ?? ''), /Day \d+/);
// render() ran, so the turn chart should have been filled in — with the same Day/Stage/phase
// chart the live game draws, from the shared renderer.
const chart = String(els.get('turnchart')?.innerHTML ?? '');
assert.match(chart, /Day \d+/, 'the turn chart was not rendered');
assert.match(chart, /Stage \d+ of 12/);
assert.match(chart, /class="tc-phase now"/, 'no phase is marked as the current one');
});
it('records what the bot chose, why, and what it passed over', () => {
@@ -394,16 +398,23 @@ describe('the replay behaves like the game it is replaying', () => {
assert.ok(kinds.has('stage'), 'no Stage ever sounded');
assert.ok(kinds.has('day'), 'no Day ever sounded');
assert.ok(kinds.has('train'), 'no train was ever announced');
// Coupling and setting out are what a switching move IS, and both were silent — a car left the
// board and only history said where it went.
assert.ok(kinds.has('couple'), 'coupling a cut never made a sound');
assert.ok(kinds.has('drop'), 'setting cars out never made a sound');
// One cue per Stage boundary, the bell replacing the whistle at a Day — the same accounting the
// live game is held to.
// One CLOCK cue per Stage boundary, the bell replacing the whistle at a Day — the same
// accounting the live game is held to. The switching cues are counted separately: they follow
// what a crew does, not what the clock does.
let boundaries = 0;
for (let k = 1; k < rec.frames.length; k++) {
const a = rec.frames[k - 1]!;
const b = rec.frames[k]!;
if (a.day !== b.day || a.stage !== b.stage) boundaries++;
}
const clock = withCues.flatMap((f) => f.cues ?? []).filter((c) => c !== 'train').length;
const clock = withCues
.flatMap((f) => f.cues ?? [])
.filter((c) => c === 'stage' || c === 'day').length;
assert.equal(clock, boundaries, 'Stage boundaries and Stage-or-Day cues disagree');
});