advanced · 28 minute study
Turn a difficult playtest into a reproducible experiment
A bug reported as 'sometimes the game ends twice' is expensive until you can replay the exact inputs. Deterministic simulation turns a visual anecdote into a testable sequence.
Useful preparation · 1 prerequisite
Use this as a map, not a lock. If these ideas are familiar, continue.
- State machines make behavior explainable — A game that can pause, finish and restart in contradictory orders will eventually charge twice, submit twice or spawn into a menu. Make legal transitions explicit before making enemies clever.
First, build a mental model
A replay is a recipe: initial state, implementation version, input sequence, random seed and time-step sequence. If hidden inputs such as wall time or global randomness enter the update, the recipe is incomplete.
- Keep pure rules callable without DOM, audio or network. Inject time, input and randomness as explicit arguments or controlled dependencies. Tests can then run thousands of meaningful updates quickly without booting a browser.
- Test behavior and invariants: health never drops below zero, a paused world does not advance, one terminal event occurs, and equivalent input recipes produce equivalent outcomes. Do not simply assert that an implementation contains a desired function name.
- A seed is useful only if all random decisions consume the controlled generator consistently. Fixed time steps are useful only if iteration order and event order are also controlled. Exact cross-platform floating-point reproducibility is a separate requirement.
- Use different test layers for different claims. Pure tests check rules; integration checks adapters; browser tests check real controls and rendering; visual review checks readability and composition. None alone certifies a complete game.
- When a failure is found, reduce the replay to the shortest input sequence that still reproduces it. Preserve that sequence as a regression test before broadening the fix.
import test from "node:test";
import assert from "node:assert/strict";
function damage(state, amount) {
if (state.health === 0) return state;
const health = Math.max(0, state.health - amount);
return { health, finishes: state.finishes + (health === 0 ? 1 : 0) };
}
test("duplicate hits cannot finish a defeated actor twice", () => {
let state = { health: 2, finishes: 0 };
for (const hit of [1, 1, 4]) state = damage(state, hit);
assert.deepEqual(state, { health: 0, finishes: 1 });
});
// This tests this pure damage rule, not collision, audio, networking or UI.- Hit 1
Health moves from 2 to 1. The terminal counter remains zero.
- Hit 2
Health reaches zero and the single completion is recorded.
- Late hit
A subsequent hit returns the defeated state unchanged. The test would fail if damage emitted another completion.
Production thinking
Take this beyond the example.
Report what actually ran. A source contract test, a headless simulation, a desktop smoke run and a mobile playtest are different evidence. Do not label a game playable merely because its HTML loaded or its rule suite passed.
Sources and further reading
Your evidence
unseen is a local learning signal, not a credential or proof of production competence.
Explain the mental model, then explicitly acknowledge it. Opening a page is not evidence.
Supported retries can complete this lesson. Independent evidence and delayed review determine the skill stage.
Next lesson · Audio is a resource graph with a lifecycle →