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": switch s { case 2: return true, 3, 1 case 3, 4: return true, 1, 1 case 10, 11: return true, 2, 1 case 12: return true, 5, 1 default: 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 of two dice. Net payouts: 2 pays 3:1; 3/4 pay 1:1; 10/11 pay 2:1; 12 pays 5:1. Totals 5/6/7/8/9 lose. Winning bets also return their stake. No point, odds, come, place, side bets or multi-roll wagers. House edge: 1/36 (approximately 2.78%). 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 / place / buy / lay .\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 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 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 / 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) }