The game plays itself
Blood Rogue has 979 test files. The guard that actually catches balance bugs is a bot that finishes the game a few hundred times a night.
There is a bot that has finished Blood Rogue more times than I ever will. It has played every one of the eight classes through all five acts, in configurations I would never pick, and it files a report about it. This post is about that bot, why a solo deckbuilder ended up with a simulation engine bigger than some of my production services, and what it caught that I couldn’t.
In the cards post I wrote about how the game’s numbers lied to me and how I learned to fix the cards before trusting my taste. The uncomfortable follow-up question was: fixed according to whom? I can hand-play maybe two runs in an evening. A balance change touches eight classes, five acts, three skill trees each, and an economy. Two runs of anecdote against that surface area is not measurement. It’s mood.
The oracle
The answer lives in src/sim, 59 TypeScript modules that have nothing to do with rendering the game. The core of it is a decision-beam policy engine (sim-policy-decision-beam.ts) that plays the game headless: at each decision point it generates candidate actions, scores them against threat and power-curve and reward-EV models, and follows the best line the way a strong player would. It runs scripted builds for all eight classes, which means it can play the awkward ones honestly. The bot does not get bored of Amazon javelin builds. I do.
Around that core sit the harnesses, all npm run sim:* entry points: sim:power-curve for hero strength against encounter design ceilings, sim:progression-sweep for RNG spread across runs, sim:balance and sim:failure-cohort for finding where runs die, sim:orchestrate for batching the whole thing overnight. There is even a sim:ml-dataset export with a baseline trainer, because once you have a bot playing thousands of runs, build-quality prediction is sitting right there.
What it caught
The reports are blunt in a way playtesting never is. One combat-score report from May flagged that boss power ratios ranged from 1.84x to 4.71x of target, against a band of 0.90x to 1.10x. Act 1’s final boss was receiving a hero nearly five times stronger than the encounter was designed to challenge. Observed boss win rates were 100% across the board, against a 70 to 85 percent target. Hand-playing, this felt like being good at my own game. The harness called it what it was: a difficulty curve that had quietly collapsed.
Class parity was worse. Amazon was completing Act V in 15 percent of runs while Paladin and Sorceress completed at 90. A mercenary audit found the hired merc performing around 42 percent of party actions for Druid and Necromancer builds, against a 15 to 25 percent target. The merc was supposed to be help. For two classes it had become the protagonist.
None of this was visible from inside a run. Every individual fight felt plausible. The failures only exist in aggregate, and aggregate is exactly what a human playtester of one cannot see.
Bands, not vibes
The load-bearing idea in all of this is the target band. Not “bosses should feel dangerous” but “boss win rate belongs between 70 and 85 percent, merc action share between 15 and 25.” Writing the bands down was harder than building the harness, because a band is a design opinion you can now be wrong about in public. But once they exist, tuning stops being an argument and becomes a diff: run the suite, read which bands are violated, fix, rerun.
The reports also cleared suspects, which matters as much as convicting them. The same failing report showed the AI oracle making near-optimal decisions 97 percent of the time, above its 85 percent floor, and enemy stats clean against their curves. The problem was the numbers, not the brain reading them. Without that line I would have spent a week tuning the wrong layer.
Where it lies
The harness has one built-in dishonesty I have to keep correcting for: the bot plays near-optimally, and humans do not. A band tuned so the beam-search policy wins 80 percent of the time can still bury an actual person. So the bands are set with slack for human sloppiness, and a few of them I periodically sanity-check the embarrassing way, by playing the game badly on purpose and seeing if it forgives me.
The other temptation is Goodhart’s. Once the suite is green it is very easy to believe the game is good, when all I actually know is that the game is calibrated. The bot cannot tell me a fight is boring, and it has no opinion on whether the second act drags. Calibration buys me the right to spend my taste on those questions instead of on arithmetic, and that is all it buys.
Blood Rogue has 979 test files and close to twenty hygiene gates, and I trust them the way I trust seatbelts. But the guard I check first after any tuning change is the bot. The tests know what the code does. The bot knows what the game is like, at a scale of experience no human author of a solo project can ever accumulate. I built a player better than me so I could stay the designer.