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") rulesView := false for _, e := range events { if view, ok := e.Snapshot.(*BaccaratRulesView); ok { rulesView = true if !view.Config.Baccarat.Dragon7 { t.Fatal("rules felt missing the enabled Dragon 7 wager") } } } if !rulesView { t.Fatal("join missing rules felt") } 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) } } } }