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 soob+airborne+softlock · 4 rounds · fixed by hand, replay-verified 4 of 4 seed 634310 · p0537 at 13.8 scrash:RuntimeError: unreachable in throwProp (props.js:303) · 3 rounds · fixed, replay-verified 3 of 3 seed 901214 · p0233 at 11.32 sstuck: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 soob · 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
- Commercial games: not testedThe crowd harness was only run on games Parth owns or that are MIT licensed. Native engines (Unreal, Unity) would need an in-engine harness; that is future work.
- Persona rules are fighting-game rulesThe Nemotron-written rules (distance, rival, punch) were dropped for both games. Archetype, skill and reaction time carried over. Rewriting rules per game would need a model call per game; not done.
- Bug yield depends on the gameOne real design gap in Homeground, none in Clean Getaway. Market Brawl has the most because two physics bodies collide in real time, which is where a crowd earns its keep.
How to add a game: adapters/README.md. Full write-up: docs/generality.md.