Claude Code · VS Code · Agent Teams · Project Two

Maze Muncher
Build a Ms. Pac-Man–style maze-chase game with a team of AI agents

The second practice project in the series. You'll rebuild the design that made Ms. Pac-Man great — a tile maze, pellets, power pellets, and four enemies with genuinely different personalities — with your own name, art, and mazes, and you'll build the hardest part (the enemy AI) with a four-teammate agent team where each personality is spec'd, owned, and tested.

Written for Tommy · FrameLogic Studio · August 2026 · Assumes you've completed the Brick Break tutorial (Stages 0–4) — setup, subagents, and team basics aren't re-taught here. Field-relevant fixes from that guide's first run are already applied. Verified against Claude Code v2.1.240.

Five stages · the power pellet in the middle is the agent-team build

Read this before you publish anything. Ms. Pac-Man is Bandai Namco's intellectual property: the name, the characters, the sprites, and the original maze layouts are protected. What's not protectable is game mechanics — tile-based maze movement, pellet eating, energizers, and the four-enemy AI design are ideas, and ideas are free to learn from and reimplement. This tutorial therefore builds Maze Muncher: original name, original character designs, original mazes, and the classic mechanics underneath. Don't rename it back, don't copy the original maze tile-for-tile, and don't use ripped sprites, especially on a business website. Your own dot-muncher and four "wisps" are legally yours; hers aren't.

Why this game is the perfect second project

Brick Break taught you the workflow. Maze Muncher teaches you why the workflow matters, because this game has something Breakout doesn't: four AI-controlled characters whose behaviors interact. The original's ghosts are one of the most celebrated designs in game history precisely because each has a distinct, simple, testable targeting rule — and distinct, simple, testable is the exact shape of work that splits across an agent team. One teammate per personality would be too fine; one teammate owning all four behaviors, gated by a spec and a QA teammate who tests each rule in isolation, is just right.

It's also a step up in a second way: movement is tile-based, not free. Breakout's ball floats in continuous space; the muncher and the wisps live on a grid, turn only at intersections, and get cornering assistance so turns feel crisp. That grid logic is pure, deterministic, and perfect for unit tests, which means your TaskCompleted hook has real teeth here.

What carries over from Brick Break unchanged: the folder-ownership discipline, the three subagent roles, the hooks, plan approval on the shared-contract piece, and you on merges.

Prerequisites

The short list, since your machine is already set up from the Brick Break guide:

  • Claude Code CLI v2.1.240 or later, signed in; agent teams flag on in ~/.claude/settings.json.
  • Your three subagent roles in ~/.claude/agents/ (the versions with SendMessage in the implementer and reviewer tools lines).
  • Your settings template with the :* permission syntax and the two team hooks.
  • A GitHub account and the gh CLI.

Create the repo the same way as before:

mkdir -p ~/code/maze-muncher && cd ~/code/maze-muncher
git init -b main
gh repo create maze-muncher --private --source=. --remote=origin
mkdir -p .claude docs/specs
cp ~/claude-templates/settings.json .claude/settings.json
code .

Remember the two trip-wires from last time: role files load at session start (restart after creating or changing them), and don't leave .claude/settings.json open with unsaved edits while Claude writes to it.

The design: tiles, cornering, and four personalities

The world is a grid of tiles

The maze is a text grid: # wall, . pellet, o power pellet, - empty corridor, M muncher spawn, 1 2 3 4 wisp spawns, = the den door, T tunnel mouths (wrap to the matching T on the other side). Everything — movement, collision, wisp pathing — happens in tile coordinates, with pixel positions derived only at render time. A character occupies a tile and a fractional offset along its direction of travel; it may only change direction when centered on a tile, except for cornering: a turn buffered a few pixels early is remembered and executed at the center, which is what makes the controls feel good instead of sticky.

The four personalities (the famous part, genericized)

Each wisp picks a target tile and, at every intersection, chooses the legal direction that minimizes straight-line distance to that target — no pathfinding, no search, and they may never reverse except on a mode change. All the personality lives in how the target is chosen:

