Design Overview
Roll a Hard 6 — Balatro-leaning co-op craps with 1:1 real-casino bet fidelity.
Vision
A roguelike co-op craps game. The dice and bet menu at the table are real-casino faithful — odds, payouts, and working/off behavior all match a real table. A Balatro-style chip/joker layer sits on top: shooter chips bend dice physics, bettor chips multiply payouts and add scoring. Runs play through rising “blinds” (table-minimum tiers); between blinds you shop for chips, level rare bets, and recruit shooters.
The game is eventually online co-op / competitive, but every layer must work in solo mode first.
Pillars
- Purist test. With every chip disabled, the table must play as a faithful real-money craps simulation. A craps purist should not be able to find a single broken payout, odd, or rule. This is enforced mechanically (see the chip dispatch contract), not by convention.
- Engine purity. The engine is a pure reducer over JSON-serializable state, deterministic from a seed. No I/O, no clocks, no
Math.random. This is what makes online, save/resume, replay, deterministic tests, and “add a chip without rewriting the engine” all the same problem solved once. See Architecture. - Co-op is mechanical, not flavor. Roller and bettor roles each have agency; the strong chip combos require both sides to set up. A bettor’s Hot Roller chip rewards consecutive non-7s; a shooter’s Setter chip locks a die face — they synergize.
The Purist Test (load-bearing)
Strip every chip and the table must play as a faithful real-money craps game. No exceptions.
This is enforced mechanically by the chip dispatch contract:
| Side | Hook | Allowed effect |
|---|---|---|
shooter | onPostRoll | Mutate the raw dice roll |
bettor | onPostResolve | Mutate the settlement list |
The TypeScript types make crossing sides unreachable; the reducer never routes a chip to the wrong hook. If you change the chip contract, you have to break this guarantee on purpose.
Practical implication: when reviewing any chip / bet / reducer change, run this check — “could a chip in this design ever affect a payout from the shooter side, or the dice from the bettor side?” If yes, the design is wrong. See Chip Dispatch Contract for the full enforcement story (compile-time union + two runtime seam guards).
Co-op Topology
Online from day one. The server is authoritative — it runs the reducer, holds the seed, and anti-cheats by construction. The client sends actions over WS and receives state.
Solo is a single-player room — same code path, no network round-trip in the engine sense but the same server/WS plumbing. The bot opponent is the cheapest possible thing: when the solo player locks bets, the bot auto-rolls. No AI.
Building solo with the online architecture from the start costs almost nothing extra (a single-player room is a degenerate case of a multi-player room) and avoids a netcode rewrite later.
Milestone Shape
A1 — Bare-craps-in-browser (shipped)
The engine wired end-to-end with no roguelike skin: server exposes the reducer over HTTP + WS, the client renders bankroll/phase/bets/roll, solo room with an auto-rolling bot.
A2 — Roguelike layer (shipped, end-to-end)
Runs, antes, target bankrolls (the “blinds”), shop, chip drafting, between-shooter meta — the Balatro skin. See Roguelike Layer.
A3+ — Online multiplayer
Multiple bettors per room (engine + server already support N), role rotation, spectating. Bettor-slot auth ships with the per-bettor opaque-token scheme; full user accounts (signup/login/JWT) are still deferred and not required for the co-op flows.