Skip to content

Commit 00f03ac

Browse files
authored
feat: show all hole cards and bot names in hand-history mode (#33)
* feat: show all hole cards and bot names in hand-history mode Improves hand-history mode for bot testing and debugging by showing all players' hole cards from the start of each hand, and displaying bot names instead of IDs throughout. Changes: - Add HoleCards field to HandPlayer struct for passing cards to monitors - Update hand runner to pass hole cards and display names in OnHandStart - Modify pretty monitor to display all players' hole cards in the "HOLE CARDS" section - Use bot.DisplayName() instead of bot.ID for consistent naming This makes it much easier to understand bot decision-making when reviewing hands, as you can see what everyone was holding from the start rather than only at showdown. Example output: *** HOLE CARDS *** Dealt to aragorn-dev Q♣ K♠ Dealt to complex-bot-1 7♣ J♥ Dealt to complex-bot-2 9♦ K♦ All names (aragorn-dev, complex-bot-1, etc.) now appear consistently in headers, actions, and summary. * fix: preserve bot ID in HandPlayer.Name for stats tracking Addresses P1 feedback: using DisplayName as HandPlayer.Name broke stats tracking because StatsMonitor uses player.Name as the stable key for indexing bot statistics. Changes: - Add DisplayName field to HandPlayer struct for human-readable display - Keep Name field as bot.ID for stable stats tracking (prevents stats key mismatches and duplicate display name conflicts) - Update pretty monitor to use DisplayName for all display purposes (seat listings, hole cards, actions) This ensures stats tracking works correctly while still showing friendly bot names (aragorn-dev, complex-bot-1, etc.) consistently in hand-history output. Co-authored-by: chatgpt-codex-connector[bot]
1 parent d4cf1a7 commit 00f03ac

File tree

3 files changed

+62
-37
lines changed

3 files changed

+62
-37
lines changed

internal/server/hand_runner.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,10 +320,20 @@ func (hr *HandRunner) broadcastHandStart() {
320320
monitor := hr.pool.GetHandMonitor()
321321
players := make([]HandPlayer, len(hr.bots))
322322
for i, bot := range hr.bots {
323+
player := hr.handState.Players[i]
324+
displayName := bot.DisplayName()
325+
if displayName == "" {
326+
displayName = bot.ID // Fallback to ID if no display name
327+
}
323328
players[i] = HandPlayer{
324-
Seat: i,
325-
Name: bot.ID, // Use bot ID for stats tracking
326-
Chips: hr.handState.Players[i].Chips,
329+
Seat: i,
330+
Name: bot.ID, // Stable bot ID for stats tracking
331+
DisplayName: displayName, // Human-readable name for display
332+
Chips: player.Chips,
333+
HoleCards: []string{
334+
player.HoleCards.GetCard(0).String(),
335+
player.HoleCards.GetCard(1).String(),
336+
},
327337
}
328338
}
329339
blinds := Blinds{

internal/server/monitor.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ type HandMonitor interface {
2525

2626
// HandPlayer represents a player at the start of a hand.
2727
type HandPlayer struct {
28-
Seat int
29-
Name string
30-
Chips int
28+
Seat int
29+
Name string // Bot ID for stable stats tracking
30+
DisplayName string // Human-readable name for display
31+
Chips int
32+
HoleCards []string // Hole cards for debugging/testing (not sent to other players)
3133
}
3234

3335
// Blinds represents the blind structure.

internal/server/pretty_monitor.go

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,20 @@ const (
2121

2222
// handState tracks the current hand for pretty printing
2323
type prettyHandState struct {
24-
handID string
25-
players []HandPlayer
26-
button int
27-
smallBlind int
28-
bigBlind int
29-
board []string
30-
currentStreet string
31-
playerStacks map[int]int
32-
playerFolded map[int]bool
33-
playerAllIn map[int]bool
34-
roles map[int][]string
35-
printedStreets map[string]bool
36-
printedHole bool
24+
handID string
25+
players []HandPlayer
26+
button int
27+
smallBlind int
28+
bigBlind int
29+
board []string
30+
currentStreet string
31+
playerStacks map[int]int
32+
playerFolded map[int]bool
33+
playerAllIn map[int]bool
34+
playerHoleCards map[int][]string // Hole cards for each seat
35+
roles map[int][]string
36+
printedStreets map[string]bool
37+
printedHole bool
3738
}
3839

3940
// PrettyPrintMonitor implements HandMonitor for formatted hand display
@@ -82,24 +83,26 @@ func (p *PrettyPrintMonitor) OnHandStart(handID string, players []HandPlayer, bu
8283

8384
// Initialize hand state
8485
p.currentHand = &prettyHandState{
85-
handID: handID,
86-
players: players,
87-
button: button,
88-
smallBlind: blinds.Small,
89-
bigBlind: blinds.Big,
90-
board: []string{},
91-
currentStreet: "preflop",
92-
playerStacks: make(map[int]int),
93-
playerFolded: make(map[int]bool),
94-
playerAllIn: make(map[int]bool),
95-
roles: make(map[int][]string),
96-
printedStreets: make(map[string]bool),
97-
printedHole: false,
98-
}
99-
100-
// Initialize player stacks
86+
handID: handID,
87+
players: players,
88+
button: button,
89+
smallBlind: blinds.Small,
90+
bigBlind: blinds.Big,
91+
board: []string{},
92+
currentStreet: "preflop",
93+
playerStacks: make(map[int]int),
94+
playerFolded: make(map[int]bool),
95+
playerAllIn: make(map[int]bool),
96+
playerHoleCards: make(map[int][]string),
97+
roles: make(map[int][]string),
98+
printedStreets: make(map[string]bool),
99+
printedHole: false,
100+
}
101+
102+
// Initialize player stacks and hole cards
101103
for _, player := range players {
102104
p.currentHand.playerStacks[player.Seat] = player.Chips
105+
p.currentHand.playerHoleCards[player.Seat] = player.HoleCards
103106
}
104107

105108
// Assign roles
@@ -112,7 +115,7 @@ func (p *PrettyPrintMonitor) OnHandStart(handID string, players []HandPlayer, bu
112115
// Print players
113116
for _, player := range players {
114117
seatNum := player.Seat + 1
115-
name := p.formatPlayerName(player.Seat, player.Name, false, false)
118+
name := p.formatPlayerName(player.Seat, player.DisplayName, false, false)
116119
line := fmt.Sprintf("Seat %d: %s", seatNum, name)
117120
line += fmt.Sprintf(" (%s in chips)", formatAmountPlain(player.Chips))
118121
fmt.Fprintln(p.writer, line)
@@ -150,13 +153,23 @@ func (p *PrettyPrintMonitor) OnPlayerAction(handID string, seat int, action stri
150153
p.currentHand.printedHole = true
151154
fmt.Fprintln(p.writer)
152155
fmt.Fprintln(p.writer, colorize("*** HOLE CARDS ***", colorBold+colorBlue))
156+
157+
// Print hole cards for all players (for testing/debugging)
158+
for _, player := range p.currentHand.players {
159+
if cards, ok := p.currentHand.playerHoleCards[player.Seat]; ok && len(cards) > 0 {
160+
name := fallbackName(player.DisplayName, player.Seat)
161+
fmt.Fprintf(p.writer, "Dealt to %s %s\n",
162+
colorize(name, colorBold),
163+
formatCards(cards))
164+
}
165+
}
153166
}
154167

155168
// Get player name
156169
playerName := "Unknown"
157170
for _, player := range p.currentHand.players {
158171
if player.Seat == seat {
159-
playerName = player.Name
172+
playerName = player.DisplayName
160173
break
161174
}
162175
}

0 commit comments

Comments
 (0)