From d114c747a7672c7e4daab3a5b5f1b2f066ebc912 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Thu, 24 Sep 2026 19:31:05 -0400 Subject: feat: additional baca and bj rules variants --- internal/casino/baccarat.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'internal/casino/baccarat.go') diff --git a/internal/casino/baccarat.go b/internal/casino/baccarat.go index 53ed0a9..68f363e 100644 --- a/internal/casino/baccarat.go +++ b/internal/casino/baccarat.go @@ -19,10 +19,6 @@ const ( baccaratSpotTie = "tie" ) -func baccaratSpotValid(spot string) bool { - return spot == baccaratSpotPlayer || spot == baccaratSpotBanker || spot == baccaratSpotTie -} - type baccaratPlayer struct { Participant lastBet int @@ -53,7 +49,7 @@ func newBaccaratTable(roomID int, cfg TableConfig, rng *rand.Rand) *baccaratTabl func (t *baccaratTable) menuEvent(name string) Event { return t.betMenuEvent(name, []MenuCommand{ {Command: "bet ", Desc: "Place a wager (min/max accepted)"}, - {Command: "bet on player|banker|tie", Desc: "Wager on a specific outcome"}, + {Command: "bet on " + t.Rules.betChoices(), Desc: "Wager on a specific outcome (one wager per round)"}, {Command: "stop", Desc: "Leave the table"}, }) } @@ -68,7 +64,7 @@ func (t *baccaratTable) join(name string) []Event { return []Event{t.private(name, "That baccarat table is full.")} } events := []Event{t.addSeatLocked(name, &baccaratPlayer{Participant: Participant{Name: name, Connected: true}})} - events = append(events, t.menuEvent(name)) + events = append(events, t.private(name, t.Config.BaccaratRulesText()), t.menuEvent(name)) return events } @@ -100,10 +96,10 @@ func (t *baccaratTable) bet(name, spot string, amount int, wallet Wallet) []Even spot = p.lastSpot } if spot == "" { - return []Event{t.private(name, "Bet on what? Try \"bet on player\", \"bet on banker\", or \"bet on tie\".")} + return []Event{t.private(name, "Bet on what? Try bet on "+t.Rules.betChoices()+".")} } - if !baccaratSpotValid(spot) { - return []Event{t.private(name, "Baccarat bets must be on player, banker, or tie.")} + if !t.Rules.allowsSpot(spot) { + return []Event{t.private(name, "Baccarat bets must be on player, banker, or tie, or an enabled optional bet. Available: "+t.Rules.betChoices()+".")} } if p.HasBet { return []Event{t.private(name, "You have already bet this round.")} @@ -163,11 +159,14 @@ func (t *baccaratTable) deal() []Event { round := t.event("New baccarat hand!", true) round.Snapshot = t.snapshot(player, banker) events := []Event{round, t.event(baccaratOutcomeMessage(playerTotal, bankerTotal), true)} + if explanation := baccaratVariantOutcome(player, banker, t.Rules); explanation != "" { + events = append(events, t.event(explanation, true)) + } for _, p := range t.players.ordered() { if !p.HasBet { continue } - result, payout, settleErr := settleBaccaratBet(p.lastSpot, p.Wager.Total, playerTotal, bankerTotal, t.Rules) + result, payout, settleErr := settleBaccaratHand(p.lastSpot, p.Wager.Total, player, banker, t.Rules) // The shared settlement path pays before announcing and leaves the // stake pending on any failure, so a refund can never be paid on top // of a payout that was not durable. @@ -311,8 +310,11 @@ func bankerDraws(bankerTotal, playerThird int) bool { } } -// settleBaccaratBet returns the result label and total payout (stake plus -// winnings) for one wager. Player and banker bets push on a tie. Winnings +// settleBaccaratBet is the ordinary main-wager payout helper. Gameplay must +// use settleBaccaratHand first to apply variant exceptions and optional bets; +// totals alone cannot identify a three-card Banker seven. +// It returns the result label and total payout (stake plus winnings). +// Player and banker bets push on a tie. Winnings // round down to whole credits, so the banker commission effectively rounds // up: never bet less than one commission unit. func settleBaccaratBet(spot string, bet int, playerTotal, bankerTotal int, rules BaccaratConfig) (string, int, error) { -- cgit v1.2.3