import { writeFileSync, readFileSync } from 'node:fs' // Fixture regeneration aid (see scripts/fixtures/README.md). Written during the 2026-11-11 build, kept for reference. // Writes into fixtures/ unless FIXTURES_OUT names another directory (with a trailing slash). const FIX = process.env.FIXTURES_OUT ?? new URL('../../fixtures/', import.meta.url).pathname const SOL = new URL('../../js/solutions/', import.meta.url).href const HERE = new URL('./', import.meta.url).pathname const R = SOL const F = FIX const { sessionUpdate, sessionView, play, main } = await import(R + 'app.js') const { Action } = await import(R + 'model.js') const { update } = await import(R + 'game.js') const g = (o) => ({ width: 4, height: 3, snake: [{ x: 1, y: 1 }, { x: 0, y: 1 }], dir: 'Right', nextDir: 'Right', apple: { x: 3, y: 0 }, seed: 5, score: 0, status: 'Playing', frame: 0, queued: null, ...o }) const eating = (score) => g({ score, apple: { x: 2, y: 1 } }) const S = (game, best, improved = false) => ({ game, best, improved }) const su = (name, s, a) => ({ name, args: [s, a], expected: sessionUpdate(s, a) }) const suCases = [ su('a-step-without-eating-keeps-the-best', S(g({ score: 2 }), 5), Action.Tick), su('the-first-apple-with-no-record-is-an-improvement', S(eating(0), 0), Action.Tick), su('eating-past-the-record-raises-the-best', S(eating(5), 5), Action.Tick), su('eating-below-the-record-keeps-the-best', S(eating(1), 5), Action.Tick), su('equalling-the-record-is-not-an-improvement', S(eating(4), 5), Action.Tick), su('the-next-action-clears-improved', S(g({ score: 6 }), 6, true), Action.Turn('Up')), su('a-frame-that-steps-and-eats-raises-the-best', S({ ...eating(3), frame: 6 }, 3), Action.Frame), su('a-frame-that-only-counts-keeps-the-best-and-clears-improved', S(g({ score: 3, frame: 1 }), 3, true), Action.Frame), su('game-over-keeps-the-best', S(g({ snake: [{ x: 3, y: 1 }, { x: 2, y: 1 }], score: 4 }), 4), Action.Tick), su('restart-keeps-the-best', S(g({ status: 'GameOver', score: 6 }), 6), Action.Restart), su('pausing-keeps-the-best', S(g({ score: 2 }), 9, true), Action.TogglePause), ] const fx = (fn, cases) => JSON.stringify({ function: fn, cases }, null, 2) + '\n' writeFileSync(F + 'm10-session-update.json', fx('sessionUpdate', suCases)) const v = (o) => ({ width: 3, height: 2, snake: [{ x: 1, y: 0 }, { x: 0, y: 0 }], dir: 'Right', nextDir: 'Right', apple: { x: 2, y: 1 }, seed: 0, score: 5, status: 'Playing', frame: 0, queued: null, ...o }) const sv = (name, s) => ({ name, args: [s], expected: sessionView(s) }) writeFileSync(F + 'm10-session-view.json', fx('sessionView', [ sv('playing-adds-the-best-at-the-top-right', S(v({}), 12)), sv('a-best-of-zero-is-shown-too', S(v({ score: 0 }), 0)), sv('game-over-shows-the-banner-and-then-the-best', S(v({ status: 'GameOver' }), 7, true)), ])) // golden: play the final main and keep the last drawing's scene const golden = (name, file, seed, best) => { const events = JSON.parse(readFileSync(HERE + 'golden-events/' + file, 'utf8')) const drawings = play(main, seed, events, best) const scene = drawings.at(-1).filter((o) => o.tag === 'Draw').map((o) => o.shape) const saves = drawings.flat().filter((o) => o.tag === 'SaveBest') console.error(name, 'drawings', drawings.length, 'saves', JSON.stringify(saves)) return { name, args: [seed, events, best], expected: scene } } writeFileSync(F + 'm10-golden.json', fx('play', [ golden('seed-42-eats-three-apples-with-no-stored-best', 'ev-42-0-3-stop.json', 42, 0), golden('seed-7-beats-a-stored-best-of-2-and-hits-the-wall', 'ev-7-2-4-wall.json', 7, 2), golden('seed-42-restart-after-game-over-keeps-the-best', 'ev-42-0-2-restart.json', 42, 0), ]))