Bet Catalog — Real-Casino Fidelity

The full standard casino table is shipped in the engine, every bet at real-casino payouts (true odds or house odds, whichever the bet actually uses — never “what feels right”). Every bet has a test in engine/src/resolver.test.ts asserting its casino-faithful payout.

Bets are data: a BetDef entry in engine/src/bets.ts (DEFAULT_CATALOG). Adding a bet is a new entry, not a reducer change.

The line + odds

  • Pass / Pass odds — the line bet plus true odds behind it.
  • Don’t pass / Don’t pass odds — the wrong-way bet plus lay odds.

Odds cap (3-4-5x)

Odds on a backing flat bet are one cumulative wager, capped by the reducer so the max odds win is 6× the flat — exactly 3x/4x/5x stake on 4·10 / 5·9 / 6·8 for the do side, and the equivalent lay-to-win on the don’t side. Capping by win (not stake) unifies both sides. A bet with no backing flat has a cap of 0 (you can’t take odds you aren’t backing); removing a backing flat (REMOVE_BET) cascade-refunds its odds, so the invariant holds after placement too. The 6 lives in ODDS_MAX_WIN_MULTIPLE (engine/src/bets.ts). Odds bets are excluded from A2 bet-leveling (BET_NOT_LEVELABLE) so the level multiplier can’t push an odds payout past the cap.

Come / don’t come

Like pass / don’t-pass but placed mid-roll, with per-bet “come point” tracking. This is the only bet shape that required a reducer extension: a new establish_point settlement outcome that mutates bet.point on the first roll after placement.

  • Come / don’t-come odds (come_odds, dont_come_odds) pay true odds against the come bet’s own point; the reducer enforces a parent come/don’t-come bet on the same point (NO_PARENT_COME_BET). Default off on come-out, mirroring most tables — toggle with TOGGLE_WORKING.

Take-down rules (REMOVE_BET, contract semantics)

Pass (once the table point is set) and come (once it has traveled to its own point) are CONTRACT bets — REMOVE_BET is rejected with BET_LOCKED. Don’t-pass / don’t-come are not contracts — removable at will (the wrong-way bets favor the house, so real tables let you pull them). The client’s canRemove mirrors this exactly.

Place, buy, lay

  • Place 4 / 5 / 6 / 8 / 9 / 10 — every place number at real-casino payouts (9:5 on 4/10, 7:5 on 5/9, 7:6 on 6/8).
  • Buy / lay (12 entries) — pure data; commission folded into the payout fractions (5% on bet for buy, 5% on win for lay). Granularity rules in bets.ts keep the math integer.

The middle of the layout

  • Big 6 / Big 8 (1:1, always working — cosmetic but standard).
  • Hardways 4 / 6 / 8 / 10 (7:1 on 4/10, 9:1 on 6/8).
  • Field (one-roll, 2:1 on 2, 3:1 on 12, 1:1 on 3/4/9/10/11).
  • Any 7 (one-roll, 4:1).
  • Any craps (one-roll, 7:1).

Props and hops

  • High-prop set — yo, snake eyes, midnight, ace-deuce, C&E, horn.
  • Hop bets (21 entries) — 6 hard at 30:1, 15 easy at 15:1. They coexist with overlapping props (e.g. hop_5_6 and yo both pay on the same combo at the same odds).

Multi-roll bets

These read shooter / tracker state via an optional ResolveContext on BetDef.resolve:

  • Fire bet — 24/249/999:1 at seven-out for 4/5/6 unique points (lose on 0–3). Driven by shooterStats.pointsMade. The reducer clears the tracker after resolution so the fire bet sees the pre-clear count.
  • Sharpshooter — 7/15/30/60/100/250/1000:1 at seven-out for 4–10+ point cycles (repeats included, not just unique). Driven by shooterStats.pointsMadeTotal.
  • ATS (All Small / All Tall / All Numbers) — 35:1 / 35:1 / 175:1 via atsTracker. The reducer advances the tracker before resolution so the bet wins on the completing roll; any 7 clears it. Placement requires an empty tracker (ATS_TRACKER_NOT_EMPTY).
  • Repeater bets (10 entries, 2/3/4/5/6/8/9/10/11/12) — 40:1 to 90:1 via repeaterTracker. Per-bet zero-at-placement: each PlacedBet snapshots the tracker into repeaterStartHits at placement; the resolver computes effective hits as live - startHits. Any 7 resets the map.

The fire bet established the multi-roll pattern; ATS and repeaters reuse the same ResolveContext mechanism — the reducer pre-computes post-roll trackers for any “wins on the completing roll” bet and threads them through to resolution, while leaving pre-roll state available for bets that need it (fire, sharpshooter).


Adding a bet? New BetDef entry in engine/src/bets.ts + a test in engine/src/resolver.test.ts asserting the real-casino payout. No reducer change. See Extension Surface.