Skip to content
04Dominó Caribe

The model had to ask

A web game of Caribbean dominoes, built in about two weeks with Claude Code. The code came mostly from the model. The rules came from the people who actually play, and the model was told, in writing, not to make any up. It’s live, playable, and still a work in progress.

dominoes-zeta.vercel.app
Role
All of it
Built with
Claude Code
Stack
TypeScript, Vite, Vitest, Colyseus
Status
Work in progress

Why dominoes

I lived in the Dominican Republic for two years, and in that time I played a lot of dominoes with my father-in-law. Dominoes there isn’t a board game you get out on holidays. It’s just what the table is for.

I also wanted a real project to push Claude Code on. Not a to-do app. Something with enough rules, edge cases, and arguments in it that a model left alone would confidently get things wrong, so I could find out what it takes to keep it from doing that.

Dominoes is perfect for that, because there is no single game. Dominican tables play to 200 in teams and pay bonuses for a capicúa. Puerto Rican tables call it capicú, and some play to 500. Cuban tables use the double-nine set and ten tiles each. Every family has a house rule or two that it will defend as the official one. A model trained on the internet has read all of those, mixed together, and will hand you an average of them with a straight face.

The instruction

If a rule is unclear, ask me. Do not invent rules.

The rules lived in a file, not in the model

The first commit, before any game code, had a document in it: docs/RULES.md, the full rules written out in plain language, one section per part of the game, with every variant named. The project guide the agent reads first says that file is the source of truth, and says what to do when it runs out.

AGENTS.mdthe guide the agent reads before it codes

Source of truth
docs/RULES.md
Unclear rule
Ask me. Do not invent rules.
Every rule
Needs a test
Randomness
Seeded. No Math.random() in the engine.
Hidden tiles
Bots and screens only see one player’s view

That one line did most of the work. When the agent hit something the spec didn’t cover, it stopped and asked, and the answer went into the file, marked user decision, usually with the month it was decided. RULES.md has seventeen of those marks now. They’re the most useful thing in it, because they’re exactly the places where the sources disagree.

Two of them came from me and my father-in-law.

  • Doubles never count as a capicú. Going out with a tile that fits either open end earns the bonus. A 5-5 on ends of 5 and 5 technically fits both. At our table it’s not a capicú, in any style, and now it isn’t in the game either.
  • A chuchazo never stacks with a capicú. Going out with the double blank is its own bonus. Going out with it on two blank ends could be read as both. It counts once, as a chuchazo. Some Puerto Rican tables don’t count that case at all, which is the whole point: somebody had to decide, and it shouldn’t have been the model.
Two winning plays, each with an arrow from the last tile to both open ends and the words ¡Por los dos lados!
From a design pass on the celebrations: the table points at both open ends when a hand goes out on a capicúa. The rule underneath is the one decided at a real table.

The spec gets corrected too, in the open. The Puerto Rican preset first played clockwise. A rules check later found no source for that, every source said counterclockwise, so I changed it, and the file says what changed, when, and why. Clockwise is still there as a house rule for anyone whose family plays it that way.

The model was very good at writing the game. It was never the authority on how the game is played.

Where no rulebook covers a case at all, like a tied tranque when bonuses have pushed both teams past the target, the file says that too, and says what we picked. That’s the difference between a spec and a prompt. A prompt is forgotten at the end of the session. The spec is still there for the next one, and for the tests.

What’s under the table

The engine is pure: no screen, no network, no clock. You give it a game and a move, it gives you back the next game. Randomness comes from a seed, so the same seed deals the same tiles every time, which means any game, including the strange one a tester reports, can be replayed exactly.

Every style is just settings on that engine. Dominican, Dominican one-on-one, Puerto Rican 200, Puerto Rican 500, and Cuban double-nine are presets, and Mi mesa, “my table,” lets you start from any of them and change the rules your family argues about. Nothing about a style is written into the game itself.

