A board game night, an itch about mental arithmetic, and the month I spent building my way out of it. This is the whole story, from the person who built it, told from the project's own records.
01 · The itch
I run a board game night with local friends, and we got into a card game called Regicide. It is a genuinely clever cooperative game by Badgers From Mars, played with a normal deck. You and your friends throw number cards at royals, each suit has a power, and the royals hit back.
Two things nagged at me. The experience felt vanilla, because a standard deck carries no atmosphere. And the game asks four people to hold a lot of arithmetic in their heads at once: damage dealt, shields banked, what each suit does, what the royal strikes back for. I kept looking for an app to keep score. I did not find one I liked.
So I built my own, and reskinned the whole world while I was at it.
02 · Forty-eight hours
The first playable version took about 48 hours. A Flask server holding the rules, a browser client, a room code you send to friends. It worked. It was also, in my own words at the time, vanilla: a grey page with numbers on it.
Everything this page is about happened after that. Forty-eight hours to a working game, and then a month to a world. The gap between those two numbers is where the real work lives, which is why the fast demo is the least interesting part of the story.
03 · The world
The original game is an uprising. Number cards overthrowing royals. So that became the name of the system underneath it: Uprising. The skin became the Cthulhu Mythos, and the game became Xenocide. Twelve entities of a Pantheon stand in for the twelve royals, and four cosmic Forces stand in for the four suits: Blood, Dream, Swarm, Void.
The lore had rules of its own. Every entity comes from Lovecraft, Chambers, or Clark Ashton Smith, writers whose work is in the public domain, and none of the art is copied. I generated the monsters from the original written descriptions and then argued with them until they were mine. One came back with googly eyes, and I wanted snake eyes, so he got snake eyes. I even reordered the Pantheon on purpose, ranking it by threat rather than by Lovecraft's own cosmology, and the project records why:
The Unbound are not cosmologists. They are people counting how much Ruin a thing absorbs before it stops. the project's own design notes
That line turned out to be measurably true, which I did not expect. There is a number at the end of this page that proves it.
04 · The language
I thought I was reskinning a game. What I actually did was rewrite every word in it. Health became Dominion, attack became Strike, damage became Ruin. The draw pile became the Gathering, the discard the Grave, the Jester the Heretic.
There are 1,753 player-facing strings in the product today, and a script fails the build if a single one carries the old vocabulary. The copy is under regression test, the same as the code. Every term was chosen against a collision and argued once, so the same word never means two things.
The same care went into the parts of the site that are not the game. The rules live in the Tomes. The bestiary is the Pantheon. There is a Lexicon for the vocabulary, a Lore page and a Tale for the fiction, and a Chronicle that is really just the changelog wearing a better name. None of those are decoration. A page called the bestiary is a menu item. A page called the Pantheon is somewhere you go.
And it is built to grow. Xenocide is the world, Uprising is the system it runs on, and there is a quiet naming discipline underneath so a sequel does not collide with anything: releases are named for people who led real uprisings (the current two are Wallace and Bruce), and future worlds get named for places. Most of this is invisible to a player, which is the point. It is the difference between a reskin and a world, and it was as much of the work as the engine was.
05 · The art
No card in the game is a finished picture. Every card is composited at the moment it is shown, from three layers: the card stock, the entity's artwork, and the rank and suit mark. That means the same painting sits on a normal card, the enemy's warmer stock, and the Heretic's violet stock with no edits. Invert the whole deck from parchment to black and the art follows, because the art only ever paints in two colours: the ink and the stock.
Getting there was a real engineering problem wearing an art-project costume. I generated raw art in ChatGPT, finished and composited in Claude, and the pipeline between them is code. I learned far more about alpha channels than I meant to. The source art ships as greyscale on transparency, and one lesson cost me three cards before it became a rule: a silhouette carried only in brightness collapses on a light background. Four monsters in a row came out as beautiful engravings that rendered as featureless black blobs on the light skins. Now there is a one-line check that predicts it before the card is ever built.
The four Forces got the same treatment. Each suit is redrawn as a living thing: a heart bled into a lattice of tentacles, a diamond around a slit pupil, a club gone to spores, a spade with a keyhole through it. And when a Force is denied in play, a second version shows it bound in chains. All of it paints through the same two-colour contract, so every one of those marks reskins with the deck and none of it is a separate image to keep in sync.
06 · The arithmetic
The engine is the authority and the browser holds no rules at all. Every number four people used to track in their heads is computed and shown before you commit: Dominion falling, Void banked against a Strike, what a doubled Swarm play actually deals.
The deeper fix came from measuring how the game was actually played. Roughly half of every interaction was settling the enemy's counterattack, and in two thirds to four fifths of those there was exactly one cheapest way to pay. Most of the time the player was doing sums, not making a choice. So the play and the payment became one action, the board runs an exact solver over your hand and shows you the three best ways to cover what is coming, and the half of the game that was ceremony disappeared. The half that was interesting stayed.
07 · The simulations
A card game reskin is easy to get wrong in a way you cannot feel until someone has lost twenty times. So I did not balance it by feel. I built eight simulators and ran the game against itself, Monte Carlo style: play a descent to the end, thousands of times, across every player count and every rule I was tuning. Over the month that adds up to hundreds of thousands of simulated descents, each one a complete game.
That is how I found that solo play was literally unwinnable. Zero wins in five hundred simulated games. The fix was a small reinforcement rule, tuned against simulation until it read 42%, then checked against real people, where it lands at 75%.
The part I am actually proud of is the honest answer to whether you can trust the model. The simulator has a known blind spot, and I measured exactly how big it is. The bot under-reads a real human by about 55 points, so the standing rule written into the project is: trust the model for the clean rate, never for the win rate. Knowing what your model cannot predict is worth more than the model.
08 · The audit question
This is the question a CFO asks about a calculator and a player asks about a rules engine, and the honest answer here is a set of stories about getting it wrong. The project runs twelve automated checks before any deploy, on markup, stylesheets, colours, fonts, copy, navigation and the rules themselves, plus 425 tests. Almost every one of them exists because something got past me.
The check that could not fail
A restructure shipped the main page with unbalanced HTML tags. The browser silently repaired the nesting, and two whole panels became inert, invisible content. Every check passed, because the tag count was measured per section instead of per file, the console was clean because bad nesting is not an error, and the visibility probe used a property that returns the same answer for a working panel and a missing one. The project's notes now carry the moral in capitals: a check that cannot fail is worse than no check.
The deploy that overwrote the database
Two days into the project, the deploy tool was pointed at whatever service it was last linked to, which was the database. It built the app and deployed it over the database, and reported SUCCESS. The site kept serving the old build while the database stopped being a database. The data survived. The checklist that now says confirm the service name before every deploy, every time was written that evening.
The hidden hands that weren't
The worst one. For weeks, the state sent to every player carried two fields that together rebuild everything: every hand, the draw order, which entity comes next. In a co-op game whose rules forbid telling your partner what you hold, either seat could read the other's hand with one line in a browser console. I found it not with a test but by asking a different question, which was what is the risk of revealing hands for finished games. That could not be answered honestly while the live ones were leaking everything.
The rule that came out of all of this: every new safeguard has to be watched failing before I trust it. Plant the fault, watch the check catch it, then keep the check. A test that has never failed is decoration.
09 · The notation
Chess has notation that lets any game be replayed move by move, forever. Xenocide has the same thing. Every game records the deck's exact starting state and every move made, and from those two things the engine rebuilds any moment of any game ever played, exactly, in a third of a millisecond.
There is a subtlety in that I am fond of. A replay is only honest while the rules that recorded it are the rules running now. The dangerous failure is the replay that does not break. Change the engine slightly and an old game can replay cleanly into a plausible, well-formed game that never happened. So the engine checks the rebuilt outcome against the recorded one, and if they disagree, it refuses to show the replay at all. The record is the truth. The replay has to earn it.
10 · The table
I finally played it in person, with the same friends, all of them Regicide veterans. And they still needed me to walk them through the first game. One of them swore the engine had shorted him cards on a play. I pulled that exact game out of the archive and replayed it move by move, and the engine was right: he had been skipped because his hand was already full, and the game had simply never said so.
That is the most useful bug report I have gotten, because it was not a bug. It was the game being correct and illegible at the same time. The math was right and the board did not show its work. So now every card that arrives in a hand is recorded, and after a game ends you can open any turn and see exactly who drew what. The hardest part of how do we know it is correct turned out to be making correct legible, which is a different and harder problem.
11 · A number I did not arrange
All four of the final entities have the same amount of Dominion. But Cthulhu denies the one Force that doubles its damage, so he is the only one you have to pay full price for. Across 216 recorded fights, he is measurably the most expensive thing in the game to put down:
| Final entity | Card value spent to kill one |
|---|---|
| Cthulhu | 40.1 |
| Yeb | 28.6 |
| Yog-Sothoth | 28.0 |
| Shub-Niggurath | 27.7 |
Nobody designed that. The lore says the Pantheon is ranked by how much Ruin a thing absorbs before it stops. The arithmetic, measured months later, agrees with the fiction.
12 · The stack
For the people who want to know what it runs on: Python and Flask on Railway, with Postgres behind it. One worker process, the rooms held in memory and written through to the database on every change. The client is one HTML file with Alpine.js and Tailwind from a CDN, and no build step at all. Live updates are server-sent events that ship the full state every frame. There are no deltas, so there is no delta protocol to get wrong.
The art pipeline runs across two models: raw generation in ChatGPT, finishing and compositing in Claude. The twelve pre-deploy checks drive a real browser through Playwright, so a broken layout is caught by something that actually renders it rather than by a rule that guesses.
The one place it is fancy is theming. Every colour, gradient and typeface in the game lives in a 66-token contract, and a gate fails the build if any file outside it names a colour. That is what makes a whole new skin a one-file change, and there are eleven of them. It is also why the failure stories above are mostly about the gates. When the rules are this strict, the interesting bugs are the ones that slip between the checks.
13 · Now
Xenocide is live, free, and still a work in progress. The balance conversations above are from this week, not from history. It plays solo or with friends from one shared room code, in a browser, on anything.
The itch was arithmetic. What I got was a sandbox that taught me a multi-model art pipeline, adversarial testing, replay systems, Monte Carlo balancing, and how much of a good product is really its language. And my friends and I got a better game night out of it.
The fiction outgrew the game somewhere along the way, and I am now writing it down as a novel, narrated by the one character on your side who knows how the Pantheon works.
Play XenocideXenocide · Uprising · rules descend from Regicide by Badgers From Mars · entities from Lovecraft, Chambers and Clark Ashton Smith, public domain · written from the project's own chronicle, September 2026