Skip to content

Repository files navigation

worm — the opponent that earns its intelligence

A field naturalist's arena notebook: two neon light-cycle trails, a prediction card stamped before the turn, and an evidence dial that must click from watching to proven before the intercept gates close

A game that only gets smarter when it can prove it learned you.

Worm is a snake/Tron duel that studies one specific player across rounds — and it may commit harder to an intercept only after its forecast provably beats a simple rival based on your own habits. No mystery difficulty ramp. No flattering accuracy counter. You watch the claim form — or fail — in public.

Play it: worm.robertgpt.ai — or cargo run --release for the terminal build.

Jump to: Playing it · The claim, and how to falsify it · Development · Decisions (23 ADRs)


Why distrust a game that says it learns you?

A worm forced down a corridor while a brass counter proudly reads 95% correct — and the field notebook says: nothing learned

Most "adaptive" games can congratulate themselves for nothing. Imagine a rival that predicts straight while you are trapped in a corridor, then calls the inevitable move proof that it understands you. That number can look excellent even when the board supplied the answer — in worm, roughly 95% of frames are routine straight travel. A clock-based difficulty ramp has the same problem: time passed, but no intelligence was earned. The consequence is personal: you cannot tell whether the opponent adapted to you or merely got stronger behind a confident dashboard.

So worm removes every cheap way its opponent could appear intelligent: forecasts are scored only on genuine decisions, published (hash-sealed) before your input lands, and compared against your own online base rate rather than imaginary uniform chance. The refusal that anchors everything: if a random coin-flip player ever looks learnable, the evidence harness is leaking the answer — and every other number in this repository is void.


The big idea: forced moves and easy agreements fall away; precommitted wins on real choices remain


A game wrapped around a falsifiable learning experiment

You steer one luminous cycle. The computer steers the other — and keeps a field notebook about the choices only you could have made. Before a meaningful turn, the opponent commits a forecast. After you move, worm compares that forecast with both reality and a deliberately simple rival based on your own usual moves. Evidence that survives the controls becomes an earned read. That read can increase commitment to an intercept; it never switches off the rules that keep the computer alive.

A forecast earns the right to close an intercept gate: a left-turn forecast is committed before the turn, repeated correct calls fill an evidence dial, and only then does the intercept gate close across the predicted path

What worm counts, and refuses:

Signal Counts as learning? Why
A voluntary left turn Potentially You had a real alternative
Straight through one open corridor Not by itself The board made it easy
Random coin-flip play Must stay at chance There is no stable habit to read

The honest exchange: the player supplies genuine choices, the opponent commits forecasts, and an evidence gate compares them with the player's own baseline, returning only an earned read

The opponent gets dangerous by surviving an audit of its predictions — not by waiting for level two.


The claim, and how to falsify it

cargo test --test persona_learning -- --nocapture

Scripted personas play the game with a known habit. The CPU's forecasts are scored only on the frames where the persona actually exercised that habit — the frames where the answer was a fact about the player rather than a fact about the board. Continuing down a corridor, or turning because there was only one way out, is free accuracy and is excluded.

The suite is built around controls, because a test that cannot fail proves nothing:

persona what it establishes
POSITIVE compass — prefers a fixed direction An absolute habit the model provably can hold. If this stops being learned, the pipeline has regressed.
NULL coinflip — no habit at all There is nothing to learn. If the CPU ever reads this above chance, the harness is leaking the answer and every other number here is void.
ACCEPTANCE lefty — "break left when cornered" Heading-relative, and the actual product goal.

Latest measured:

compass  (POSITIVE)   n=10052   read 95.3%  chance 34.3%  z= +128.9
coinflip (NULL)       n=15292   read 34.8%  chance 35.0%  z=   -0.5
lefty    (ACCEPTANCE) n=144     read 74.3%  chance 50.0%  z=   +5.8

The null control is what makes the other two rows evidence: the same CPU that reads lefty at 74% remains unable to read an opponent with no habit, so the suite is not leaking the answer into the forecast.

Learning, and then winning

