import test from 'node:test' import assert from 'node:assert/strict' import { states, classicActions, render, main, TICK_MS } from '../src/app.js' import { run } from '../src/stream.js' import { trace } from '../src/io.js' import { view } from '../src/view.js' import { newGame } from '../src/spawn.js' import { Action } from '../src/model.js' import { freeze } from './helpers.js' import { classicMain } from './m09-helpers.js' const time = (t) => ({ tag: 'Time', t }) const down = (key) => ({ tag: 'KeyDown', key }) test('m09 the game ticks every 150 ms', () => assert.equal(TICK_MS, 150)) test('m09 classicActions turns Tick into actions on the clock and keys into Turn actions', () => assert.deepStrictEqual( run(classicActions, freeze([time(150), down('ArrowUp'), down('x'), time(300)])), [Action.Tick, Action.Turn('Up'), Action.Tick])) test('m09 states starts with the new game, then yields one state per action', () => { const out = run(states(42, classicActions), freeze([time(150), down('ArrowUp'), time(300)])) assert.equal(out.length, 4) assert.deepStrictEqual(out[0], newGame(42)) assert.deepStrictEqual(out.map((s) => s.snake[0]), [ { x: 10, y: 10 }, { x: 11, y: 10 }, { x: 11, y: 10 }, { x: 11, y: 9 }, ]) assert.deepStrictEqual(out.map((s) => s.nextDir), ['Right', 'Right', 'Up', 'Up']) assert.deepStrictEqual(out.map((s) => s.dir), ['Right', 'Right', 'Right', 'Up']) }) test('m09 render draws the whole view of the state', () => assert.deepStrictEqual(trace(render(newGame(42)), 0).ops, view(newGame(42)).map((shape) => ({ tag: 'Draw', shape })))) test('m09 main asks for a random seed, then builds the stream from it', () => { const { ops, result: stream } = trace(main, 42) assert.deepStrictEqual(ops, [{ tag: 'RandomSeed' }]) const [outs] = stream.step(time(0)) assert.equal(outs.length, 1) assert.deepStrictEqual(trace(outs[0], 0).ops, trace(render(newGame(42)), 0).ops) }) // `main` itself switches to speedActions in m10, so the 150 ms behaviour is checked on the same program built from classicActions. test('m09 the program built from classicActions draws the new game first, then the ticked game after 150 ms', () => { const { result: stream } = trace(classicMain, 42) const [first, next] = stream.step(time(0)) const [second] = next.step(time(150)) assert.deepStrictEqual(trace(first[0], 0).ops.map((o) => o.shape), view(newGame(42))) assert.equal(second.length, 1) assert.notDeepEqual(trace(second[0], 0).ops, trace(first[0], 0).ops) }) // Holds before and after m10: whatever `main` ticks with, the clock must be wired in, so time passing produces more drawings. test('m09 main draws again once time has passed', () => { const { result: stream } = trace(main, 42) const [first, next] = stream.step(time(0)) const [later] = next.step(time(150)) assert.equal(first.length, 1) assert.ok(later.length >= 1) })