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 withTOGGLE_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.tskeep 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_6andyoboth 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: eachPlacedBetsnapshots the tracker intorepeaterStartHitsat placement; the resolver computes effective hits aslive - 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
BetDefentry inengine/src/bets.ts+ a test inengine/src/resolver.test.tsasserting the real-casino payout. No reducer change. See Extension Surface.