The claim is not that the CPU predicts you — it is that predicting you makes it beat you. cargo test --test domination holds board, seeds, and opponent fixed and varies only whether the CPU may remember. Since ADR-022 the memory invariant runs as five paired 90-game arms scored by expected points (a draw counts half in both arms), asserting mean and median paired gap ≤ 5 points with every per-seed gap published — one pathological seed stays visible on the record instead of vetoing the doctrine or being re-baselined away. A survival-competent persona is beaten ~80-90% by default play, so memory's job here is non-inferiority plus a genuinely earned read; the conversion claim is carried by the acceptance tests (a strict alternator — a habit no modal baseline can call — read far above chance end-to-end). The evidence rebuild that made these numbers honest, and the fabricated 81% they replaced, are ADR-020; the earlier failures and reverts are ADR-009, ADR-012, and ADR-014.

A read is only worth something if it reaches the wheel. read_rate is lift over the player's own base rate (scaled by which temperament is winning — see the Exp3 portfolio in the code), and it drives how much survival margin the CPU will spend on an intercept. Confidence buys commitment, never safety: the survival floor is untouched at every read rate, and the dodge, ring-evacuation and wall-follow layers never consult the hunt floor. A well-read player faces a CPU that takes intercepts it would otherwise decline. They never face one that suicides. See ADR-007.

Where the habit models plateau, and what took over

The habit family alone settles around 0.80 lift: a single global turn prior learns "this player breaks left" but not "…when the wall is three cells away". The headroom turned out to be why the player moves, not finer habit features: the ensemble now carries six errand models — {food, hunt, weapon} × {holds-their-line, weaves} — each a BFS route step toward an observed goal, elected by the same fixed-share weights as everything else. Against a committed forager persona the voluntary-turn read went from 6% (habit models only) to ~76-85% (ADR-012, ADR-014). Compensating with a difficulty multiplier would just restore the arbitrary clock ramp ADR-007 deleted.

Measured against its owner: 10% → 80%

The whole stack's report card, session by session against the one player with a long history (153 rounds; peak earned read = the statistically-proven evidence the difficulty spends):

session rounds CPU win % peak earned read
1 (pre-honest era) 48 10% 0.00
2 20 75% 0.00
3 (honest evidence lands) 13 38% 0.41
4 13 46% 0.63
5 59 80% 0.62