WispNicknameTarget rule (chase mode)Personality that emerges
Rushthe chaserThe muncher's current tile.Direct pursuit; always on your tail.
Cutoffthe ambusherFour tiles ahead of the muncher's current direction.Gets in front of you; punishes straight lines.
Pincerthe flankerTake the tile two ahead of the muncher, draw the vector from Rush's tile to it, and double it.Unpredictable sweeps; teams up with Rush without either knowing it.
Wanderthe shy oneThe muncher's tile when more than 8 tiles away; its own scatter corner when closer.Approaches, loses nerve, retreats; keeps a lane open that skilled players exploit.

Wisps cycle globally between scatter (each targets its own home corner — the pack disperses) and chase on a timer (7s scatter / 20s chase, repeating, scatter phases shrinking per level). Eating a power pellet flips everyone to frightened: they slow down, pick random directions at intersections, and can be eaten, returning to the den as "eyes." In the Ms. Pac-Man tradition, the first few seconds of a level use semi-random targeting so patterns can't be memorized — you'll implement that as a per-wisp random target during the opening scatter.

Layout (ownership boundaries are directories, again)

maze-muncher/
├── CLAUDE.md
├── .claude/            settings.json + agents/ (copied from template)
├── docs/               ROADMAP.md · STATUS.md · specs/
├── public/             index.html · style.css
├── src/
│   ├── engine/         loop.js · input.js (with turn buffering) · draw.js
│   ├── logic/          grid.js · movement.js · targeting.js · modes.js   ← pure, no DOM
│   ├── entities/       muncher.js · wisp.js · fruit.js                   ← plain objects
│   ├── levels/         mazes.js (text grids) · parse.js                  ← pure
│   ├── ui/             hud.js · screens.js
│   └── main.js
├── tests/              unit/ (mirrors src) · integration/
├── server.js           Express static, port 3000
└── package.json        npm start · npm test (node --test)

The load-bearing decision: all four targeting rules live in one pure file, src/logic/targeting.js, as small functions taking plain state and returning a target tile. That makes each personality independently unit-testable ("given muncher at (10,10) facing left and Rush at (3,3), Pincer targets (13,17)"), which is exactly what your QA teammate will do in Stage 4.

Roadmap

MilestoneScopeBuilt by
M1 MazeParse and render a maze; muncher moves on the grid with cornering; eats pellets; level clears when none remain.Solo session (Stage 1)
M2 One wispRush chases; collision costs a life (3 lives); power pellets flip frightened mode; eaten wisps return as eyes; score.Solo + subagents (Stage 2)
M3 TuningSpeeds, timers, frightened duration, cornering window — reviewed and tuned.Review team (Stage 3)
M4 The packAll four personalities, scatter/chase cycling, semi-random opening, the den and door logic, fruit that wanders the maze.Build team (Stage 4)
M5 Maze packThree more mazes with per-maze colors and a maze-select screen, built as its own project in a worktree.Two PMs + leads (Stage 5)

1Scaffold: maze, muncher, pellets

Goal: a playable M1 — you steer the muncher around an original maze eating pellets, with cornering that feels right.

What you'll learn: speccing "feel" (cornering, turn buffering) precisely enough that an agent can build it, and designing an original maze in text.

Start claude --name solo in the repo, switch to plan mode (Shift+Tab), and paste:

