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_projection.go | |
| parent | 724079f8de1ae69965c6267dca3d7fc3cc86afa2 (diff) | |
| download | thehouseoficarus-a4c09c2d38d1f3bc1d3ad4bdb0234bdea7c37e73.tar.gz | |
feat: additional craps variants added, standardizard casino command hints
Diffstat (limited to 'internal/casino/craps_projection.go')
| -rw-r--r-- | internal/casino/craps_projection.go | 141 |
1 files changed, 41 insertions, 100 deletions
diff --git a/internal/casino/craps_projection.go b/internal/casino/craps_projection.go index 5be2f28..96d64b7 100644 --- a/internal/casino/craps_projection.go +++ b/internal/casino/craps_projection.go @@ -6,22 +6,17 @@ import ( "strings" ) -// crapsReturn is the pure payout math shared by settlement and by the -// player-facing "what could win on the next roll" projection, so the two can -// never disagree. It mirrors the money path of crapsTable.pay exactly. +// crapsReturn is shared by wallet settlement and next-roll previews. func crapsReturn(b *crapsBet, profit, den, oddsProfit int, refundOdds bool) int { payout := 0 if profit >= 0 { payout = b.stake.Total + b.stake.Total/den*profit - if b.kind == "buy" || b.kind == "lay" { + if (b.kind == "buy" || b.kind == "lay") && profit > 0 { base := payout - b.stake.Total if b.kind == "buy" { base = b.stake.Total } - // Removals (zero profit) return the full stake without commission. - if profit > 0 { - payout -= (base + 19) / 20 - } + payout -= (base + 19) / 20 } } if b.odds.Total > 0 { @@ -37,69 +32,50 @@ func crapsReturn(b *crapsBet, profit, den, oddsProfit int, refundOdds bool) int } return payout } - -// crapsNet returns settlement profit: returns (stake included) less commit. func crapsNet(b *crapsBet, profit, den, oddsProfit int, refundOdds bool) int { - return crapsReturn(b, profit, den, oddsProfit, refundOdds) - (b.stake.Total + b.odds.Total) + return crapsReturn(b, profit, den, oddsProfit, refundOdds) - b.stake.Total - b.odds.Total } - -// crapsRefundOdds mirrors come bets' come-out refundOdds policy. func crapsRefundOdds(b *crapsBet, point int) bool { return point == 0 && b.kind == "come" } -// crapsRollView is one possible dice pair: its total, the pair shown, and the -// player's combined settlement profit if it rolled next. type CrapsRollView struct { Total int Rolls string Net int } -// projectionFor evaluates one player's live bets as if the listed pair rolled -// with the current table point, via the same rules engine used for settlement. +func (t *crapsTable) rollPairs() [][2]int { + var pairs [][2]int + for a := 1; a <= 6; a++ { + if t.Config.Craps.variant() == "die_rich" { + pairs = append(pairs, [2]int{a, 0}) + continue + } + for b := a; b <= 6; b++ { + pairs = append(pairs, [2]int{a, b}) + } + } + return pairs +} func (t *crapsTable) projectionFor(p *crapsPlayer) []CrapsRollView { var rows []CrapsRollView - for total := 2; total <= 12; total++ { - for a := 1; a <= 6; a++ { - b := total - a - if b < 1 || b > 6 || a > b { - continue - } - net := 0 - for _, bet := range p.bets { - done, x, y := crapsOutcome(bet.kind, bet.number, a, b, t.point, t.Config.Craps.Field12) - if !done { - // An unresolved contract keeps its stake; only bonuses - // can complete within a single non-settling roll. - if bet.kind == "fire" { - x = crapsFireProfit(len(t.made)) - } else if bet.kind == "small" || bet.kind == "tall" || bet.kind == "all" { - lo, hi := 2, 12 - if bet.kind == "small" { - hi = 6 - } - if bet.kind == "tall" { - lo = 8 - } - if crapsBonusComplete(lo, hi, t.seen, a+b) { - x = 34 - if bet.kind == "all" { - x = 175 - } - } else { - continue - } - } else { - continue - } - } + for _, pair := range t.rollPairs() { + a, b := pair[0], pair[1] + net := 0 + for _, bet := range p.bets { + done, x, y := t.outcomeFor(bet, a, b) + if done { net += crapsNet(bet, x, y, x, crapsRefundOdds(bet, t.point)) } - rows = append(rows, CrapsRollView{total, fmt.Sprintf("%d+%d", a, b), net}) } + label := fmt.Sprintf("%d+%d", a, b) + if b == 0 { + label = fmt.Sprint(a) + } + rows = append(rows, CrapsRollView{a + b, label, net}) } + sort.SliceStable(rows, func(i, j int) bool { return rows[i].Total < rows[j].Total }) return rows } - func crapsFireProfit(made int) int { switch made { case 4: @@ -111,7 +87,6 @@ func crapsFireProfit(made int) int { } return 0 } - func crapsBonusComplete(lo, hi int, seen map[int]bool, next int) bool { for n := lo; n <= hi; n++ { if n != 7 && !seen[n] && n != next { @@ -120,9 +95,6 @@ func crapsBonusComplete(lo, hi int, seen map[int]bool, next int) bool { } return true } - -// crapsRollLabel names the roll result a bet cares about: hardways and hop -// bets distinguish their exact pair ("4+4"); every other bet reads by total. func crapsRollLabel(bet *crapsBet, a, b int) string { s := a + b switch bet.kind { @@ -142,59 +114,28 @@ func crapsRollLabel(bet *crapsBet, a, b int) string { } return fmt.Sprint(s) } - -// rollTable lists each roll result and its combined payout across the -// player's bets, one row per roll. Dice-distinguishing bets (hardways, hop) -// name their pair; everything else reads by total. Pairs that share a -// label and outcome collapse; across bets, same-roll payouts add. Tags -// stay single-level: the tag expander does not nest. func (t *crapsTable) rollTable(p *crapsPlayer) string { agg := map[string]int{} + pairsMatter := false for _, bet := range p.bets { - perLabel := map[string]int{} - for a := 1; a <= 6; a++ { - for b := a; b <= 6; b++ { - done, x, y := crapsOutcome(bet.kind, bet.number, a, b, t.point, t.Config.Craps.Field12) - if !done { - // Unresolved contracts stay put; only bonuses can - // complete within a single non-settling roll. - switch bet.kind { - case "fire": - x = crapsFireProfit(len(t.made)) - case "small", "tall", "all": - lo, hi := 2, 12 - if bet.kind == "small" { - hi = 6 - } - if bet.kind == "tall" { - lo = 8 - } - if !crapsBonusComplete(lo, hi, t.seen, a+b) { - continue - } - x = 34 - if bet.kind == "all" { - x = 175 - } - default: - continue - } - } - net := crapsNet(bet, x, y, x, crapsRefundOdds(bet, t.point)) - if net != 0 { - perLabel[crapsRollLabel(bet, a, b)] = net - } - } + if bet.kind == "hard" || bet.kind == "hop" { + pairsMatter = true + } + } + for _, row := range t.projectionFor(p) { + label := fmt.Sprint(row.Total) + if pairsMatter { + label = row.Rolls } - for label, net := range perLabel { - agg[label] += net + if row.Net != 0 { + agg[label] = row.Net } } if len(agg) == 0 { if len(p.bets) == 0 { return "No open wagers." } - return "{casino_action}Your place, buy and hardway bets are OFF on the come-out — nothing pays until a point is set.{/}" + return "{casino_action}No wager settles on the next roll; contracts remain live. Place/buy/hardways are OFF on the come-out.{/}" } labels := []string{} for label := range agg { |
