Add an offline Ix initializer through the real setup pipeline, supporting Basic and Advanced play, base factions, Ixians and Tleilaxu with the full 47-card deck and 22-card spice deck. Preserve all normal start and publication gates. Add a reusable local prototype starter: back up the named fresh ready lobby, initialize it once by exact version/state comparison and preserve seat/recovery tables and other rooms. Update the existing checklist with a separate prototype stage and document functional scope, source interpretations and later refinement. Six new tests cover all-profile private setup, exact typed starting forces, Face Dancer/treachery custody, JSON continuation, two fixed-seed complete games and local SQLite write/rejection boundaries. Independent review caught a missing JSON version update, now fixed and covered; focused checking fixed one test typing error before the stable broad check. No new rule interpretation was introduced. Validation: types, lint and 4,211 offline tests passed (123.237s); production build passed (6.543s); all 40 HTTP tests passed (23.578s). Source-bound report: 20260913-ix-prototype-final/report.json, fingerprint 3b8f295593bcb314bb72308d81739006b11d949d1e5906dbf6ce0704b15ebd72. Browser W5LE6VFB: human Ixian + Medium Tleilaxu + Hard Atreides from genuine setup, private starting-card refresh, Weather Control, mobile stronghold placement, then owned Brutal autopilot. Atreides won on turn 1; this short complete journey is not proof of every card or faction interaction. Finished private hand restored at phone width; expanded Poison Tooth was readable. Fixed-seed offline games 2026091301 (Basic) and 2026091302 (Advanced) both completed with both Ix factions. All 64 opening room versions/hashes are unchanged. One browser room and 30 HTTP rooms were added, totaling 95. Reused the running server; no restart or automation. Aggregate goal counters: 59,524,858 to 59,976,567 at completed verification, 451,709 tokens before final Git overhead. Two bounded workhorse helpers handled inventory/tests and independent review; one stable full check/build/HTTP run. Full game scope remains active and unfinished; future work prioritizes missing functional prototypes before exhaustive refinement and final acceptance.
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import type { DatabaseSync } from 'node:sqlite';
|
|
import { initializeIxGameForAudit, viewGame, type Game } from '../game/engine';
|
|
|
|
/** Local development only. This cannot create a seat, change its owner, or
|
|
* redeal a started game. The caller backs up the database before invoking it. */
|
|
export function startIxPrototypeRoom(
|
|
db: DatabaseSync,
|
|
code: string,
|
|
expectedVersion: number,
|
|
) {
|
|
if (
|
|
!/^[A-Z0-9]{8}$/.test(code) ||
|
|
!Number.isSafeInteger(expectedVersion) ||
|
|
expectedVersion < 0
|
|
)
|
|
throw new Error('Provide an eight-character room and its current version.');
|
|
const row = db
|
|
.prepare('SELECT state, version FROM rooms WHERE code = ?')
|
|
.get(code);
|
|
if (!row || typeof row.state !== 'string' || row.version !== expectedVersion)
|
|
throw new Error(
|
|
'The room is missing or changed. Refresh before starting the prototype.',
|
|
);
|
|
const initial = JSON.parse(row.state) as Game;
|
|
if (initial.code !== code)
|
|
throw new Error('The room identity is inconsistent.');
|
|
const game = initializeIxGameForAudit(initial);
|
|
// Exercise the same player projection before accepting the new saved state.
|
|
for (const player of game.players) viewGame(game, player.id);
|
|
const version = expectedVersion + 1;
|
|
game.version = version;
|
|
const result = db
|
|
.prepare(
|
|
'UPDATE rooms SET state = ?, version = ?, updated_at = ? WHERE code = ? AND version = ? AND state = ?',
|
|
)
|
|
.run(
|
|
JSON.stringify(game),
|
|
version,
|
|
Date.now(),
|
|
code,
|
|
expectedVersion,
|
|
row.state,
|
|
);
|
|
if (result.changes !== 1)
|
|
throw new Error('The lobby changed during setup. No prototype was saved.');
|
|
return {
|
|
code,
|
|
version,
|
|
status: game.status,
|
|
advanced: game.advanced,
|
|
players: game.players.length,
|
|
};
|
|
}
|