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}