Building a Polished Match-3 with an AI — Step by Step, Under Contract
A match-3 puzzle is deceptively deep: a swap engine, run detection, gravity, refills, cascades, combo scoring, juice, levels, and screens. Exactly the kind of thing that goes off the rails when you “one-shot prompt” an AI. So I built one the disciplined way: I had Claude Opus 4.8 write it across 5 governed batches, each bound by a contract it could not exceed, each validated before it could land.
This is the full tutorial — every step, every command. Then you can play it.
🎮 Play it first
▶ Play Match-3 Under Contract — one self-contained HTML file, mobile + desktop. Swap adjacent gems (tap-tap or swipe) to line up 3+, chain cascades for combos, hit the target before you run out of moves.
Here’s the real thing running (a headless-browser capture): distinct colour-and-shape gems, a live HUD, and an x2 COMBO mid-cascade.

Source + the contract that governed it: github.com/ruslanmv/match-3-under-contract.
The two tools
- Matrix Builder (
mb) — turns an idea into a contract: a locked blueprint, pinned standards, an allow-list of exactly which files may change, and acceptance criteria. It also validates the result. - GitPilot — the coder. Pointed at Claude Opus 4.8, it writes the game, bound by each batch’s contract.
Step 0 — Install and point GitPilot at Claude
pip install agent-generator gitcopilot crewai
export GITPILOT_PROVIDER=claude
export GITPILOT_CLAUDE_MODEL=claude-opus-4-8
export ANTHROPIC_API_KEY=sk-ant-…
Step 1 — One sentence becomes a contract
mb init "A polished neon Match-3 (Bejeweled-style) gem-swap puzzle, single self-contained HTML file, mobile + desktop" \
--quality standard --title "Match-3 Under Contract"
Steps 2–6 — Five governed batches
Each batch runs the same four commands — plan a scoped batch, render the contract-bound prompt, let Claude extend the single allowed file, validate fail-closed:
mb next "<the batch goal>" # plan a scoped batch (allow-list: frontend/index.html)
mb prompt --coder gitpilot # render the contract-bound prompt
gitpilot generate -m "$(cat coder-prompts/gitpilot.md)" -o .
mb check frontend/index.html # approved / needs-repair / rejected
Here is exactly what each batch asked Claude to add, the resulting size, and the immutable Matrix Commit:
Batch 1 — Foundation
8×8 board, 7 gems that differ by colour and shape (colourblind-friendly), canvas render loop, a board generated with no pre-existing matches, neon styling.
→ 12 KB · mc-8200e9006db3 · approved
Batch 2 — Swap + match detection
Tap-to-select and swipe-to-swap adjacent gems; detect horizontal/vertical runs of 3+; a swap only sticks if it creates a match (otherwise it animates back).
→ 24 KB · mc-eb05dc6d6f40 · approved
Batch 3 — Clear, gravity, cascades, scoring
Remove matches, drop gems with gravity, refill from the top, then re-scan and repeat — cascade chains — with combo multipliers and 4/5-match bonuses. Reshuffle if no moves remain.
→ 30 KB · mc-c6326ada66b2 · approved
Batch 4 — Juice
Tween swap + falling animations, particle bursts in gem colours, screen-shake on big cascades, floating combo popups, WebAudio SFX (with a mute toggle), and a fully responsive, DPR-aware canvas with refined touch.
→ 41 KB · mc-ae0fc4388e80 · approved
Batch 5 — Meta + polish
Levels with a target score and limited moves, start / level-complete / game-over screens, high score in localStorage, accessibility, and the footer credit “coded by GitPilot — under a Matrix Builder contract.”
→ 56 KB · mc-721814e1b8c7 · approved
$ mb timeline
Match-3 Under Contract v1.0.0
Batch 01 Foundation ✓ mc-8200e9006db3
Batch 02 Swap and match detection ✓ mc-eb05dc6d6f40
Batch 03 Clear, gravity, cascades, scoring ✓ mc-c6326ada66b2
Batch 04 Juice: animations, particles, sound, mobile ✓ mc-ae0fc4388e80
Batch 05 Levels, moves, screens, high score, polish ✓ mc-721814e1b8c7
Across all five batches, Claude wrote to only frontend/index.html — never a stray file. Every batch returned MATRIX_STATUS: approved score=100. If the model had written outside the allow-list, mb check returns needs-repair (exit 1) or rejected (exit 2) and the change is blocked.
Step 7 — Verify it actually runs
node --check on each batch wasn’t enough, so I ran the finished game in a real headless Chromium — started a level, swapped gems, triggered a cascade:
RUNTIME JS ERRORS: NONE ✓
The screenshot above is that run. Full transcript in EVIDENCE.md.
How it works — and why Matrix Builder
The shift is from prompting to engineering:
- A contract, not a prompt — a locked blueprint and pinned standards.
- Allow-list scope — the AI edits only the files you permit.
- Fail-closed validation —
mb checkreturns approved / needs-repair / rejected (exit 0/1/2), so it gates CI. - Immutable Matrix Commits — every accepted change pins the prompt, diff, and verdict.
- Standards baked in — NIST SSDF, OWASP Top 10 & LLM Top 10, SLSA.
- Provider-agnostic — Claude here, but OpenAI, Watsonx, or local Ollama work the same.
Five batches, one file, zero out-of-scope edits — and a genuinely fun puzzle that runs on a page, on your phone or desktop.
Reproduce it
pip install agent-generator gitcopilot crewai
export GITPILOT_PROVIDER=claude GITPILOT_CLAUDE_MODEL=claude-opus-4-8 ANTHROPIC_API_KEY=sk-ant-…
mb init "a neon Match-3 puzzle" --quality standard
# then, per batch:
mb next "the next feature" && mb prompt --coder gitpilot
gitpilot generate -m "$(cat coder-prompts/gitpilot.md)" -o .
mb check frontend/index.html
Take it for a spin
- Play / fork: github.com/ruslanmv/match-3-under-contract
- Matrix Builder: agent-matrix/matrix-builder
- GitPilot: gitpilot.ruslanmv.com
- Also under contract: Pong · Tetris
Five scoped batches, one file, and no out-of-scope edits — the same governed workflow that sits behind Matrix Builder and GitPilot. The game is simply the most enjoyable way to watch it work.
Leave a comment