Prompt — scaffold, plan mode first
Scaffold a browser maze-chase game called maze-muncher with this exact directory layout: [paste the tree from the design section]. Constraints: plain HTML/CSS/JS with ES modules, no framework, no build step; canvas rendering; server.js is a minimal Express static server on port 3000; npm test runs node --test over tests/. Everything in src/logic and src/levels must be pure — no DOM, window, canvas, timers, or Math.random (callers pass rng) — so it is unit-testable. Maze format: a text grid parsed by src/levels/parse.js. Characters: # wall, . pellet, o power pellet, - empty corridor, M muncher spawn, 1..4 wisp spawns inside a den, = den door, T tunnel mouths that wrap to the matching T. Design ONE original 28x31 maze in src/levels/mazes.js — do not reproduce any real Pac-Man or Ms. Pac-Man maze; make it symmetric, fully connected, with one tunnel row and a centered den. Movement (src/logic/movement.js): characters live on tiles with a fractional offset; direction changes only at tile centers; implement turn buffering (a queued turn within 4 pixels of center executes at center) and cornering (the buffered turn may cut the corner by up to 3 pixels). Input (src/engine/input.js) records the most recent arrow key as the buffered turn. Build milestone 1 only: render the maze, move the muncher with cornering, eat pellets and power pellets (no effect yet beyond score), clear the level when no pellets remain, show score. Unit tests for parse.js (counts, symmetry, tunnel pairing, rejects unknown chars), grid.js, and movement.js (turn buffering happens at center; illegal turns ignored; tunnel wrap). Write CLAUDE.md with the pure-logic boundary, the ownership directories, conventional commits, one branch per feature, and "run npm test before completing any task." Add docs/ROADMAP.md with the five milestones I'll paste next. Make npm test pass. Show me the plan before writing anything.

Review the plan for two things: the pure boundary held (no Math.random in logic), and the maze is original. Approve, let it build, then npm install && npm start and play at localhost:3000. Cornering is the thing to feel-test: turns should catch when pressed slightly early. Paste the roadmap when asked, then commit, merge, push — the same rhythm as Brick Break.

Check yourself: maze renders and is clearly not the original layout; pellets disappear and score climbs; a buffered early turn executes at the corner; npm test passes; repo pushed.

2Subagents: one wisp, lives, and power pellets

Goal: M2 — Rush hunts you, contact costs a life, power pellets turn the tables — built through your explorer / implementer / reviewer roles.

What you'll learn: delegating a feature that spans pure logic and wiring, and writing the mode state machine that Stage 4's team will extend.

Restart the session if you've edited any role files. Then:

Prompt — M2 via subagents
Work on branch feat/m2-first-wisp. Use the explorer subagent to map how movement, entities, and main.js connect, and report the cleanest seams for adding: a wisp entity, a global mode state machine, and muncher-wisp collision. Then use the implementer subagent for the pure parts in src/logic only: targeting.js with chaseTarget_rush(state) returning the muncher's tile, and modes.js — a state machine over {scatter, chase, frightened, eyes} with a timer table (scatter 7s / chase 20s, frightened 6s from a power pellet, eyes until the den), pure and rng-injected. Then use the implementer subagent again for wiring: wisp entity using targeting + intersection choice (never reverse except on mode change), frightened slowdown and random turns, collision = lose a life (3 lives, respawn positions), eating a frightened wisp = 200 points and eyes-to-den, HUD lives display. Unit tests for targeting_rush, the mode timer table, and never-reverse; an integration test that runs the pure sim until a collision. Finally run the reviewer subagent on the full diff and show me all reports.

Play it: get chased, grab a power pellet, eat Rush, watch the eyes travel home. Fix anything that feels wrong by describing it in plain words, then commit, merge, push.

Why the mode machine had to be pure and rng-injected: Stage 4's QA teammate will unit-test "frightened wisps turn randomly" with a seeded rng, and "scatter flips to chase at exactly 7s" with a fake clock. Neither test is writable if the machine reads Date.now() or Math.random() directly. You just built the contract the team depends on.

Check yourself: Rush corners you eventually (it should — direct pursuit always does); frightened mode is survivable; the eyes reach the den and Rush re-emerges; tests pass; merged and pushed.

3Review team: tune the chase

Goal: a tuned M3 and a written spec for M4, produced by a three-teammate review team.

