It started as a sandbox

I wanted to get a feel for AI-assisted coding tools — Claude Code, Codex — in a low-stakes environment where breaking things was fine. A browser game seemed like the right vehicle: self-contained, no prod database, no users to disappoint.

I picked a premise I knew was fun: Dice Wars. The Flash-era classic. Roll dice to attack adjacent territories, biggest army snowballs. Simple enough that I could focus on the tooling rather than the design. Or so I thought.

Six weeks later I had five asymmetric character classes, a procedural hex map generator with acceptance criteria, a FastAPI telemetry service recording every dice roll, and a stat dashboard I check more than I probably should. The tooling became a background concern. The game took over.


How the game works

The rules are genuinely minimal.

You start with a random slice of a procedurally generated map — a patchwork of irregular coloured territories, each one a cluster of hex tiles that reads as a solid blob. You and up to seven opponents begin scattered across it. Objective: own everything.

Attacking is one click. Select your territory, click an adjacent enemy territory. Both sides roll all their dice and sum. Higher total wins. Attacker wins: you capture the territory, your dice advance in minus one. Defender wins: your attack is repelled, you’re reduced to a single die on the attacking territory.

Reinforcements are the mechanic that makes this a strategy game. At the end of your turn, you receive bonus dice equal to the size of your largest contiguous group of territories. Not total territories — the biggest connected blob. Fragmented territory generates almost nothing. A solid connected bloc snowballs.

That one rule creates the entire strategic texture. Grab fast but stay connected. Chokepoints are worth defending at a loss. Cutting an opponent in half collapses their income immediately. The late game turns into tense standoffs until one roll cracks something open.

An 8-player Epic game in progress — territories changing hands, rankings shifting in the side panel


The shrines

Early on I added shrines — special territories marked with a ★. They behave differently from normal territories in a few ways:

  • Higher dice cap: normal territories max out at 8 dice, shrines at 10
  • Minimum floor: a shrine never drops below 2 dice after attacking, win or loss — it can’t be stripped bare
  • Guaranteed reinforcement: the shrine gets a die first at end of turn, before random distribution
  • Aura: each of your own territories adjacent to the shrine gets a +1 guaranteed die (shown with a dim ◆ indicator)

The shrine mechanic does something interesting to the risk calculus. Holding a shrine isn’t just a territory — it’s a node that warps the value of everything adjacent to it. You’ll defend an aura territory harder than a territory of equivalent size elsewhere on the map, because losing it means losing the aura bonus. It also means attacking into a shrine aura is expensive: the shrine can’t be worn down easily, and the neighboring territories keep refilling.

Shrines turned out to be the moment the math got interesting.


Five guardians

The game has a character select screen. Each player — human or AI — picks a guardian before the map generates. Five options, each with a passive and an active ability that meaningfully change how you play:

Hippo gets to manually place one reinforcement die each turn before the rest distribute randomly. One die placement doesn’t sound like much until you realise you always have a frontline territory that needs it more than anywhere else. High floor, consistent.

Hedgehog fills weakest territories first during reinforcement. Defensively solid — you never bleed out a border territory to zero while inland territories sit at cap. It doesn’t generate more dice, but it wastes fewer.

Fox banks a stored die every other turn and can spend it as a critical multiplier on an attack. The stored dice accumulate (up to a cap), so the power compounds if you resist using it. Two-hop flanking passive: Fox can attack across two territory hops from border territories, making it very hard to feel safely tucked away from.

Owl has a passive two-hop attack range like Fox but for all attacks — Owl sees further. The active ability is a Dice Transfer: move dice from one of your territories to an adjacent friendly one. Lets you concentrate force without committing to an attack.

Turtle gets 2 dice back to a neighboring territory any time it loses a defense. It’s the only guardian that turns taking damage into a resource. Hard to snowball, but very hard to finish off.

The AI cycles through the five guardians deterministically, so in a full 8-player game you’ll face at least one of each.


The map

The map generator produces irregular coloured territories from a hex grid. Hexes are the visual scaffolding — what matters in gameplay is the blob they form. Internal edges within a territory are hidden; only the outer border of each cluster is drawn. The result reads like a contested piece of ground rather than a grid.

A freshly generated map before the first move — eight starting positions across irregular territory blobs

The generator has acceptance criteria. A proposed map is rejected if territory sizes are too uneven — a tiny starting territory with 2 hexes versus a sprawling 15-hex territory produces a wildly unfair game. The stats service actually records generation attempts and acceptance rates per map, so I can see how often the map generator throws away its own work.

The mapId is stamped on every game record, so I can eventually correlate map topology with game outcomes. I haven’t done that analysis yet, but the data is there.


What the tools actually felt like

Claude Code handles the kind of work that benefits from holding a lot of context simultaneously: “this change to resolveAttack() in game.js needs corresponding updates to the AI logic in ai.js and the render pass in render.js.” That’s tedious to track manually and exactly the kind of thing where the tool earns its keep.

Codex was more useful for the boilerplate-heavy parts — filling in schema.sql, wiring up FastAPI endpoints, the chart.js setup in the dashboard. Directed generation of code you know exactly how should look.

Neither tool replaces thinking. The guardian ability design, the shrine balance, the question of whether Fox’s stored critical is too swingy in epic mode — that’s all still just sitting down and working it out. The tools speed up the translation from “I know what I want” to “here is working code.” The game design itself doesn’t compress.


It’s live

The game runs at dice.hippotion.com. Single HTML file served from a Kubernetes ConfigMap on my homelab. No accounts, no install. It runs fast — AI turns are instant when you toggle off animations, and a full game can be over in five minutes or stretch to twenty depending on how the map falls.

Stats dashboard at game-stats.hippotion.com. More on that in the next post.