aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/betting.go
blob: fa2d2dc099fad9e915ac1a34779bfef15deab5bb (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
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{}
	}
}