Prompt — review team
Create an agent team to review the current state of this repo. Spawn three teammates named feel, bugs, and fairness. Use Sonnet for each. - feel: cornering window, muncher vs wisp speed ratio, frightened slowdown, timer lengths — propose concrete numbers with reasoning, and flag anywhere the maze layout creates dead zones or overpowered lanes. - bugs: tile-boundary edge cases (tunnel wrap while turning, mode change at an intersection, collision during cornering), score/lives correctness, anything that could throw. - fairness: with only Rush active the game is win-by-circling — verify, and specify what the remaining three personalities must each contribute so the pack closes the strategies Rush alone can't. Write this as input to the M4 spec. Have them message each other when findings overlap. Each reports with severity and file:line. Wait for all three, then synthesize: a tuning changelist I can approve, and a draft of docs/specs/pack.md covering the three remaining personalities, scatter/chase cycling, the semi-random opening, den/door release rules, and wandering fruit. Do not change any files.

Approve the tuning numbers you like ("apply the tuning changelist, run npm test, show me the diff"), then have it write the spec: "Write the final docs/specs/pack.md, commit both as 'feat: tuning + docs: pack spec', push, and shut the team down." Read pack.md yourself before Stage 4 — every vagueness in it becomes a teammate's guess.

Check yourself: the game feels meaningfully better than your pre-review build; docs/specs/pack.md exists and gives each personality an exact target rule, the timer table, den release order, and fruit behavior.

4The agent team builds the four personalities

Goal: M4 shipped by a four-teammate team — the full pack with distinct personalities, scatter/chase cycling, the den, and fruit — with plan approval on the targeting contract and hooks gating every completion.

What you'll learn: ownership when the "feature" is behavior rather than screens, and letting QA test personalities as math instead of by playing.

Confirm the hooks block is in .claude/settings.json (test-gate on TaskCompleted, commit-and-report on TeammateIdle), start a fresh session, and paste:

