// m05 game: one `update` function turns every action into a new state; replay is a fold over actions. // m10 adds pause, restart and a frame clock that speeds up as the score grows. import { turn } from './rules.js' import { tick, newGame } from './spawn.js' /** How many frames pass between game steps: 8 at first, one fewer every 3 points, never fewer than 2. */ export const movesEvery = (score) => Math.max(2, 8 - Math.floor(score / 3)) // One animation frame: count it, and every `movesEvery(score)`-th frame run a game step. const frame = (state) => { if (state.status !== 'Playing') return state const counted = { ...state, frame: state.frame + 1 } return counted.frame % movesEvery(counted.score) === 0 ? tick(counted) : counted } // Pause and resume only make sense in those two statuses; a finished game stays finished. const togglePause = (state) => state.status === 'Playing' ? { ...state, status: 'Paused' } : state.status === 'Paused' ? { ...state, status: 'Playing' } : state export const update = (state, action) => { switch (action.tag) { case 'Tick': return tick(state) case 'Turn': return turn(state, action.dir) case 'TogglePause': return togglePause(state) case 'Restart': return newGame(state.seed) case 'Frame': return frame(state) default: return state } } /** Fold the actions over the state, left to right. */ export const replay = (state, actions) => actions.reduce(update, state) /** Like replay, but keeps each intermediate state. Recursion instead of mutation: no array is ever pushed to. */ export const history = (state, actions) => actions.length === 0 ? [state] : [state, ...history(update(state, actions[0]), actions.slice(1))]