LLearnCode
Academy/games/Behavior and verification

intermediate · 26 minute study

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.

Loading progressEvidence grows with practice
Useful preparation · 1 prerequisite

Use this as a map, not a lock. If these ideas are familiar, continue.

First, build a mental model

A state machine is a small vocabulary of situations and the transitions permitted between them. AI then becomes an observation-to-action policy inside the current situation, rather than a collection of unrelated conditions that all fire at once.

  1. Separate application phase from actor behavior. Menu/playing/paused/result controls the world lifecycle. Patrol/chase/return can describe one enemy. Combining them into one flag invites impossible combinations.
  2. Transitions can guard side effects. Emit a result only when moving from playing to result; repeated finish events in result should do nothing. The renderer can observe phase changes without owning their authority.
  3. AI uses a pipeline: perceive allowed information, choose a goal, find a path if needed, then propose input to the normal movement system. It should not bypass walls merely because it is controlled by code.
  4. Think about fairness as well as difficulty. A bot can use reaction delay, limited sensors and cooldowns while still choosing strong actions. Seed or inject random choices when you want repeatable tests.
  5. Asynchronous path or search results can arrive after the world changes. Carry a request or turn identity; discard a result whose input state is no longer current.
Read the codetypescript · educational example
type Phase = "menu" | "playing" | "paused" | "result";
type GameEvent = "start" | "pause" | "resume" | "finish";
const transitions: Partial<Record<Phase, Partial<Record<GameEvent, Phase>>>> = {
  menu: { start: "playing" },
  playing: { pause: "paused", finish: "result" },
  paused: { resume: "playing" },
};
function transition(phase: Phase, event: GameEvent): Phase {
  return transitions[phase]?.[event] ?? phase;
}
console.log(transition("playing", "finish")); // result
console.log(transition("result", "finish"));  // result
// Emit completion only when oldPhase !== nextPhase && nextPhase === 'result'.
  1. Finish once

    The legal playing → result transition can produce one completion event.

  2. Duplicate signal

    Another finish event leaves result unchanged. A side effect conditioned on an actual transition does not run again.

  3. AI boundary

    An enemy can select a route while playing. The world pause gate prevents its movement update while paused, even if a delayed path computation finishes.

Production thinking

Take this beyond the example.

Test invalid and repeated transitions, not only the happy path. Keep result publication and save changes behind one terminal authority. Avoid presenting client-run AI or a local score check as anti-cheat enforcement for an online leaderboard.

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 · Turn a difficult playtest into a reproducible experiment →