Backpack Battle: balancing an autobattler so no class beats every other class
Backpack Battle is an inventory autobattler. Between fights you buy items and pack them into a grid backpack. Where things sit matters: a whetstone only boosts a weapon if it touches one of the weapon's star cells. Then you watch a fully animated, turn-based 3D fight play out. Win ten rounds before you lose five lives.
If you've played the PC game Backpack Battles, you know the idea. It's a genre I love, and the mobile versions I tried had the same complaints in their reviews: forced ads that grow over time, fiddly touch dragging, accidental sells, no tutorial, and subscription-only Remove Ads. Those complaints shaped this game as much as anything I wanted to add.
Why this genre
The search research for "backpack battle" on Google Play showed a top-ten median around half a million installs, three small apps and three young apps in that top ten, and no top-ten title containing the exact phrase. Less demand than some genres, but much easier to rank in.
Classes, items and recipes
- 3 archetypes: Warrior, Ranged and Magic.
- 12 specialisations, four per archetype: Knight, Berserker, Paladin, Duelist; Deadeye, Crossbowman, Hunter, Ranger; Pyromancer, Frost Mage, Necromancer, Arcanist.
- 101 items and 20 recipes. Some items combine when they touch, and some recipes chain.
You face one rival per run who keeps their pack all game and shops each round on the same income as you. Extra packs (from a 1×1 pouch to a 2×3 backpack) are bought and attached, rather than the board growing by itself.
Balance is a web, not a ladder
This was my one firm design rule: no class may beat every other class. A class can be generally strong, but never strongest against everyone. Played well, some matchups should be lopsided one way, some the other, and some even. If one class is best, everyone plays it and the variety dies.
You can't check that by feel, so there's a simulator:
npm run simulateplays whole games at about 1.3 ms per round, after caching item shapes and lookups (it started at 26 ms).--matrixplays every class against every other class, both sides with the same smart AI and the same gold, and prints a win-rate grid.- An independent validator checks every board every round, so a sim bug can't pass as a balance result.
The first matrix was a ladder: Barbarian 71% average, Rogue 64%, Knight 39%, Mage 26%. And whoever moved first won about 65% of fights.
Two traps in measuring balance
Survivorship bias. My first matrix played full runs (first to 10 wins or 5 losses). Even mirror matches, a class against itself, came out at 57–60% for the player. A run that ends early on losses contributes fewer fights than a run that goes long. The matrix now plays a fixed 12 rounds and counts every fight. Mirrors then come out 46–54%, and they're printed in brackets on every matrix as a sanity check.
Catch-up rules hide imbalance. The game has a rematch catch-up mechanic that helps whoever is behind. Great for players, terrible for measuring, because it pulls every matchup toward 50%. The matrix runs without it.
Building the triangle
After seven tuning passes, nobody beat or lost to everyone, and class averages sat between 46% and 55%. The Knight became a hard counter: 67–69% against physical damage, 23–30% against magic and piercing.
The archetype triangle was too soft, though: Warrior beat Ranged 52%, Ranged beat Magic 56%, Magic beat Warrior 52%. Tuning numbers stalled, and it's easy to see why in hindsight. Power in a triangle is circular, so making one edge stronger weakens another.
What worked was one damage-meets-defence rule per edge, each with its own dial: shields catch arrows, arrows shred energy shields, and spells melt armour. The triangle is now 59%, 59% and 62%, with lopsided matchups in the matrix up from 6 to 11.
Locking armour and weapons to archetypes helped too. When every class could buy steel, cloth and daggers, builds converged and no matchup was lopsided. Purity created counters.
Fights that ended too fast, or never
- The sim found a 51-damage critical hit killing a 75-health Berserker on turn 1. New rule: no single hit takes more than 25% of max health. The shortest fight in 300 games is now three turns.
- Some fights stalled forever. From turn 14 fatigue sets in: true damage each turn, growing by 2% of max health per turn. The median fight is still 13 turns.
- Fatigue started out invisible. My playtest note on that was one swear word and a request for a grim reaper icon. It's now a debuff chip like every other status, and it appears only when fatigue starts.
First-fight fairness
The most important fight in the game is the first one. I measured it by simulating a novice who follows the tutorial coach and then fills their pack: they won the first fight 31% of the time. A smart AI won 60%. A player who only did the coached step won 6%.
A fight balanced for a good player is lost by a new one. So the first run's rival now gets 30%, 60% and 85% of its gold in rounds 1 to 3, measured over 1,000 games per starting class.



Next week: how the fights are animated, with three.js and free 3D characters on a phone.