Skip to content

Commit d4cf1a7

Browse files
authored
feat: replace --pretty with --output flag for flexible log control (#32)
Replaces the boolean --pretty flag with an --output enum flag that supports three modes for better control over bot log output during testing: - logs: Show all bot logs (default, current behavior) - bot-cmd: Show only custom bot logs from --bot-cmd, suppress built-in bot logs from --spec (useful for focusing on your bot's behavior) - hand-history: Pretty hand visualization with all bot logs suppressed (replaces --pretty) Implementation adds QuietLogs field to BotSpec, allowing per-bot log control. Built-in bots are quieted in bot-cmd and hand-history modes, while custom bots are only quieted in hand-history mode. This makes it much easier to test custom bots without noise from the pre-canned opponent bots.
1 parent d89c370 commit d4cf1a7

File tree

2 files changed

+49
-29
lines changed

2 files changed

+49
-29
lines changed

cmd/pokerforbots/spawn.go

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ type SpawnCmd struct {
4747
WriteStats string `kong:"help='Write stats to file on exit'"`
4848
PrintStats bool `kong:"help='Print stats on exit'"`
4949

50-
// Display mode
51-
Pretty bool `kong:"help='Display hands in pretty format instead of logs'"`
50+
// Output format
51+
Output string `kong:"default='logs',enum='logs,bot-cmd,hand-history',help='Output format: logs (all logs), bot-cmd (only custom bot logs), hand-history (pretty hand visualization)'"`
5252

5353
// Logging
5454
LogLevel string `kong:"help='Log level (debug|info|warn|error)'"`
@@ -68,8 +68,8 @@ func (c *SpawnCmd) Run() error {
6868
level = zerolog.ErrorLevel
6969
}
7070

71-
// In pretty mode, suppress most logs unless explicitly set
72-
if c.Pretty && c.LogLevel == "" {
71+
// In hand-history mode, suppress most logs unless explicitly set
72+
if c.Output == "hand-history" && c.LogLevel == "" {
7373
level = zerolog.WarnLevel
7474
}
7575

@@ -96,7 +96,7 @@ func (c *SpawnCmd) Run() error {
9696
wsURL := fmt.Sprintf("ws://%s/ws", listener.Addr())
9797

9898
// Now parse bot specifications with the WebSocket URL
99-
specs, err := parseSpecString(c.Spec, wsURL)
99+
specs, err := parseSpecString(c.Spec, wsURL, c.Output)
100100
if err != nil {
101101
return fmt.Errorf("failed to parse spec: %w", err)
102102
}
@@ -109,10 +109,12 @@ func (c *SpawnCmd) Run() error {
109109
if len(parts) == 0 {
110110
continue
111111
}
112+
// Custom bot logs are suppressed only in hand-history mode
112113
specs = append(specs, spawner.BotSpec{
113-
Command: parts[0],
114-
Args: parts[1:], // Don't append wsURL - bot should use POKERFORBOTS_SERVER
115-
Count: c.Count,
114+
Command: parts[0],
115+
Args: parts[1:], // Don't append wsURL - bot should use POKERFORBOTS_SERVER
116+
Count: c.Count,
117+
QuietLogs: c.Output == "hand-history",
116118
})
117119
}
118120

@@ -130,7 +132,7 @@ func (c *SpawnCmd) Run() error {
130132
minPlayers := c.MinPlayers
131133
if minPlayers == 0 {
132134
minPlayers = max(2, min(totalBots, c.MaxPlayers))
133-
if !c.Pretty {
135+
if c.Output != "hand-history" {
134136
logger.Info().Int("min_players", minPlayers).Int("total_bots", totalBots).Msg("Auto-setting min-players to match bot count")
135137
}
136138
}
@@ -168,8 +170,8 @@ func (c *SpawnCmd) Run() error {
168170
return fmt.Errorf("server failed to start: %w", err)
169171
}
170172

171-
// Set up pretty printer if requested
172-
if c.Pretty {
173+
// Set up hand-history monitor if requested
174+
if c.Output == "hand-history" {
173175
monitor := server.NewPrettyPrintMonitor(os.Stdout)
174176
srv.SetHandMonitor(monitor)
175177
} else {
@@ -179,16 +181,13 @@ func (c *SpawnCmd) Run() error {
179181
}
180182
}
181183

182-
if !c.Pretty {
184+
if c.Output != "hand-history" {
183185
logger.Info().Str("spec", c.Spec).Int("additional", len(c.BotCmd)).Int("total_bots", totalBots).Msg("Spawning bots")
184186
}
185187

186188
// Create spawner and spawn bots
189+
// Note: Per-bot quiet logs are handled by BotSpec.QuietLogs, not here
187190
spawnerLogger := logger
188-
if c.Pretty {
189-
// Create a quiet logger for bot processes in pretty mode
190-
spawnerLogger = zerolog.New(io.Discard).Level(zerolog.Disabled)
191-
}
192191

193192
botSpawner := spawner.NewWithSeed(wsURL, spawnerLogger, seed)
194193
defer botSpawner.StopAll()
@@ -197,7 +196,7 @@ func (c *SpawnCmd) Run() error {
197196
return fmt.Errorf("failed to spawn bots: %w", err)
198197
}
199198

200-
if !c.Pretty {
199+
if c.Output != "hand-history" {
201200
logger.Info().Int("count", botSpawner.ActiveCount()).Msg("Bots spawned")
202201
}
203202

@@ -233,7 +232,7 @@ func (c *SpawnCmd) Run() error {
233232
case <-ctx.Done():
234233
logger.Info().Msg("Shutting down...")
235234
case <-srv.DefaultGameDone():
236-
if !c.Pretty {
235+
if c.Output != "hand-history" {
237236
logger.Info().Msg("Hand limit reached")
238237
}
239238
case err := <-serverErr:
@@ -271,15 +270,15 @@ func (c *SpawnCmd) Run() error {
271270
time.Sleep(100 * time.Millisecond)
272271

273272
// Stop bots (will be done by defer)
274-
if !c.Pretty {
273+
if c.Output != "hand-history" {
275274
logger.Info().Msg("Stopping bots...")
276275
}
277276

278277
return nil
279278
}
280279

281280
// parseSpecString parses a specification string like "calling-station:2,random:1,aggressive:3"
282-
func parseSpecString(spec string, wsURL string) ([]spawner.BotSpec, error) {
281+
func parseSpecString(spec string, wsURL string, outputMode string) ([]spawner.BotSpec, error) {
283282
if spec == "" {
284283
return nil, nil
285284
}
@@ -314,6 +313,7 @@ func parseSpecString(spec string, wsURL string) ([]spawner.BotSpec, error) {
314313
// Check if it's a built-in bot or a custom command
315314
var command string
316315
var args []string
316+
var isBuiltIn bool
317317

318318
if strings.Contains(strategy, "/") || strings.Contains(strategy, ".") {
319319
// Custom command (e.g., "./my-bot")
@@ -324,16 +324,29 @@ func parseSpecString(spec string, wsURL string) ([]spawner.BotSpec, error) {
324324
command = fields[0]
325325
args = fields[1:]
326326
args = append(args, wsURL)
327+
isBuiltIn = false
327328
} else {
328329
// Built-in bot - use pokerforbots bot subcommand
329330
command = pokerforbotsBin
330331
args = []string{"bot", strategy, "--server", wsURL}
332+
isBuiltIn = true
333+
}
334+
335+
// Determine if logs should be suppressed for this bot
336+
quietLogs := false
337+
if isBuiltIn {
338+
// Suppress built-in bot logs in bot-cmd and hand-history modes
339+
quietLogs = (outputMode == "bot-cmd" || outputMode == "hand-history")
340+
} else {
341+
// Suppress custom bot logs only in hand-history mode
342+
quietLogs = (outputMode == "hand-history")
331343
}
332344

333345
specs = append(specs, spawner.BotSpec{
334-
Command: command,
335-
Args: args,
336-
Count: count,
346+
Command: command,
347+
Args: args,
348+
Count: count,
349+
QuietLogs: quietLogs,
337350
})
338351
}
339352

sdk/spawner/spawner.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ type BotSpawner struct {
3030

3131
// BotSpec defines a bot to spawn.
3232
type BotSpec struct {
33-
Command string // Command to execute (e.g. "go")
34-
Args []string // Arguments (e.g. ["run", "./sdk/examples/calling-station"])
35-
Count int // Number to spawn
36-
GameID string // Target game (default: "default")
37-
Env map[string]string // Additional environment variables
33+
Command string // Command to execute (e.g. "go")
34+
Args []string // Arguments (e.g. ["run", "./sdk/examples/calling-station"])
35+
Count int // Number to spawn
36+
GameID string // Target game (default: "default")
37+
Env map[string]string // Additional environment variables
38+
QuietLogs bool // Suppress process output logs
3839
}
3940

4041
// GameStats represents game statistics from the server.
@@ -109,8 +110,14 @@ func (s *BotSpawner) Spawn(specs ...BotSpec) error {
109110
// Build environment with deterministic bot ID
110111
env := s.buildEnvWithID(spec, botID)
111112

113+
// Create logger - use quiet logger if requested
114+
procLogger := s.logger
115+
if spec.QuietLogs {
116+
procLogger = zerolog.New(io.Discard).Level(zerolog.Disabled)
117+
}
118+
112119
// Create and start process
113-
proc := NewProcess(s.ctx, spec.Command, spec.Args, env, s.logger)
120+
proc := NewProcess(s.ctx, spec.Command, spec.Args, env, procLogger)
114121
if err := proc.Start(); err != nil {
115122
s.logger.Error().Err(err).Int("index", i).Msg("Failed to spawn bot")
116123
// Stop previously spawned bots on error

0 commit comments

Comments
 (0)