The bots come in three levels. Fácil plays its heaviest tile. Normal keeps track of what has been played and who has passed on what, and wins about 85% of games against Fácil. Difícil guesses where the hidden tiles are and plays the hand out many times before it moves, and wins about 73% of team games against Normal. None of them can cheat, because none of them can see anything a person in that seat couldn’t: their own tiles, how many everyone else holds, the table, the score.

589

automated tests, across 61 test files

5

rule presets, plus Mi mesa for house rules

3

bot levels, all limited to one player’s view

186

commits, 177 of them co-written with Claude

The spec says every rule needs a test, and that’s why there are 589 of them. GitHub runs them on every change, and a change doesn’t go into the live game until they pass.

A table you can hold in one hand

I wanted it to look like a real table, not a casino: a wooden top with felt, tiles that sit on it like objects, the score on a strip of paper. You can look straight down at it or tilt it into 3D.

A two versus two Dominican game on a laptop: a wooden table, the line of tiles turning a corner, open ends marked 0 and 3, and a bot partner thinking
Two versus two on a laptop, captured November 2026. The line turns the corner instead of running off the table, doubles always sit crosswise, and the circled numbers are the two open ends. The red notes under a name are what everyone at the table already knows, like which number a player has shown he doesn’t have.

The line of tiles is the hard part. It has to snake around the corners of whatever screen it’s on without ever leaving the table, and it has to do that on a phone held upright, which is how most people will play it. Every word on screen comes in Spanish and English, switched from the start screen.

A real game on the live site, recorded November 2026 and played back at one and a half times speed, which mostly shortens the bots’ thinking. My partner opens with the double six, the next player can’t follow it, and that’s a pase de salida: 25 points to us before anyone else has played a tile.
Two phone screens side by side: a two versus two game and a one-on-one game with the boneyard showing
Phone first. Two versus two with a bot partner, and one-on-one with the boneyard (el pozo) face down, both fitting one screen with nothing to scroll.

A game is saved as you play, so a refresh doesn’t lose it. It installs to the home screen and plays offline against the bots. Sounds are off until you turn them on.

The same live game on a phone, recorded November 2026 at one and a half times speed. On my turn the rack grows, marks what I can play, and asks which end it goes on, and the line folds back on itself rather than run off a table this narrow. Like the rest of the game, still a work in progress.
A celebration burst around the double blank, with a banner reading Chuchazo +100 — Nosotros
Another frame from the celebrations design pass. The value on the banner depends on the style: the Dominican preset scores a chuchazo at zero, Puerto Rican 500 scores it at 100.

Proving nobody cheated

When I read through what players complain about in other domino apps, one of the complaints was that the shuffle is rigged. You can’t argue someone out of that. You can let them check.

So before the first move, the game shows a short code, the fingerprint of the shuffle. It’s made from the seed with a one-way function, so it gives nothing away during the game. When the game ends, the seed is revealed and a Comprobar button checks two things: that the seed really produces the code you were shown at the start, and that it really deals the tiles you got. The screen explaining it, Juego limpio, says this in plain words, because the people most suspicious of the shuffle are not the people who want to read about hashes.

Online play runs through a Colyseus server hosted on Render. The server holds the only real copy of the game, and each phone is sent only its own view of it, so a curious cousin with the browser’s developer tools open sees their own tiles and nothing else. You open a table, share the link, and friends join from their phones with no account. Empty seats get bots. If someone drops, a bot plays for them and the seat waits a minute for them to come back.

What I’d do next

The roadmap is written the same way as the rules, from what players of other domino apps actually complain about.

  • Friend tables that finish, every time: your own seat back after a drop, and “play again with the same people” that remembers who they were.
  • The fingerprint at online tables, so Juego limpio means the same thing everywhere.
  • A partner bot that plays like a partner: doesn’t block you, and holds back a double so you can go out.
  • Easy for abuela: arrive from a WhatsApp link, in Spanish, with big tiles, no account, and no timers.
  • Android and iOS apps, wrapped from the same web game.

The roadmap also has a short list of things it will never do: real-money prizes, betting, loot boxes, ads that grow over time, or notifications that guilt you into opening it. More games are on the list after that, on the same engine, and each one starts the same way: its rules written into the spec before any code.

~/work/domino-caribe0%