Prompt — build team: the pack
Create an agent team to build the pack described in docs/specs/pack.md. Spawn four teammates using these agent types and names, and give each ONLY the files listed. Put the ownership map in the shared task list. - brains (implementer, require plan approval): owns src/logic/** — the three remaining target rules in targeting.js (cutoff: 4 tiles ahead of the muncher's facing; pincer: double the vector from Rush's tile to 2 tiles ahead of the muncher; wander: muncher's tile beyond 8 tiles, own scatter corner within), scatter-corner table, the semi-random opening targeting (rng-injected), and den release rules in modes.js. Every function pure. Only approve a plan that lists exact files, exact function signatures, and the tests for each rule. - pack (implementer): owns src/entities/** and src/main.js — instantiate all four wisps with per-wisp state, wire targeting per personality, den entry/exit through the door, staggered release, fruit entity that wanders corridors and scores on catch. Depends on brains' signatures; message brains to agree on them before coding. - face (implementer): owns src/ui/** , src/engine/draw.js , and public/** — four visually distinct original wisp designs (shapes/colors of your invention, not the classic ghost sprite), frightened and eyes states readable at a glance, fruit rendering, mode indicator in the HUD. - qa (reviewer then implementer): owns tests/** — unit tests proving each personality's target for fixed board states (including pincer's vector math and wander's 8-tile flip), the never-reverse rule, seeded-rng tests for the opening and frightened turns, den release order, and an integration sim of one full scatter/chase cycle with all four active. Reviews each teammate's diff before they mark tasks complete. Rules: branches feat/pack-<name>; nobody edits outside their files — message the owner; 5–6 tasks each with dependencies (pack and face depend on brains' plan approval); wait for all teammates before summarizing; do not implement anything yourself.

Watch the same beats as Brick Break's build: Ctrl+T for the task list, the plan-approval exchange with brains, and any hook rejections. When all branches are ready: "Merge in the order brains, pack, face, qa, running npm test after each; stop on failure; then delete the branches, push, and report the final test count."

Then play it properly. The test that matters isn't in the suite: with all four active, does circling stop working? Cutoff should punish your straight runs, Pincer should surprise you from angles, and Wander's retreats should be the escape lane you learn to use. If a personality isn't legible in play, that's a spec conversation, not a bug — sharpen its rule in pack.md and hand it back to a single implementer.

Check yourself: four visually and behaviorally distinct wisps; scatter moments where the pack visibly disperses; the semi-random opening differs run to run; qa's tests pin each personality's math; merged, green, pushed.

5Second maze pack in a worktree

Goal: the two-project stack from the Brick Break guide's Stage 5, with real work on both sides: one PM polishes the game, the other builds a maze pack — three more original mazes, per-maze palettes, and a maze-select screen — in its own worktree. Ms. Pac-Man's signature improvement over the original was multiple mazes; it makes the perfect second project.

The shared contract between the projects is the maze format, which is why parse.js validation matters: the maze-pack PM's team designs against the format, and the game PM's team consumes whatever validates. Set it up exactly as the Brick Break guide describes — claude --worktree maze-pack --name pm-mazes, claude --worktree polish --name pm-game, two leads, standing briefs, idle notices — and give pm-mazes this charter:

Prompt — to pm-mazes
You are the project lead for the maze pack in this worktree. Run an agent team that delivers: three new original 28x31 mazes in the documented format (symmetric, fully connected, one or two tunnel rows, varied den placement — and verifiably distinct from each other and from maze 1), a per-maze color palette, and a maze-select screen. Require plan approval for any change to parse.js or the maze format itself; format changes must be backward compatible and coordinated with pm-game through the leads. A maze ships only when qa proves it parses, is fully connected (every corridor tile reachable), and contains no dead ends wider than one tile. Report status to lead-a per the standard protocol. Start now.

If your son is running his own stack, this is the natural split: one of you takes pm-game, the other pm-mazes, and the leads negotiate the format between you. The maze-connectivity check qa runs (flood fill from the muncher spawn) is a nice piece of pure logic to compare implementations of.

Check yourself: four sessions in /list-agents; STATUS.md maintained by a lead; a format question that actually traveled PM → lead → PM; four selectable mazes, each clearing correctly.

Troubleshooting specific to this project

SymptomLikely causeFix
Wisps vibrate or spin at intersectionsDirection chosen every frame instead of once per tile centerDecide direction only when centered on a new tile; cache it until the next center
A wisp reverses mid-corridorReverse not excluded from the legal-direction setExclude the opposite of current direction except on a mode change; qa has a test for this — check it's not skipped
Muncher sticks on cornersTurn buffering window too small, or buffered turn cleared on wall contactKeep the buffered turn until executed or replaced; widen the window to 4px and re-feel
Wisps never leave the denDoor tile treated as a wall for wisps, or release rules never fireThe door is passable to wisps in eyes/exiting states only; check den release counters in modes.js
Pincer behaves exactly like RushVector doubled from the wrong origin (muncher instead of Rush)The vector runs FROM Rush's tile TO two-ahead-of-muncher, then doubles; qa's fixed-board test pins the exact tile
Frightened mode identical every timerng not actually injected — a literal Math.random inside logicGrep src/logic for Math.random; the seeded-rng tests should have caught it, so also check they assert on sequences, not just types
Game unwinnable / pellet count never reaches zeroA pellet tile unreachable, or tunnel tiles counted as pelletsRun the flood-fill connectivity check from Stage 5 against maze 1 too
Everything from the Brick Break guideThat guide's Troubleshooting and Field Notes sections all still apply, including role-loading at session start and the stale-tab problem

Credits

Written by Tommy Waddell (FrameLogic Studio) with Claude (Anthropic). Multi-agent architecture based on the daily-driver workflow described by Daisy, an engineer on the Claude Code team, as quoted in an Anthropic email newsletter (2026). Game design mechanics are drawn from the publicly documented behavior of the Pac-Man family of games; Maze Muncher uses original names, art, and mazes, and is not affiliated with or endorsed by Bandai Namco. Verified against Claude Code v2.1.240, August 2026. Agent teams are experimental — trust your screen over this document where they differ.