diff options
| author | historia <[not public]> | 2026-09-24 19:31:05 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-24 19:31:05 -0400 |
| commit | d114c747a7672c7e4daab3a5b5f1b2f066ebc912 (patch) | |
| tree | 81cc03354561f2f631d68f0de28bc742b1f49114 /internal/casino/baccarat_variants_test.go | |
| parent | f606db40c3290fd9ce40b53ef9bbe135b1e6f6e2 (diff) | |
| download | thehouseoficarus-d114c747a7672c7e4daab3a5b5f1b2f066ebc912.tar.gz | |
feat: additional baca and bj rules variants
Diffstat (limited to 'internal/casino/baccarat_variants_test.go')
| -rw-r--r-- | internal/casino/baccarat_variants_test.go | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/internal/casino/baccarat_variants_test.go b/internal/casino/baccarat_variants_test.go new file mode 100644 index 0000000..4f88227 --- /dev/null +++ b/internal/casino/baccarat_variants_test.go @@ -0,0 +1,139 @@ +package casino + +import ( + "strings" + "testing" +) + +func baccaratCards(ranks string) []card { + var cards []card + for _, rank := range strings.Fields(ranks) { + cards = append(cards, card{rank: rank}) + } + return cards +} + +func TestBaccaratVariantSettlement(t *testing.T) { + cases := []struct { + name, variant, spot, player, banker, result string + bet, payout int + }{ + {"commission six", BaccaratCommission, "banker", "A A 2", "3 3", "win", 100, 195}, + {"super six two cards", BaccaratSuper6, "banker", "A A 2", "3 3", "win", 100, 150}, + {"super six three cards", BaccaratSuper6, "banker", "A A 2", "A A 4", "win", 100, 150}, + {"super six odd stake", BaccaratSuper6, "banker", "A A 2", "3 3", "win", 3, 4}, + {"super six tiny stake", BaccaratSuper6, "banker", "A A 2", "3 3", "win", 1, 1}, + {"super six other win", BaccaratSuper6, "banker", "A A 2", "3 4", "win", 100, 200}, + {"six tied banker", BaccaratSuper6, "banker", "2 4", "3 3", "push", 100, 100}, + {"six tied tie wager", BaccaratSuper6, "tie", "2 4", "3 3", "win", 100, 900}, + {"six loses", BaccaratSuper6, "banker", "3 4", "3 3", "loss", 100, 0}, + {"ez dragon banker", BaccaratEZ, "banker", "A A 2", "A A 5", "push", 100, 100}, + {"ez dragon player", BaccaratEZ, "player", "A A 2", "A A 5", "loss", 100, 0}, + {"ez dragon tie", BaccaratEZ, "tie", "A A 2", "A A 5", "loss", 100, 0}, + {"ez two card seven", BaccaratEZ, "banker", "A A 2", "3 4", "win", 100, 200}, + {"ez other three card", BaccaratEZ, "banker", "A A 2", "A A 4", "win", 100, 200}, + {"ez seven loses", BaccaratEZ, "banker", "A A 6", "A A 5", "loss", 100, 0}, + {"ez seven ties", BaccaratEZ, "tie", "3 4", "A A 5", "win", 100, 900}, + {"player pair on tie", BaccaratCommission, "player_pair", "3 3", "2 4", "win", 10, 120}, + {"banker pair loses main hand", BaccaratCommission, "banker_pair", "4 5", "3 3", "win", 10, 120}, + {"faces not rank pair", BaccaratCommission, "player_pair", "K Q 6", "3 4", "loss", 10, 0}, + {"third card not pair", BaccaratCommission, "player_pair", "A 2 A", "3 4", "loss", 10, 0}, + {"dragon wins despite main push", BaccaratEZ, "dragon_7", "A A 2", "A A 5", "win", 10, 410}, + {"dragon not two card", BaccaratEZ, "dragon_7", "A A 2", "3 4", "loss", 10, 0}, + {"dragon not tie", BaccaratEZ, "dragon_7", "3 4", "A A 5", "loss", 10, 0}, + {"panda win", BaccaratEZ, "panda_8", "A A 6", "A A 5", "win", 10, 260}, + {"panda not natural", BaccaratEZ, "panda_8", "4 4", "3 4", "loss", 10, 0}, + {"panda not tie", BaccaratEZ, "panda_8", "A A 6", "A A 6", "loss", 10, 0}, + {"super6 side two card", BaccaratSuper6, "super_6", "A A 2", "3 3", "win", 10, 130}, + {"super6 side three card", BaccaratSuper6, "super_6", "A A 2", "A A 4", "win", 10, 130}, + {"super6 side tie", BaccaratSuper6, "super_6", "2 4", "3 3", "loss", 10, 0}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + rules := BaccaratConfig{Variant: tc.variant, PairBets: true, Dragon7: true, Panda8: true, Super6Bet: true} + result, payout, err := settleBaccaratHand(tc.spot, tc.bet, baccaratCards(tc.player), baccaratCards(tc.banker), rules) + if err != nil || result != tc.result || payout != tc.payout { + t.Fatalf("got %s/%d/%v; want %s/%d", result, payout, err, tc.result, tc.payout) + } + }) + } + _, _, err := settleBaccaratHand("dragon_7", MaxCredits, baccaratCards("A A 2"), baccaratCards("A A 5"), BaccaratConfig{Dragon7: true}) + if err == nil { + t.Fatal("optional payout overflow accepted") + } +} + +func TestBaccaratVariantValidation(t *testing.T) { + for _, variant := range []string{"", BaccaratCommission, BaccaratSuper6, BaccaratEZ} { + cfg := TableConfig{ID: "test", Game: GameBaccarat, Baccarat: BaccaratConfig{Variant: variant}} + if err := cfg.Validate(); err != nil { + t.Fatal(err) + } + if err := cfg.Normalize().Validate(); err != nil { + t.Fatal(err) + } + } + for _, rules := range []BaccaratConfig{{Variant: "typo"}, {Variant: BaccaratSuper6, BankerPayout: .95}, {Variant: BaccaratEZ, BankerPayout: .98}, {BankerPayout: 1}} { + if err := (TableConfig{ID: "test", Game: GameBaccarat, Baccarat: rules}).Validate(); err == nil { + t.Fatalf("accepted %+v", rules) + } + } + for _, rules := range []BaccaratConfig{{Variant: BaccaratEZ}, {PairBets: true}, {Dragon7: true}, {Panda8: true}, {Super6Bet: true}} { + if err := (TableConfig{ID: "test", Game: GameBlackjack, Baccarat: rules}).Validate(); err == nil { + t.Fatalf("foreign rule accepted: %+v", rules) + } + } +} + +func TestBaccaratEZLiveSettlementAndOptionalMenus(t *testing.T) { + cfg := TableConfig{ID: "ez", Game: GameBaccarat, Baccarat: BaccaratConfig{Variant: BaccaratEZ, Dragon7: true}} + table := newBaccaratTestTable(cfg) + events := table.join("alice") + if !hasMessage(events, "Variant: EZ Baccarat") || !hasMessage(events, "dragon_7") { + t.Fatal("join missing rules/optional wager") + } + table.join("bob") + forceShoe(table, baccaratCards("A A A A 2 5")...) + wa, wb := &testWallet{credits: 100}, &testWallet{credits: 100} + table.bet("alice", "banker", 10, wa) + events = table.bet("bob", "dragon_7", 10, wb) + if wa.paid != 10 || wb.paid != 410 { + t.Fatalf("banker=%d dragon=%d", wa.paid, wb.paid) + } + if !hasMessage(events, "Banker bets push; Player bets lose") { + t.Fatal("special outcome not explained") + } + table = newBaccaratTestTable(TableConfig{ID: "plain", Game: GameBaccarat}) + table.join("alice") + wallet := &testWallet{credits: 100} + events = table.bet("alice", "dragon_7", 10, wallet) + if wallet.credits != 100 || table.players.get("alice").HasBet || !hasMessage(events, "Available: player|banker|tie") { + t.Fatal("disabled optional bet accepted") + } +} + +func TestBaccaratRuleCard(t *testing.T) { + for _, variant := range []string{BaccaratCommission, BaccaratSuper6, BaccaratEZ} { + cfg := TableConfig{ID: "test", Game: GameBaccarat, Baccarat: BaccaratConfig{Variant: variant, Decks: 6, TiePayout: 9}} + text := cfg.BaccaratRulesText() + for _, want := range []string{"Number of decks: 6", "Tie pays 9:1", "Player pair (player_pair): Not offered", "Dragon 7 (dragon_7): Not offered", "Panda 8 (panda_8): Not offered", "Super 6 bet (super_6): Not offered", "One per player per round"} { + if !strings.Contains(text, want) { + t.Errorf("missing %q: %s", want, text) + } + } + switch variant { + case BaccaratCommission: + if !strings.Contains(text, "commission 5%") { + t.Fatal(text) + } + case BaccaratSuper6: + if !strings.Contains(text, "winning 6 pays 1:2") { + t.Fatal(text) + } + case BaccaratEZ: + if !strings.Contains(text, "winning three-card 7 pushes") { + t.Fatal(text) + } + } + } +} |
