aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/baccarat_variants_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino/baccarat_variants_test.go')
-rw-r--r--internal/casino/baccarat_variants_test.go139
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)
+ }
+ }
+ }
+}