FUN.md — the game-design playbook

Distilled from the 20-genre Build-Measure-Learn campaign (BUILDLOG.md). This is a prompt, not an archive: when authoring a new game, obey Part 1, look up your genre in Part 2, and run the Part 3 checklist before writing any level or content data. Corpus notes cite the js13k dataset (js13k/data/registry.csv, 1,164 games with per-criterion judge ranks; rank_gameplay used as the fun proxy).

Designing from a high-level intent? FUN.md is the verification half — it proves a genre is fair. For the generative half — turning “make an RTS with faction asymmetry” into a concrete design — start with the spine, the Codex’s primary generator: name the one tension (objective · superpower · scarcity · obstacle · renewal) and derive the game from it, using the “X but Y” twist only to give that tension a face. The spine gives you a loop; the twist gives you a pitch — lead with the loop. Then return here to prove what you designed — and note law 8 below is where the spine’s central coupling becomes a mechanical proof.

Part 1 — Universal laws

  1. Every genre has a mechanical truth, and it is provable. Find yours before coding: solver (puzzles), bot run (movement/combat), pacing windows (economies), duels (counter systems), connectivity (procgen), both-ways affordance proofs (stealth), frame windows (rhythm), content lints (narrative). Test recipe: the verify suite IS the design doc — write its assertions in the spec, first.
  2. Skill-delta proofs are the closest thing to a fun proof. Run the intended strategy AND a null strategy; assert the gap. Drafting 17/20 vs skipping 9/20; greedy 158 vs random 82; braking 26.2s vs flooring 27.7s; judgement 19/20 vs recklessness 0/20. If null play competes, the game is broken. Test recipe: expect(smartScore).toBeGreaterThan(nullScore * K).
  3. Derive constraints, don’t vibe them. Movement envelope before levels; season length ≥ growDays; turn radius < corner radius; fuel budget vs night length; spike damage ≤ block ceiling. Every “feels wrong” traces to a violated inequality. Test recipe: state the inequality in a comment, assert it against the actual config and content.
  4. Null-strategy baselines are the cheapest scenario test. The undefended lane, the do-nothing turn, the camping keeper. A threat a null strategy survives isn’t a threat; a scenario needs both a winning line AND a losing null. Test recipe: run the do-nothing bot, assert it loses.
  5. Grace is a system, not polish. Coyote time, jump/hit-stop input buffering, i-frames, wound-before-death, mercy clears, undo, instant retry — the same shape at every timescale, each unit-testable. Any pause the sim injects must buffer intent across it. Test recipe: frame-pump the grace window, assert accepted-inside / refused-outside to the exact frame. (Corpus: CLAWSTRIKE, 2025 gameplay #2, sells itself on “each retry is instant, keeping the momentum alive”; Dying Dreams, 2022 gameplay #1, ships Undo as a first-class key.)
  6. The cosmetic-view rule. Views (pooled sprites, springs, particles, spatial audio, the audio clock itself) must be deletable without changing a single sim bit. The sim resolves instantly and returns choreography; the view replays it. Beat/turn/cascade timing is sim time, never wall-clock. Test recipe: run headless, hash; run with view, hash; equal.
  7. Pure-data state pays compound interest. Plain-JSON world.state + world.rng makes clone-and-score bots (tactics options, aim search, deck pilots), golden replay hashes, and save/load all free. Every sim doubles as its own planning model. Test recipe: golden hash of a full scripted run; snapshot→restore→hash round-trip.
  8. Coupling is provable by ablation — the sharp form of law 2. Law 2 proves some tension exists; it can’t say where it lives. A game can pass law 2 while its fun rides on one system and the rest is decoration (Design Codex). Localize it: build the game twice — once with its central coupling intact (coupled), once with that ONE coupling neutralized (ablated — free light, infinite water, no stamina) — and run the SAME skilled and lazy policies over both. A real spine (00-process/the-spine.md) makes the skill-gap collapse under ablation (e.g. skilled 1.0/1.0, lazy 0.0→0.875: a 1.0 gap falls to 0.125). A gap that survives ablation proves the coupling is decoration — the fun comes from elsewhere and this system is unproven weight. This is the “does using the superpower well create the next problem?” gate, made mechanical. Test recipe: assertLoadBearing({ coupling, coupled, ablated, skilled, lazy }) — from @hayao; the gap must collapse (ablation.ts).

Part 2 — Per-genre cheat sheet

Each entry: what makes it fun / the mechanical truth / the verify pattern.

1 · Grid puzzle (Sokoban)

2 · Precision platformer (Celeste-like)

3 · Metroidvania

4 · Top-down action-adventure (Zelda-like)

5 · Stealth

6 · Twin-stick horde survival (Vampire Survivors-like)

7 · Bullet hell

8 · Tower defense

9 · RTS-lite

10 · Traditional roguelike

11 · Roguelike deckbuilder

12 · Turn-based tactics (Into the Breach-like)

13 · Match-3

14 · Incremental/idle

15 · Farming/life sim

16 · Survival horror

17 · City/colony builder

18 · Rhythm

19 · Physics arcade (Breakout/Peggle)

20 · Top-down racing

21 · Narrative decisions (Reigns-like)

Part 3 — Before you author levels/content

Run these BEFORE writing level/content data; each is a law-3 inequality or a standing trap from the campaign:

If the genre is a blend, satisfy every parent genre’s verify pattern — genres compose (rhythm = roguelike + input legality; Peggle = physics + aim search).

This module rendersthe repo's markdowndirectly — edit it there, it changes here.