diff options
| author | historia <[not public]> | 2026-10-01 19:30:38 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-10-01 19:30:38 -0400 |
| commit | 6d9908b29c8187196d23b126b1165c7e0bd0fc76 (patch) | |
| tree | ddbc02357ed92646b801687b90b1a06c71a15d71 /internal/casino/pai_gow_test.go | |
| parent | 8a94f21bd47c0a00bc415395232dba22fa958837 (diff) | |
| download | thehouseoficarus-6d9908b29c8187196d23b126b1165c7e0bd0fc76.tar.gz | |
fix: casino pai gow sorting, universal unbet, table/rules split, remove solo timers
Diffstat (limited to 'internal/casino/pai_gow_test.go')
| -rw-r--r-- | internal/casino/pai_gow_test.go | 126 |
1 files changed, 123 insertions, 3 deletions
diff --git a/internal/casino/pai_gow_test.go b/internal/casino/pai_gow_test.go index 3936704..7be9e8a 100644 --- a/internal/casino/pai_gow_test.go +++ b/internal/casino/pai_gow_test.go @@ -675,11 +675,11 @@ func TestPaiGowUnbetAndStopRefundBeforeDeal(t *testing.T) { tab.join("a") tab.join("b") tab.bet("a", "main", 10, wa) - if events := tab.action("a", "unbet main"); !hasMessage(events, "take back") { - t.Fatalf("unbet main failed: %+v", events) + if events := tab.action("a", "unbet"); !hasMessage(events, "take back") { + t.Fatalf("unbet failed: %+v", events) } if wa.chips+wa.paid != 100 || len(wa.open) != 0 || tab.phase != "waiting" || tab.player("a").main.Total != 0 { - t.Fatalf("unbet main left state: chips=%d paid=%d phase=%s", wa.chips, wa.paid, tab.phase) + t.Fatalf("unbet left state: chips=%d paid=%d phase=%s", wa.chips, wa.paid, tab.phase) } // A side bet placed and then abandoned is returned when the seat leaves. tab.bet("a", "fortune", 5, wa) @@ -718,3 +718,123 @@ func TestPaiGowRefundAllReturnsSideBetsWithoutMain(t *testing.T) { t.Fatalf("side-only refund failed: chips=%d open=%d", w.chips, len(w.open)) } } + +func TestPaiGowDealtCardsSortedLowToHigh(t *testing.T) { + tab := pgTestTable(false) + w := &testWallet{chips: 100} + tab.join("a") + tab.bet("a", "", 10, w) + p := tab.player("a") + if len(p.cards) != 7 { + t.Fatalf("no seven-card deal: %v", p.cards) + } + for i := 1; i < len(p.cards); i++ { + if pgSortRank(p.cards[i-1]) > pgSortRank(p.cards[i]) { + t.Fatalf("cards not sorted small to large: %v", p.cards) + } + } +} + +func TestPaiGowNewSideBetClearsLastRound(t *testing.T) { + tab := pgTestTable(false) + w := &testWallet{chips: 100} + tab.join("a") + tab.bet("a", "", 10, w) // a lone bettor deals at once + tab.action("a", "house") + tab.action("a", "set") + p := tab.player("a") + if p.showdown == nil { + t.Fatal("setup: round did not settle") + } + // A side bet for the next round must not re-show the previous result. + tab.bet("a", "fortune", 5, w) + if p.showdown != nil || p.result != "" || len(p.cards) != 0 { + t.Fatalf("stale round leaked into a new bet: showdown=%v result=%q cards=%d", p.showdown != nil, p.result, len(p.cards)) + } +} + +func TestPaiGowUnbetRemovesEveryWager(t *testing.T) { + tab := pgTestTable(false) + wa, _ := &testWallet{chips: 100}, &testWallet{chips: 100} + tab.join("a") + tab.join("b") + tab.bet("a", "fortune", 5, wa) + tab.bet("a", "main", 10, wa) + // b has not bet, so the round is still in the betting window. + if tab.phase != "betting" { + t.Fatalf("setup: wanted betting phase, got %s", tab.phase) + } + if events := tab.action("a", "unbet"); !hasMessage(events, "take back") { + t.Fatalf("unbet failed: %+v", events) + } + p := tab.player("a") + if wa.chips+wa.paid != 100 || len(wa.open) != 0 || p.main.Total != 0 || p.fortune.Total != 0 { + t.Fatalf("unbet left state: chips=%d paid=%d open=%d main=%d fortune=%d", wa.chips, wa.paid, len(wa.open), p.main.Total, p.fortune.Total) + } +} + +func TestPaiGowSoloSettingNeverTimesOut(t *testing.T) { + cfg := TableConfig{ID: "pg", Game: GamePaiGow, PaiGow: PaiGowConfig{ActionTicks: 1}}.Normalize() + tab := newPaiGowTable(1, cfg, rand.New(rand.NewSource(3))).(*paiGowTable) + w := &testWallet{chips: 100} + tab.join("a") + tab.bet("a", "", 10, w) + if tab.phase != "setting" { + t.Fatalf("setup: wanted setting, got %s", tab.phase) + } + for i := 0; i < 5; i++ { + tab.tick() + } + if tab.phase != "setting" || tab.player("a").ready { + t.Fatalf("a lone player was timed out: phase=%s ready=%v", tab.phase, tab.player("a").ready) + } +} + +func TestPaiGowMultiplayerTimeoutWarnsAndSets(t *testing.T) { + tab := pgTestTable(false) // ActionTicks 2 + wa, wb := &testWallet{chips: 100}, &testWallet{chips: 100} + tab.join("a") + tab.join("b") + tab.bet("a", "main", 10, wa) + tab.bet("b", "main", 10, wb) // the second main wager deals + if tab.phase != "setting" { + t.Fatalf("setup: wanted setting, got %s", tab.phase) + } + tab.action("a", "house") + tab.action("a", "set") + warned := false + for i := 0; i < 3; i++ { + for _, e := range tab.tick() { + if e.Color == "casino_warning" && strings.Contains(e.Message, "timed out") { + warned = true + } + } + } + if !warned { + t.Fatal("multiplayer timeout did not announce the auto-set") + } + if tab.phase != "waiting" || tab.player("b").showdown == nil { + t.Fatal("timed-out hand was not set and settled") + } +} + +func TestPaiGowBettingWindowTimeoutNotifiesNonBettor(t *testing.T) { + tab := pgTestTable(false) // BettingTicks 2 + wa, wb := &testWallet{chips: 100}, &testWallet{chips: 100} + tab.join("a") + tab.join("b") + tab.join("c") + tab.bet("a", "main", 10, wa) + tab.bet("b", "main", 10, wb) + notified := false + for i := 0; i < 3; i++ { + for _, e := range tab.tick() { + if e.PrivateTo == "c" && strings.Contains(e.Message, "betting window closed") { + notified = true + } + } + } + if !notified { + t.Fatal("non-bettor was not told the betting window closed") + } +} |
