aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/baccarat_multi_wager_test.go
blob: 2a56e63cc2a3ca276229139ed828fb7253a048d9 (plain)
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
package casino

import "testing"

// TestBaccaratSideBetsRideWithMain pins the multi-wager contract: a side bet
// is committed before the main wager, both settle in the same round, and only
// the main wager deals the cards.
func TestBaccaratSideBetsRideWithMain(t *testing.T) {
	cfg := TableConfig{ID: "baccarat", Game: GameBaccarat, Baccarat: BaccaratConfig{PairBets: true}}
	table := newBaccaratTestTable(cfg)
	table.join("alice")
	forceShoe(table, baccaratCards("5 5 5 5 5 5")...)
	w := &testWallet{credits: 1000}
	if events := table.bet("alice", "player_pair", 10, w); hasMessage(events, "New baccarat hand!") {
		t.Fatal("side bet alone dealt a round")
	}
	if table.players.get("alice").HasBet {
		t.Fatal("side bet counted as the round's main wager")
	}
	events := table.bet("alice", "player", 10, w)
	if !hasMessage(events, "New baccarat hand!") {
		t.Fatalf("main wager did not deal: %+v", events)
	}
	// Player and banker tie at 5; the player main pushes and the player pair wins 11:1.
	if w.paid != 130 {
		t.Fatalf("paid=%d, want 130", w.paid)
	}
	var labels []string
	for _, e := range events {
		for _, line := range e.Settlement {
			labels = append(labels, line.Label)
		}
	}
	if len(labels) != 2 || labels[0] != "player" || labels[1] != "player_pair" {
		t.Fatalf("settlement labels=%v, want [player player_pair]", labels)
	}
	// The snapshot carries both bets for the table view.
	var snapshot *BaccaratSnapshot
	for _, e := range events {
		if s, ok := e.Snapshot.(*BaccaratSnapshot); ok && len(s.Bets) > 0 {
			snapshot = s
		}
	}
	if snapshot == nil || len(snapshot.Bets) != 2 ||
		snapshot.Bets[0].Spot != "player" || snapshot.Bets[0].Amount != 10 ||
		snapshot.Bets[1].Spot != "player_pair" || snapshot.Bets[1].Amount != 10 {
		t.Fatalf("snapshot bets=%+v", snapshot)
	}
}

// A side wager may not follow the main wager: the main is the deal trigger.
func TestBaccaratSideBetAfterMainRefused(t *testing.T) {
	table := newBaccaratTestTable(TableConfig{ID: "baccarat", Game: GameBaccarat, Baccarat: BaccaratConfig{PairBets: true}, MaxPlayers: 2})
	table.join("alice")
	table.join("bob")
	w := &testWallet{credits: 1000}
	table.bet("alice", "player", 10, w)
	if events := table.bet("alice", "player_pair", 10, w); !hasMessage(events, "before your main wager") {
		t.Fatalf("side bet after main accepted: %+v", events)
	}
}

// A bare "bet" repeats the remembered side bets and the main wager together.
func TestBaccaratRebetAll(t *testing.T) {
	cfg := TableConfig{ID: "baccarat", Game: GameBaccarat, Baccarat: BaccaratConfig{PairBets: true}}
	table := newBaccaratTestTable(cfg)
	table.join("alice")
	forceShoe(table, baccaratCards("5 5 5 5 5 5")...)
	table.bet("alice", "player_pair", 10, &testWallet{credits: 1000})
	table.bet("alice", "player", 10, &testWallet{credits: 1000})
	forceShoe(table, baccaratCards("5 5 5 5 5 5")...)
	w := &testWallet{credits: 1000}
	events := table.rebetAll("alice", w)
	if !hasMessage(events, "New baccarat hand!") || w.paid != 130 {
		t.Fatalf("rebet all did not replay both wagers: paid=%d events=%+v", w.paid, events)
	}
}

// unbet returns the main wager and every side bet in one step.
func TestBaccaratUnbetReturnsAllWagers(t *testing.T) {
	table := newBaccaratTestTable(TableConfig{ID: "baccarat", Game: GameBaccarat, Baccarat: BaccaratConfig{PairBets: true}})
	table.join("alice")
	w := &testWallet{credits: 1000}
	table.bet("alice", "player_pair", 10, w)
	table.bet("alice", "banker_pair", 15, w)
	if events := table.unbetAllLocked("alice"); !hasMessage(events, "take back 25") {
		t.Fatalf("unbet did not return both side stakes: %+v", events)
	}
	if w.paid != 25 || len(w.open) != 0 {
		t.Fatalf("paid=%d open=%d, want 25/0", w.paid, len(w.open))
	}
}

// A side-only seat is not part of the round: the timer returns the side stake
// and the other seat's main wager still deals.
func TestBaccaratSideOnlySeatRefundedOnTimeout(t *testing.T) {
	cfg := TableConfig{ID: "baccarat", Game: GameBaccarat, Baccarat: BaccaratConfig{PairBets: true}, MaxPlayers: 2, BettingTicks: 2}
	table := newBaccaratTestTable(cfg)
	table.join("alice")
	table.join("bob")
	wa, wb := &testWallet{credits: 1000}, &testWallet{credits: 1000}
	table.bet("alice", "player_pair", 10, wa)
	table.bet("bob", "player", 10, wb)
	forceShoe(table, baccaratCards("5 5 5 5 5 5")...)
	var events []Event
	for i := 0; i < 4 && !hasMessage(events, "New baccarat hand!"); i++ {
		events = append(events, table.tick()...)
	}
	if !hasMessage(events, "New baccarat hand!") {
		t.Fatalf("side-only seat held the round open: %+v", events)
	}
	if wa.paid != 10 {
		t.Fatalf("alice side stake not returned: paid=%d", wa.paid)
	}
	if !hasMessage(events, "side bets is returned") {
		t.Fatalf("missing side-bet refund notice: %+v", events)
	}
}