package casino import ( "fmt" "sort" "strings" ) // 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") && profit > 0 { base := payout - b.stake.Total if b.kind == "buy" { base = b.stake.Total } payout -= (base + 19) / 20 } } if b.odds.Total > 0 { if refundOdds { payout += b.odds.Total } else if oddsProfit >= 0 { x, y := crapsTrueOdds(b.number) if strings.HasPrefix(b.kind, "dont") { x, y = y, x } payout += b.odds.Total + b.odds.Total/y*x } } return payout } func crapsNet(b *crapsBet, profit, den, oddsProfit int, refundOdds bool) int { return crapsReturn(b, profit, den, oddsProfit, refundOdds) - b.stake.Total - b.odds.Total } func crapsRefundOdds(b *crapsBet, point int) bool { return point == 0 && b.kind == "come" } type CrapsRollView struct { Total int Rolls string Net int } 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 _, 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)) } } 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: return 24 case 5: return 249 case 6: return 999 } 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 { return false } } return true } func (t *crapsTable) rollTable(p *crapsPlayer) string { agg := map[string]int{} pairsMatter := false for _, bet := range p.bets { 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 } if row.Net != 0 { agg[label] = row.Net } } if len(agg) == 0 { if len(p.bets) == 0 { return "No open wagers." } 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 { labels = append(labels, label) } sort.Slice(labels, func(i, j int) bool { ni, nj := agg[labels[i]], agg[labels[j]] if (ni > 0) != (nj > 0) { return ni > 0 } if ni != nj { return ni > nj } return labels[i] < labels[j] }) var sb strings.Builder sb.WriteString("{casino_emphasis}ROLL RESULTS ON YOUR BETS{/}\n") for _, label := range labels { net := agg[label] tag, amount := "casino_result_loss", fmt.Sprint(net) if net > 0 { tag, amount = "casino_result_win", "+"+amount } fmt.Fprintf(&sb, " {casino_payout}%s{/}%s {%s}%s{/}\n", label, strings.Repeat(" ", max(1, 10-len(label))), tag, amount) } sb.WriteString("{casino_action}Hardways and hops name their pair; totals cover both orders.{/}") return sb.String() }