Skip to main content

chipzen_bot/
bot.rs

1//! The `Bot` trait — the user-facing extension point.
2//!
3//! Implement [`Bot::decide`] to return one [`crate::Action`] per
4//! `turn_request`. Override the lifecycle hooks if you want to react
5//! to match/round events (default impls do nothing).
6
7use crate::models::{Action, GameState};
8use serde_json::Value;
9
10/// User-implementable poker bot.
11///
12/// `Bot` is `Send + 'static` so [`crate::run_bot`] can move the
13/// instance into the async session loop. It is intentionally **not**
14/// `Sync` — only the session loop calls into it, and forcing `Sync`
15/// would push every implementation into wrapping its strategy state
16/// in a `Mutex` for no benefit.
17///
18/// All hook methods take `&mut self` so a bot can keep mutable state
19/// (opponent model, hand counter, etc.) without interior mutability.
20///
21/// The `*_msg` arguments are passed as raw `serde_json::Value` for
22/// forward-compat — the protocol may add fields the SDK doesn't yet
23/// know about, and consumers can read them without waiting for an SDK
24/// update.
25pub trait Bot: Send + 'static {
26    /// Required: pick the next action. Must be in
27    /// `state.valid_actions`; raises must satisfy
28    /// `state.min_raise <= amount <= state.max_raise`. The session
29    /// loop substitutes a safe fallback if your decide panics or
30    /// returns an invalid action — but lean on the validator
31    /// (`chipzen-sdk validate`) to catch those during development.
32    fn decide(&mut self, state: &GameState) -> Action;
33
34    /// Called when the server's `match_start` arrives. Use for
35    /// per-match setup (read game_config, prep opponent model).
36    fn on_match_start(&mut self, _msg: &Value) {}
37
38    /// Called when each new hand starts.
39    fn on_round_start(&mut self, _msg: &Value) {}
40
41    /// Called when the betting phase changes (preflop → flop, etc.).
42    fn on_phase_change(&mut self, _msg: &Value) {}
43
44    /// Called after every turn the server resolves — yours or any
45    /// opponent's. Useful for opponent modeling.
46    fn on_turn_result(&mut self, _msg: &Value) {}
47
48    /// Called when a hand ends with the result envelope.
49    fn on_round_result(&mut self, _msg: &Value) {}
50
51    /// Called when the match ends. Last chance to flush state if you
52    /// were persisting anything to disk.
53    fn on_match_end(&mut self, _results: &Value) {}
54
55    /// Called after each `turn_action` is sent, with your decision time
56    /// in milliseconds.
57    ///
58    /// `latency_ms` is the wall-clock time [`Bot::decide`] took for the turn
59    /// just sent. The platform enforces a per-turn timeout (default 5000ms,
60    /// announced in `match_start.turn_timeout_ms`); logging this lets you
61    /// catch a bot drifting toward the limit before it starts timing out.
62    ///
63    /// Default impl is a no-op. Override to record / log timings
64    /// (chipzen-ai/chipzen-sdk#46).
65    fn on_decision_latency(&mut self, _latency_ms: f64) {}
66}