diff options
| author | historia <[not public]> | 2026-09-30 16:16:17 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-30 16:16:17 -0400 |
| commit | a4c09c2d38d1f3bc1d3ad4bdb0234bdea7c37e73 (patch) | |
| tree | 00c9567e620f331a59dd44f1f1fa779ecd94a845 /internal/casino/craps_variants.go | |
| parent | 724079f8de1ae69965c6267dca3d7fc3cc86afa2 (diff) | |
| download | thehouseoficarus-a4c09c2d38d1f3bc1d3ad4bdb0234bdea7c37e73.tar.gz | |
feat: additional craps variants added, standardizard casino command hints
Diffstat (limited to 'internal/casino/craps_variants.go')
| -rw-r--r-- | internal/casino/craps_variants.go | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/internal/casino/craps_variants.go b/internal/casino/craps_variants.go new file mode 100644 index 0000000..b3be51b --- /dev/null +++ b/internal/casino/craps_variants.go @@ -0,0 +1,184 @@ +package casino + +import "fmt" + +// Variants are explicit house editions; regional games with the same names +// need not use these rules. Random sources never affect wallet/queue handling. +func (c CrapsConfig) variant() string { + if c.Variant == "" { + return "bank" + } + return c.Variant +} +func (c CrapsConfig) title() string { + switch c.variant() { + case "simplified": + return "Simplified Craps" + case "high_point": + return "High Point Craps" + case "crapless": + return "Crapless Craps" + case "new_york": + return "New York Craps" + case "die_rich": + return "Die Rich Craps" + case "diceless": + return "Diceless Craps" + default: + return "Bank Craps" + } +} +func (c CrapsConfig) allows(kind string) bool { + switch c.variant() { + case "simplified", "high_point", "die_rich": + return kind == "pass" + case "new_york": + return kind == "pass" || kind == "dontpass" || kind == "buy" || kind == "lay" || kind == "odds" + case "crapless": + return kind != "dontpass" && kind != "dontcome" && kind != "fire" && kind != "small" && kind != "tall" && kind != "all" + default: + return true + } +} +func (c CrapsConfig) pointNumber(n int) bool { + switch c.variant() { + case "simplified": + return false + case "high_point": + return n >= 4 && n <= 10 + case "die_rich": + return n >= 2 && n <= 5 + case "crapless": + return n >= 2 && n <= 12 && n != 7 + default: + return crapsNumber(n) + } +} +func (c CrapsConfig) number(n int) bool { + return crapsNumber(n) || (c.variant() == "crapless" && n >= 2 && n <= 12 && n != 7) +} +func crapsPlaceOdds(n int) (int, int) { + switch n { + case 2, 12: + return 11, 2 + case 3, 11: + return 11, 4 + case 4, 10: + return 9, 5 + case 5, 9: + return 7, 5 + default: + return 7, 6 + } +} +func (c CrapsConfig) outcome(kind string, n, a, b, point int) (bool, int, int) { + s := a + b + switch c.variant() { + case "simplified": + if s == 2 || s == 3 || s == 4 || s == 10 || s == 11 || s == 12 { + return true, 1, 1 + } + return true, -1, 1 + case "high_point": + if n == 0 { + if s == 2 || s == 3 { + return false, 0, 1 + } + if s >= 11 { + return true, 1, 1 + } + return false, 0, 1 + } + if s > n { + return true, 1, 1 + } + return true, -1, 1 + case "die_rich": + if n == 0 { + if s == 6 { + return true, 1, 1 + } + if s == 1 { + return true, -1, 1 + } + return false, 0, 1 + } + if s == n { + return true, 1, 1 + } + if s == 1 { + return true, -1, 1 + } + return false, 0, 1 + case "crapless": + if (kind == "pass" || kind == "come") && n == 0 { + if s == 7 { + return true, 1, 1 + } + return false, 0, 1 + } + } + return crapsOutcome(kind, n, a, b, point, c.Field12) +} + +// outcomeFor includes bonus progress without mutating it. Both previews and +// actual settlement call this before recording the new roll. +func (t *crapsTable) outcomeFor(bet *crapsBet, a, b int) (bool, int, int) { + s := a + b + switch bet.kind { + case "fire": + if t.point == 0 || s != 7 { + return false, 0, 1 + } + x := crapsFireProfit(len(t.made)) + if x == 0 { + x = -1 + } + return true, x, 1 + case "small", "tall", "all": + if s == 7 { + return true, -1, 1 + } + lo, hi := 2, 12 + if bet.kind == "small" { + hi = 6 + } + if bet.kind == "tall" { + lo = 8 + } + if !crapsBonusComplete(lo, hi, t.seen, s) { + return false, 0, 1 + } + if bet.kind == "all" { + return true, 175, 1 + } + return true, 34, 1 + default: + return t.Config.Craps.outcome(bet.kind, bet.number, a, b, t.point) + } +} +func (c CrapsConfig) variantRulesText() string { + rules := "" + switch c.variant() { + case "simplified": + rules = "Pass only, one roll: 2/3/4/10/11/12 win 1:1; 5/6/7/8/9 lose. No point. A loss passes the dice." + case "high_point": + rules = "Pass only, 1:1. Come-out 2/3 roll again, 11/12 win; 4-10 establish a point. The very next roll wins only if HIGHER than the point; equal/lower loses. Loss passes the dice." + case "die_rich": + rules = "Pass only, ONE six-sided die, 1:1. Come-out 6 wins, 1 loses; 2-5 establish a point. Repeat the point before 1 to win; other faces roll again. Any 1 passes the die. No free odds." + case "new_york": + rules = "House New York: bank pass/dontpass (bar 12), free odds, buy and lay only. No come, place, field, propositions, hardways or bonuses. Buy/lay use true odds with 5% commission on buy stake / lay profit, rounded up, wins only. Buy is off on come-out; lay works." + case "crapless": + rules = "Come-out 7 wins; ALL other totals become points, including 2/3/11/12. Pass/come only, no don't bets or bonuses. All bank side bets supported. Extra-number place: 2/12 pay 11:2; 3/11 pay 11:4. Buy and free odds: 2/12 pay 6:1; 3/11 pay 3:1. Lay pays inverse true odds. Default extra-number odds limit: 1x stake." + } + if c.variant() == "new_york" || c.variant() == "crapless" { + rules += "\nTrue odds on 4/10: 2:1; 5/9: 3:2; 6/8: 6:5. Lay and don't odds invert these ratios. Buy/lay commission: 5% buy STAKE / lay PROFIT, rounded up, on wins only; removals have no commission." + } + if c.variant() == "crapless" { + rules += fmt.Sprintf("\nPass/come pay 1:1 on their point before 7; 7 loses. Line bets only on come-out; come requires a table point.\nTargets: pass / come / odds pass / odds come <number> / place <number> / buy <number> / lay <number>.\nPlace 4/10 pays 9:5; 5/9 7:5; 6/8 7:6.\nfield: 2 pays 2:1, 12 pays %d:1, 3/4/9/10/11 pay 1:1; others lose.\nhard 4/10 pays 7:1; hard 6/8 pays 9:1; easy number or 7 loses.\nseven 4:1; craps 7:1; eleven / ace-deuce 15:1; aces / boxcars 30:1.\nhorn splits equally among 2/3/11/12; hop <a> <b> pays 15:1 easy / 30:1 double, unordered.\nPlace/buy/hardways and come odds are off on come-out; established come contracts and lay always work.\nNo Fire or ATS. Making a point retains the shooter; seven-out rotates.", c.Field12) + } + if c.variant() == "new_york" { + rules += "\nPass come-out 7/11 wins, 2/3/12 loses; 4/5/6/8/9/10 establish a point. Make it before 7 to win, all 1:1. Don't pass is opposite, except come-out 12 pushes. Line bets only on come-out. Making a point retains the shooter; seven-out rotates." + } + return fmt.Sprintf("%s — house rules\n%s\nBet: bet <amount> on pass; table rules is authoritative for this edition.\nFree odds: %d (0 = 3-4-5x on ordinary points, don't 6x laid). No odds on Simplified, High Point or Die Rich.\nEach wager obeys table limits and exact payout units. Wins return stake and remove the bet.\nPass/come contracts cannot be removed; optional bets/odds can: unbet <target> / unbet all.\nSolo roll anytime; multiplayer waits 10 seconds, then dealer rolls after 60 seconds.\nBets stay open until the roll. autoroll repeats every 3 seconds, respecting the betting window.\nstop cancels autoroll; manual stop/sitout waits for live bets. Disconnect forfeits; shutdown refunds.", c.title(), rules, c.Odds) +} |
