import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { areaOf } from '../src/engine/apply.ts'; import { createGame } from '../src/engine/setup.ts'; import { coordKey } from '../src/engine/state.ts'; import type { GameConfig, GameState } from '../src/engine/state.ts'; import { applyDelta, deltaFrame } from '../src/sim/frame-delta.ts'; import { snapshot } from '../src/sim/view.ts'; const config: GameConfig = { mode: 'solitaire', days: 5, minCombinedRevenue: 0, maxCollisionsPerDay: 0, maxCollisionsTotal: 0, pvpCardsAllowed: false, // These fixtures were written against a Whistle Post opening — one A/D track and no // Control Point — and several of them test exactly that. Named explicitly since the // default became a Depot. houseRules: { startingOffice: 'whistlePost' }, optionalRules: { reducedVisibility: false, employeeRotation: false, emergencyToolbox: false, }, }; const game = (seed = 1): GameState => createGame({ id: 'g', seed, config, playerNames: ['a'] }); describe('deltaFrame — the live one-step-back board diff', () => { it('nulls cells/facilities/division when the board has not changed, keeps everything else', () => { const s = game(); const first = snapshot(s, [], null); s.players[0]!.revenue = 42; // a non-board change const second = snapshot(s, [], null); const delta = deltaFrame(first, second); assert.equal(delta.cells, null, 'unchanged cells were not nulled'); assert.equal(delta.facilities, null, 'unchanged facilities were not nulled'); assert.equal(delta.division, null, 'unchanged division was not nulled'); assert.equal(delta.revenue, 42, 'a real change was dropped along with the unchanged board'); }); it('sends the real board when it changed', () => { const s = game(); const first = snapshot(s, [], null); const area = areaOf(s, 0); area.grid.set(coordKey({ row: area.runningRow - 1, col: 0 }), { geometry: { kind: 'track', geometry: 'straight' }, baseOperationalRail: true, standing: [], standingWest: 0, facility: null, modifiers: [], enhancements: [], }); const second = snapshot(s, [], null); const delta = deltaFrame(first, second); assert.notEqual(delta.cells, null, 'a real board change was nulled'); assert.deepEqual(delta.cells, second.cells); }); it('never nulls anything on a first connect (no previous Frame)', () => { const s = game(); const frame = snapshot(s, [], null); const delta = deltaFrame(null, frame); assert.deepEqual(delta.cells, frame.cells); assert.deepEqual(delta.facilities, frame.facilities); assert.deepEqual(delta.division, frame.division); }); it('applyDelta round-trips: merge(delta(A, B)) against A reconstructs B exactly', () => { const s = game(); const a = snapshot(s, [], null); s.players[0]!.revenue = 7; const area = areaOf(s, 0); area.grid.set(coordKey({ row: area.runningRow - 1, col: 1 }), { geometry: { kind: 'track', geometry: 'straight' }, baseOperationalRail: true, standing: [], standingWest: 0, facility: null, modifiers: [], enhancements: [], }); const b = snapshot(s, [], null); const merged = applyDelta(a, deltaFrame(a, b)); assert.deepEqual(merged, b); }); it('throws rather than silently reconstructing garbage when previous is missing but the delta says "unchanged"', () => { const s = game(); const frame = snapshot(s, [], null); const delta = deltaFrame(frame, frame); // everything nulled, since nothing changed assert.throws(() => applyDelta(null, delta)); }); });