1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
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
}
// rollTable previews the next roll as one compact line of every total 2-12
// (1-6 on the single-die table): wins are bold green, losses bold red, and
// totals no open wager settles are dim. Hardway and hop bets are pair
// specific, so the best pair outcome stands in for that total.
func (t *crapsTable) rollTable(p *crapsPlayer) string {
if len(p.bets) == 0 {
return ""
}
best := map[int]int{}
found := map[int]bool{}
for _, row := range t.projectionFor(p) {
if !found[row.Total] || row.Net > best[row.Total] {
best[row.Total] = row.Net
found[row.Total] = true
}
}
lo, hi := 2, 12
if t.Config.Craps.variant() == "die_rich" {
lo, hi = 1, 6
}
var sb strings.Builder
sb.WriteString("{casino_emphasis}ROLL OUTCOMES{/}:")
for total := lo; total <= hi; total++ {
token := fmt.Sprintf("%d(-)", total)
tag := "dim"
switch {
case found[total] && best[total] > 0:
token, tag = fmt.Sprintf("%d(+%d)", total, best[total]), "casino_result_win"
case found[total] && best[total] < 0:
token, tag = fmt.Sprintf("%d(%d)", total, best[total]), "casino_result_loss"
}
fmt.Fprintf(&sb, " {%s}%s{/}", tag, token)
}
return sb.String()
}
|