aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/baccarat.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino/baccarat.go')
-rw-r--r--internal/casino/baccarat.go64
1 files changed, 46 insertions, 18 deletions
diff --git a/internal/casino/baccarat.go b/internal/casino/baccarat.go
index d415901..fa54ac8 100644
--- a/internal/casino/baccarat.go
+++ b/internal/casino/baccarat.go
@@ -179,6 +179,8 @@ func (t *baccaratTable) rebet(name string) (string, int) {
// deal plays out the hand by the punto banco tableau, settles every wager,
// and resets the table for the next round.
func (t *baccaratTable) deal() []Event {
+ // The cut-card reshuffle happens between rounds, never mid-hand.
+ t.shoe.shuffleIfDue()
player, banker := playTableau(t.shoe)
playerTotal, bankerTotal := baccaratTotal(player), baccaratTotal(banker)
round := t.event("New baccarat hand!", true)
@@ -189,19 +191,38 @@ func (t *baccaratTable) deal() []Event {
continue
}
result, payout := settleBaccaratBet(p.lastSpot, p.Wager.Total, playerTotal, bankerTotal, t.Rules)
+ payoutFailed := false
if payout > 0 && p.Wallet != nil {
- p.Wallet.PayCredits(payout)
+ if err := p.Wallet.PayCredits(payout); err != nil {
+ // The payout could not be made durable; recovery will
+ // re-apply the owed amount from the open journal record.
+ // Do not announce the win as paid and leave the stakes
+ // open for recovery to settle.
+ payoutFailed = true
+ publicResult, _ := baccaratResultMessages(p.Name, result, payout)
+ settlement := t.playerAction(p.Name, publicResult, payoutFailedMessage())
+ if result == "win" {
+ settlement.Color = "casino_big_win"
+ }
+ events = append(events, settlement)
+ }
}
- publicResult, privateResult := baccaratResultMessages(p.Name, result, payout)
- settlement := t.playerAction(p.Name, publicResult, privateResult)
- if result == "win" {
- if p.lastSpot == baccaratSpotTie {
- settlement.Color = "casino_jackpot_win"
- } else {
- settlement.Color = "casino_big_win"
+ if !payoutFailed {
+ publicResult, privateResult := baccaratResultMessages(p.Name, result, payout)
+ settlement := t.playerAction(p.Name, publicResult, privateResult)
+ if result == "win" {
+ if p.lastSpot == baccaratSpotTie {
+ settlement.Color = "casino_jackpot_win"
+ } else {
+ settlement.Color = "casino_big_win"
+ }
}
+ events = append(events, settlement)
+ }
+ if p.Wallet != nil && !payoutFailed {
+ // The round resolved; the stakes are no longer outstanding.
+ p.Wallet.ResolveStakes()
}
- events = append(events, settlement)
}
t.resetRound()
for _, p := range t.players.ordered() {
@@ -247,6 +268,11 @@ func (t *baccaratTable) markDisconnected(name string) []Event {
if !t.players.has(name) {
return nil
}
+ // A disconnected player forfeits the round in flight: resolve the
+ // stakes now so startup recovery does not refund them later.
+ if p := t.players.get(name); p != nil && p.HasBet && p.Wallet != nil {
+ p.Wallet.ResolveStakes()
+ }
t.players.remove(name)
events := []Event{t.event(fmt.Sprintf("%s stands up.", name), true)}
if t.players.count() == 0 {
@@ -364,7 +390,8 @@ func baccaratResultMessages(name, result string, payout int) (string, string) {
}
// refundAll resolves every committed wager at this table (graceful
-// shutdown): stakes return to their players through the wallet.
+// shutdown/eviction): the wallet returns every outstanding stake exactly
+// once.
func (t *baccaratTable) refundAll() []Event {
t.mu.Lock()
defer t.mu.Unlock()
@@ -373,17 +400,18 @@ func (t *baccaratTable) refundAll() []Event {
if !p.HasBet || p.Wallet == nil {
continue
}
- name, refund := p.Name, p.Wager.Total
- if refund <= 0 {
- continue
- }
- if err := p.Wallet.PayCredits(refund); err != nil {
- events = append(events, t.private(name, "Shutdown refund could not be persisted; the stake is recorded for recovery."))
+ name := p.Name
+ chips, credits, err := p.Wallet.RefundOutstanding()
+ refund := chips + credits
+ if err != nil || refund <= 0 {
+ if err != nil {
+ events = append(events, t.private(name, "Shutdown refund could not be persisted; the stake is recorded for recovery."))
+ }
continue
}
events = append(events, t.playerAction(name,
- fmt.Sprintf("%s is refunded %d credits as the casino closes.", name, refund),
- fmt.Sprintf("The house refunds your %d credit stake for the shutdown.", refund)))
+ fmt.Sprintf("%s is refunded %d as the casino closes.", name, refund),
+ fmt.Sprintf("The house refunds your %d stake for the shutdown.", refund)))
}
return events
}