diff options
Diffstat (limited to 'internal/casino/blackjack.go')
| -rw-r--r-- | internal/casino/blackjack.go | 56 |
1 files changed, 33 insertions, 23 deletions
diff --git a/internal/casino/blackjack.go b/internal/casino/blackjack.go index a047c71..3bc70b4 100644 --- a/internal/casino/blackjack.go +++ b/internal/casino/blackjack.go @@ -81,12 +81,11 @@ type blackjackTable struct { } // loneTurnGrace multiplies the action window into an absolute per-turn -// ceiling. A lone connected player is deliberately never timed out by the -// action timer (a solo player's think time must not end the hand), but -// "never" cannot mean forever: a hung-but-connected client would hold the -// hand, the seat, and the stake open with no automatic resolution, since -// "leave" refuses mid-round. After this many ticks on one turn the table -// stands the hand for the player and plays on. +// ceiling. A lone connected player has no time limit at all (a solo player's +// think time must not end the hand), but a multiplayer turn is still bounded: +// a hung client must not hold the table, the other seats, and their stakes +// open, since "leave" refuses mid-round. After this many ticks on one turn +// the table stands the hand for the player and plays on. const loneTurnGrace = 10 func newBlackjackTable(roomID int, cfg TableConfig, rng *rand.Rand) *blackjackTable { @@ -212,6 +211,10 @@ func (t *blackjackTable) tick() []Event { if t.insuranceSettled() { return t.finishInsurance() } + // A lone bettor is never timed out into declining insurance. + if t.bettorCount() <= 1 { + return nil + } t.timer.tick() if !t.timer.expired() { var events []Event @@ -224,7 +227,14 @@ func (t *blackjackTable) tick() []Event { } return events } - return t.finishInsurance() + // Announce the timeout itself, never silently declining the offer. + var events []Event + for _, p := range t.players.ordered() { + if p.HasBet && p.insurance == 0 && !p.declinedInsurance && p.Connected { + events = append(events, t.warning(p.Name, "Insurance timed out; continuing without it.")) + } + } + return append(events, t.finishInsurance()...) case blackjackPlaying: name := t.activeName() if name == "" { @@ -236,24 +246,20 @@ func (t *blackjackTable) tick() []Event { t.activePlayer++ return t.finishHand() } - // The absolute turn ceiling bounds every turn, including a lone - // connected player's: the action timer below deliberately never - // expires for a solo player, so without this a hung-but-connected - // client would hold the stake and the phase open forever. - t.ceiling.tick() - if t.ceiling.expired() { - active.hands[t.activeHand].stood = true - events := []Event{t.warning(name, "You've been idle too long; standing.")} - // Restart the action timer so the next hand gets a full window. - t.timer.start(t.Rules.ActionTicks) - return append(events, t.finishHand()...) + // A lone bettor has no time limit at all. With two or more bettors + // the absolute turn ceiling bounds a hung client; a disconnected + // active player is always timed out so the table can never stall. + if t.bettorCount() > 1 { + t.ceiling.tick() + if t.ceiling.expired() { + active.hands[t.activeHand].stood = true + events := []Event{t.warning(name, "You've been idle too long; standing.")} + // Restart the action timer so the next hand gets a full window. + t.timer.start(t.Rules.ActionTicks) + return append(events, t.finishHand()...) + } } if active.Connected && t.bettorCount() <= 1 { - // A lone connected player is never timed out. A disconnected - // active player always is, so the table can never stall. - if t.ceiling.warn() { - return []Event{t.warning(name, fmt.Sprintf("%d ticks remaining. No response means stand.", t.ceiling.remaining()))} - } return nil } t.timer.tick() @@ -278,6 +284,10 @@ func (t *blackjackTable) tick() []Event { func (t *blackjackTable) action(name, action string) []Event { t.mu.Lock() defer t.mu.Unlock() + if action == "unbet" { + events, _ := t.unbetLocked(name) + return events + } if events, handled := t.controlLocked(name, action); handled { if t.phase == blackjackBetting && allBet(t.players.participants()) { return append(events, t.startRound()...) |
