aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/baccarat_variants.go
blob: 95b5041ea7d0e00f8ac82c0aae8605ed08f414de (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
package casino

import "strings"

const (
	BaccaratCommission = "commission"
	BaccaratSuper6     = "super_6"
	BaccaratEZ         = "ez_baccarat"
)

// Optional wagers use fixed, conventional profit odds. They are evaluated
// independently of the main Banker wager (notably the EZ Dragon 7 push).
func (c BaccaratConfig) spots() []string {
	spots := []string{baccaratSpotPlayer, baccaratSpotBanker, baccaratSpotTie}
	if c.PairBets {
		spots = append(spots, "player_pair", "banker_pair")
	}
	if c.Dragon7 {
		spots = append(spots, "dragon_7")
	}
	if c.Panda8 {
		spots = append(spots, "panda_8")
	}
	if c.Super6Bet {
		spots = append(spots, "super_6")
	}
	return spots
}

func (c BaccaratConfig) allowsSpot(spot string) bool {
	for _, allowed := range c.spots() {
		if spot == allowed {
			return true
		}
	}
	return false
}

func (c BaccaratConfig) betChoices() string { return strings.Join(c.spots(), "|") }

func baccaratPair(hand []card) bool {
	return len(hand) >= 2 && hand[0].rank == hand[1].rank
}

// settleBaccaratHand needs the complete hands: totals alone cannot identify
// a three-card seven/eight or an initial pair.
func settleBaccaratHand(spot string, bet int, player, banker []card, rules BaccaratConfig) (string, int, error) {
	rules = rules.Normalize()
	if !rules.allowsSpot(spot) {
		return "loss", 0, nil
	}
	pt, bt := baccaratTotal(player), baccaratTotal(banker)
	win, odds := false, 0.0
	switch spot {
	case "player_pair":
		win, odds = baccaratPair(player), 11
	case "banker_pair":
		win, odds = baccaratPair(banker), 11
	case "dragon_7":
		win, odds = bt > pt && bt == 7 && len(banker) == 3, 40
	case "panda_8":
		win, odds = pt > bt && pt == 8 && len(player) == 3, 25
	case "super_6":
		win, odds = bt > pt && bt == 6, 12
	default:
		if spot == baccaratSpotBanker && bt > pt {
			if rules.Variant == BaccaratEZ && bt == 7 && len(banker) == 3 {
				return "push", bet, nil
			}
			if rules.Variant == BaccaratSuper6 && bt == 6 {
				// Floor the total return, not a positive-profit minimum: a one-credit
				// stake returns one credit, not two, on this half-pay outcome.
				payout, err := MulPayout(bet, 1.5)
				return "win", payout, err
			}
		}
		return settleBaccaratBet(spot, bet, pt, bt, rules)
	}
	if !win {
		return "loss", 0, nil
	}
	payout, err := MulPayout(bet, 1+odds)
	return "win", payout, err
}

func baccaratVariantOutcome(player, banker []card, rules BaccaratConfig) string {
	pt, bt := baccaratTotal(player), baccaratTotal(banker)
	if bt <= pt {
		return ""
	}
	if rules.Variant == BaccaratEZ && bt == 7 && len(banker) == 3 {
		return "EZ Baccarat: Banker wins with a three-card 7. Banker bets push; Player bets lose."
	}
	if rules.Variant == BaccaratSuper6 && bt == 6 {
		return "Super 6: Banker wins with 6. Banker bets pay 1:2."
	}
	return ""
}