package casino // countdown tracks tick-based deadlines for table phases (betting windows, // insurance offers, and player action timeouts). type countdown struct { clock int deadline int } func (c *countdown) start(ticks int) { c.clock, c.deadline = 0, ticks } func (c *countdown) stop() { c.clock, c.deadline = 0, 0 } func (c *countdown) tick() { c.clock++ } func (c *countdown) expired() bool { return c.deadline > 0 && c.clock >= c.deadline } // bettingWindow is the timed betting phase shared by multiplayer table games. // The owning table opens it when the first wager lands with multiple seated // players and decides the outcome (deal/resolve vs reset) from advance. type bettingWindow struct { timer countdown } func (w *bettingWindow) open(ticks int) { w.timer.start(ticks) } func (w *bettingWindow) close() { w.timer.stop() } // advance moves the window one tick. pending means the window is still open; // otherwise resolve reports whether wagers are down and the round should play // out, and sitOuts names the seated players who never bet. func (w *bettingWindow) advance(seats []*Participant) (sitOuts []string, resolve, pending bool) { w.timer.tick() if allBet(seats) { return nil, true, false } if !w.timer.expired() { return nil, false, true } return sitOutNames(seats), anyBet(seats), false } // allBet reports whether at least one connected participant exists and every // connected participant has placed a bet. Disconnected participants never // hold a round open. func allBet(seats []*Participant) bool { connected := false for _, p := range seats { if !p.Connected { continue } connected = true if !p.HasBet { return false } } return connected } // anyBet reports whether any participant, connected or not, has a wager // committed to the current round. func anyBet(seats []*Participant) bool { for _, p := range seats { if p.HasBet { return true } } return false } // sitOutNames lists participants without a bet, in seat order. func sitOutNames(seats []*Participant) []string { var names []string for _, p := range seats { if !p.HasBet { names = append(names, p.Name) } } return names } // clearBets resets per-round betting state on every participant. func clearBets(seats []*Participant) { for _, p := range seats { p.HasBet = false p.Wager = Wager{} } }