Honest caveats attached: sessions 1–2 ran on older builds, so the full arc conflates build improvements with learning; the clean single-build evidence is session 5 alone — 47 of 59 under one binary with the earned read pinned at 0.62. Meanwhile the drift alarm — its own anytime-valid evidence family — LATCHED on real play when the owner started scrambling his turn timing to fight the read, and the between-round ratchet (an exact minimum cut over the situation cells' co-movement graph, examples/drift_partition.rs) named exactly which region of his game moved. His timing entropy went up; the side read held at 0.71; the CPU kept winning through the noise he was deliberately generating. That sentence is the product.

The learning program: nine ways it studies you

ADR-021 turned "where else can it learn?" into nine measured surfaces, built kata-by-kata under two external design consults: a tactic ledger with Thompson-sampled preferences (which intercept kills YOU), a weapon opportunity book with a bounded bait-exploration floor, a per-cause death ledger that raises its escape floors against players who box it in, a persisted swerve grammar (your rhythm is read from round one when you return), a drift alarm (its own anytime-valid evidence family) that notices when you change your game and says so, an epistemic self-map of the situations it has never seen you in, and an envelopment alarm that feels its space collapsing before the box closes. Two more are deliberately gated: fleet warm-starting (policy written, waits for a second returning human) and any neural challenger (a plateau detector measures its entry gate weekly — and currently reports the target MOVING: the first player is measurably adapting to being read, which holds that door shut). Every aggressive consumer spends only round-boundary snapshots of family-gated evidence; everything else is narration. A committed golden brain file makes wiping a player's learning a test failure instead of an accident. The full prose tour of every method lives in docs/learning-methods.md, and the post-round 📓 TELL ME MORE button now writes from the CPU's complete dossier — the round's events set against every round you have ever played, ledgers quoted by name.

The turn book: reading the frames that decide games

The owner's own 45-round ghost corpus then exposed the next wall (ADR-020): global model selection let straight-frame volume (88% of play) crown always-straight experts, so the published forecast scored 9% on his voluntary turns while unelected models in the same ensemble scored 55% on those exact frames. The fix is class-conditional selection — a turn-HAZARD model (context cells over swerve cadence, food alignment, and pursuit pressure) for WHEN, a turn-scored book over the same roster for WHICH WAY, a no-knob derived gate deciding what gets published, and a fourteenth model (alt, the rhythm reader — a variable-order model over voluntary swerves) that the book elects on the frames it exists for. The book's precommitted side calls are scored through the same honest machinery as everything else, and they are where the CPU earned its first statistically proven read of the owner's play — his alternation, called at 71% on genuine choices across a thousand prequential events.

The metric, and why the obvious version is wrong

Runtime flow: a genuine turn is identified, the precommitted forecast is scored against reality and the base-rate rival, evidence accumulates through scheduled looks, and only a proven read closes the intercept gate

The obvious fix — compare against uniform chance, as rps.shaal.dev does with its stated 33% baseline — fails for a subtler reason. An RPS player really is near-uniform; they are trying to be random. A worm player is not. Against a straight-driving opponent, "always predict Straight" scores 98%, which against a 33% baseline reads as "it has found a pattern". It has found nothing, and the error is biased toward flattering the CPU.

So the baseline is the player's own base rate — the realized accuracy of an online "predict their commonest move so far" rival — and significance is an exact McNemar test over the frames where the CPU and that rival actually disagreed. Against a very predictable player the two agree almost always, and thousands of frames can carry a dozen frames of real evidence. That gets reported honestly rather than as a large green number.

The slipstream

Punch a hole in the arena wall and slip into the outer corridor and time itself takes sides: the corridor worm runs at 25% of original speed while the arena worm runs at 4× — with the reaction tax priced symmetrically, because a body moving 4× with a normal-rate mind rams trails exactly like a human would (the CPU re-decides at its normal wall-clock rate while accelerated, wall reflexes on, trails unseen). Bolts fly at full speed regardless: light does not slow down. The screen drops into a hyperspace field — streaks tearing outward from your worm, chromatic warp rings, a focus vignette — and every rule is symmetric: if the CPU takes the corridor, the same physics serve you.

The arsenal

The corridor is two lanes wide — turning and overtaking out there are real maneuvers. The laser ricochets off the arena walls, kills what it visibly touches (the beam exists across the frame it fires into: anything that steps onto the line during the discharge frame is hit — solid beam means hot, the fading afterimage means spent, a low clank means you truly whiffed), and its fifth wall strike punches a hole. The bomb is a fifteen-second decoy: indistinguishable from food until its final two seconds, when it flashes — softly, then hard — and detonates, punching walls. You dodge it by memory: I planted that. The tri-shot is napalm: three full-length bolts at double speed — a fired bolt cannot be outrun — and anything they touch catches fire on contact, dropping burning ground where they stop; a worm aflame burns down on a wall-clock schedule, up to five segments in the first second, three in the next, one in the last, shedding embers as it shrinks; burned past the head is dead. One law covers all three: you are immune to your own discharged weapon — your laser, your blast, your fire. The counterplay to everything else is attention.

World rules are versioned (v1–v11) and every recorded ghost replays under the exact physics it was played on.

Playing it

Three altitudes: play the duel, audit the adaptive-system claim, or borrow the evidence discipline as a test harness

Worm is useful at three altitudes: as a finished game, as a transparent adaptive-system study, and as a test harness for claims that are usually hand-waved.

  • Meet a rival that remembers your tells — play several rounds in the terminal or browser; the opponent persists its per-player brain, notices genuine turn habits and rhythm changes, and can explain which evidence it earned.
  • Try to prove the learning claim false — run the persona suite: a fixed-direction player is the positive control, a coin-flip player the null, a corner-left player the acceptance case. A harness that cannot say no proves nothing. cargo test --test persona_learning -- --nocapture
  • Study an honest adaptive-difficulty loop — trace how forecast evidence is separated from board knowledge, how aggression spends only round-boundary snapshots, and how schema changes preserve a returning player's learned history.

Terminal

cargo run --release

WASD or arrow keys to steer · Space fires a held power-up · P pause · Q quit. The brain persists to $XDG_DATA_HOME/worm/worm_brain.bin (override with WORM_BRAIN).

Browser

wasm-pack build --target web --out-dir web/pkg --features wasm
python3 scripts/serve.py 8080   # local dev; no-store headers, so rebuilds never serve stale

The browser build keeps a per-player brain in IndexedDB, keyed by an identity stored beside it in the same database — deliberately, so the two share an eviction fate. See ADR-005 for why that mattered.

Development

From your keypress to an earned intercept: on each game update, the world identifies whether you had a real choice, captures your local situation, and scores the forecast already committed for this frame. The brain updates its predictors and evidence ledgers, then the decision layers choose a legal move without dropping the survival floor. At round boundaries, the earned read is snapshotted and the sectioned brain persisted. The browser exposes the same decisions and evidence through versioned state — the canvas and the terminal cannot tell two different stories.

Architecture: terminal and browser clients share one Rust game core — WormGame owns world rules and update order, CpuBrain owns learning and evidence, web-state serializes browser telemetry, and a sectioned wire format persists the brain

For the deep map — bounded contexts, invariants, and the academic lineage of every mechanism — see docs/architecture.md.

scripts/eval.sh                                       # THE GAUNTLET — run before every merge
cargo test                                            # unit + integration
cargo test --test persona_learning -- --nocapture     # the claim, ~45s
cargo run --release --example engagement_probe -- browser   # engagement + death census
cargo run --release --example intent_probe -- 24 1500 20260805  # errand-model reads
node scripts/page_probe.mjs                           # the SERVED page, headless (needs playwright)
cargo run --release --example ghost_eval -- rounds.json  # read of the REAL player (EXPORT MY ROUNDS)
cargo bench --bench cpu_ai_bench                      # CPU benchmarks

The gauntlet is the improvement flywheel (ADR-013): champion = main, candidates prove themselves against the fixed seeds, and the measured numbers go in the commit message — the receipts are the ledger.

Layout: src/lib/game.rs (rules, board, power-ups), src/lib/cpu_ai.rs (opponent model, decision layers, brain persistence), src/lib/web_state.rs (browser wire format), src/main.rs (terminal client), web/ (canvas client), tests/ (the falsification suites — the claim lives here), examples/ (the eval probes), scripts/ (gauntlet, dev server, page probe), benches/ (CPU benchmarks).

The brain is serialized in a sectioned format so that a schema change costs only the section it invalidates. What the CPU has learned about you — habit priors, head-to-head record — is encoding-independent and survives every future change to the feature space. That is a hard requirement, not an optimisation: a game whose premise is "it remembers you" cannot afford a release that resets everyone.

Decisions

Architecture decisions live in docs/adrs/ and are treated as living documents — if one disagrees with the code, the ADR is the bug.

  • ADR-001 — opponent-centric CPU
  • ADR-002 — responsive browser arena
  • ADR-003 — truthful CPU telemetry
  • ADR-004 — fixing the CPU's fundamentals
  • ADR-005 — durable identity and brain integrity
  • ADR-006 — measuring against the player's own base rate
  • ADR-007 — difficulty earned by reading you, not by the clock
  • ADR-008 — the bomb becomes a mine disguised as food
  • ADR-009 — learning must convert into winning
  • ADR-010 — what the CPU learns from, and what it may know
  • ADR-011 — intent inference and an engaged CPU
  • ADR-012 — errand twins, kinematic traps, and honest silence
  • ADR-013 — the improvement flywheel
  • ADR-014 — the codex corrections: what survived measurement
  • ADR-015 — nightly Darwin: continual improvement with human promotion
  • ADR-016 — ghost replay: the real human becomes the benchmark
  • ADR-017 — automatic round collection: every visitor feeds the flywheel
  • ADR-018 — the beatable opening: wits are earned, not given
  • ADR-019 — the CPU's notebook: LLM depth, on request only
  • ADR-020 — the turn book: reading the frames that decide games
  • ADR-021 — the learning program: nine measured surfaces
  • ADR-022 — the world-rules program: corridor, decoy, napalm, input queue, reach
  • ADR-023 — laser simultaneity: the beam exists across its frame

A note on the numbers in this repo

Win-rate and read-rate figures are quoted from seeded runs, and the seeds are in the harnesses. Where a change turned out to be measurement-neutral it is recorded as measurement-neutral rather than credited with a delta — several fixes in ADR-004 are exactly that, kept because a test proves them rather than because a number moved. Where a claim has been withdrawn, the withdrawal sits in the ADR next to the original claim.

Numbers measured against anyone other than the author are a separate class. Since the v9 build every round played in the browser records a ghost log — the complete input streams of both worms plus the round seed (ADR-016) — captured locally in the player's own browser and exported only by their hand (EXPORT MY ROUNDS). No such exported number appears in this repo yet; when one does it will be quoted with the export's date and round count, and marked as measured against a real player rather than a scripted persona. (The owner's own sessions above are the author's data, collected on his own builds — a different class again, quoted as such.)

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages