Generality · 3 games
Recorded run · headless crowd runs, no model calls

Does it work beyond one game?

The same crowd harness ran on two more games far from a fighting game: a longboard physics ride and a nine-seat heist and trial. Each needed a thin adapter (seeded sim, action vocabulary, invariants). The harness carried over; the persona rules did not.

2,831 rounds199 rounds/sreplays 45 of 45 match

Market Brawl

3D physics brawler (Three.js + Rapier)

players
1000 personas
outcomes
891 win, 1940 loss
speed
Measured on the current build, 11 worker threads (README, Throughput). The recorded run's own wall time was not logged.
What the crowd found (29 flagged rounds: 10 from the 2,831-round main run, 19 from the 2,875-round sweep-nerf run)
  • airborne · 21 rounds · fixed by hand, replay-verified 21 of 21 seed 822230 · p0009 at 16.6 s
  • oob+airborne+softlock · 4 rounds · fixed by hand, replay-verified 4 of 4 seed 634310 · p0537 at 13.8 s
  • crash:RuntimeError: unreachable in throwProp (props.js:303) · 3 rounds · fixed, replay-verified 3 of 3 seed 901214 · p0233 at 11.32 s
  • stuck:KNOCKDOWN+airborne+softlock · 1 round · Ultra's patch fixed 1 of 1 seeds but is not shipped; a narrower hand fix is shipped and replay-verified 1 of 1 seed 529060 · p0653 at 18.5 s
How replays held up

45 of 45 replays match. Sampled recorded rounds replayed event for event in Node (README, Replay).

What the adapter needed

The original target. Needed window and DOM shims plus Three.js in Node to run headless.

What had to change
  • Personas were written for this game: their rules use fighting vocabulary (distance, rival, punch).
  • Real-time contact between two physics bodies is where the crowd found the most bugs.
Reproduce
node engine/crowd.mjs --personas data/run-main/personas.json
200 rounds12.1 rounds/sreplays 22 of 22 match

Homeground

Longboard downhill physics on map tiles (TypeScript)

players
100 personas + 100 fuzzers
outcomes
164 timeout, 35 finish, 1 oob
stalled
76 rounds (counted, not a bug)
speed
One core, 16.53 s wall, 2670 simulated seconds per wall second.
What the crowd found
  • stuck · 1 round · not a bug: nose-first at rest, by design; pump while carving escapes seed 100132 · fuzz29 at 111 s
  • oob · 1 round · real design gap: no map boundary, the rider leaves the 1 km tile seed 100135 · fuzz32 at 194.3 s
How replays held up

22 of 22 replays match. Every round with an event plus every 10th round, replayed from recorded inputs alone; hash and event list compared.

What the adapter needed

Board physics was already three.js-free with a fixed 120 Hz step. Real levels need the live tile server, so the adapter builds seeded synthetic tiles. About half a day.

What had to change
  • Persona rules (fighting vocabulary) were dropped; archetype, skill and reaction time carried over.
  • New action vocabulary and situation buckets: road, offroad, wall, air, bail.
  • A first "softlock" invariant gave 16 false positives; the game has no fail state, so not moving is a choice. It is now counted as "stalled", not a bug.
Reproduce
node --import tsx adapters/crowd.mjs --game homeground --n 100 --fuzz 100 --seed 1 --maxT 240
200 rounds11.1 rounds/sreplays 20 of 20 match

Clean Getaway

9-seat heist, investigation and trial (state machine)

players
100 personas + 100 fuzzers
outcomes
200 closed
speed
One core, 18.01 s wall, 1534 simulated seconds per wall second.
What the crowd found

No invariant events. No crash, NaN, out-of-bounds actor, duplicate seat, overdue phase or case closed without an outcome.

How replays held up

20 of 20 replays match. Every round with an event plus every 10th round, replayed from recorded inputs alone; hash and event list compared.

What the adapter needed

Pure engine with explicit time and its own seeded RNG; only player ids needed seeding. Fuzz goes through the server's own parseAction. About half a day.

What had to change
  • Moves are typed actions with arguments, not a key mask, so the replay records actions.
  • Competent play is the game's own bot; the archetype sets how a persona deviates from it.
  • Zero events is a real result: a well-tested state machine with input validation held up. Constructed bad states do trip the detectors in tests.
Reproduce
node --import tsx adapters/crowd.mjs --game clean-getaway --n 100 --fuzz 100 --seed 1

Market Brawl controls

controls checked: 92 of 92 work

0 failed · 0 untested. Checked with keyboard input and a simulated standard gamepad in Chrome. Physical controller hardware is untested.

Honest limits

How to add a game: adapters/README.md. Full write-up: docs/generality.md.