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
|
package casino
import (
"fmt"
"testing"
)
// TestCrapsProjectionMatchesSettlement pins the player-facing roll table to
// the real settlement path: for every pair, at several points, the projected
// net equals what resolveRoll would actually pay.
func TestCrapsProjectionMatchesSettlement(t *testing.T) {
tb := crapsFixture()
tb.join("alice")
w := &testWallet{chips: 10000}
tb.bet("alice", "pass", 10, w)
tb.bet("alice", "field", 10, w)
tb.bet("alice", "hard 6", 10, w)
tb.bet("alice", "place 4", 10, w)
tb.bet("alice", "hop 2 3", 10, w)
tb.bet("alice", "horn", 4, w)
tb.resolveRoll(3, 3)
tb.bet("alice", "odds pass", 20, w)
tb.bet("alice", "dontcome", 10, w)
tb.bet("alice", "fire", 1, w)
for _, point := range []int{0, 4, 6, 10} {
tb.point = point
for a := 1; a <= 6; a++ {
for b := 1; b <= 6; b++ {
projected := 99999
for _, row := range tb.projectionFor(tb.players.get("alice")) {
if row.Total == a+b && row.Rolls == fmt.Sprintf("%d+%d", min(a, b), max(a, b)) {
projected = row.Net
}
}
settled := tb.simulateNet("alice", a, b)
if projected != settled {
t.Fatalf("pair %d+%d point %d: projected %d, settled %d", a, b, point, projected, settled)
}
}
}
}
}
// simulateNet resolves one pair on a deep copy of the table (fresh test
// wallets) and returns alice's true net through the ordinary settlement path.
func (t *crapsTable) simulateNet(name string, a, b int) int {
clone := &crapsTable{tableCore: newTableCore[*crapsPlayer](&tableBase{RoomID: t.RoomID, Config: t.Config}), rng: t.rng}
wallets := map[string]*testWallet{}
for _, seat := range t.players.ordered() {
wallet := &testWallet{}
wallet.open = append(wallet.open, seat.Wallet.(*testWallet).open...)
wallets[seat.Name] = wallet
cp := &crapsPlayer{Participant: Participant{Name: seat.Name, Connected: true, HasBet: seat.HasBet, Wallet: wallet}}
for _, bet := range seat.bets {
cp.bets = append(cp.bets, &crapsBet{kind: bet.kind, number: bet.number, stake: bet.stake, odds: bet.odds})
}
clone.players.add(seat.Name, cp)
}
clone.point = t.point
clone.made = map[int]bool{}
for n := range t.made {
clone.made[n] = true
}
clone.seen = map[int]bool{}
for n := range t.seen {
clone.seen[n] = true
}
before := walletWealth(wallets[name])
clone.resolveRoll(a, b)
return walletWealth(wallets[name]) - before
}
// walletWealth is cash in hand plus the cash value of every pending stake,
// so a resolved win/loss reports true settlement profit.
func walletWealth(w *testWallet) int {
pending := 0
for _, wager := range w.open {
pending += wager.Total
}
return w.chips + w.paid + pending
}
|