simple_mcts/
game.rs

1//! Module defining traits for games and game evaluators used in MCTS.
2
3/// Trait defining the interface for a game that can be used with MCTS.
4///
5/// Implementations of this trait provide the core game logic,
6/// allowing the MCTS algorithm to simulate and analyze game states.
7///
8/// # Type Parameters
9/// - `N`: The number of possible actions in the game. This is a constant generic
10///        parameter, meaning the number of actions is fixed at compile time.
11pub trait Game<const N: usize>{
12    /// The associated type representing the immutable state of the game.
13    /// This type should ideally be lightweight. It can be use
14    /// in `GameEvaluator` or AI model for make prediction.
15    type State;
16
17    /// Creates a new instance of the game in its initial state.
18    ///
19    /// This is typically the starting point for any new MCTS simulation.
20    ///
21    /// # Returns
22    /// A new game instance initialized to its starting state.
23    ///
24    /// # Examples
25    /// ```rust*
26    /// use simple_mcts::Game;
27    /// use simple_mcts::test_utils::GameTest;
28    /// let game = GameTest::new();
29    /// // game is now in its initial state.
30    /// ```
31    fn new() -> Self;
32
33    /// Returns an array indicating which actions are currently valid from the current game state.
34    ///
35    /// An action is valid if it can be played at this specific moment in the game.
36    /// Actions are identified by their index (from 0 to `N-1`).
37    ///
38    /// # Returns
39    /// An array of booleans where `true` at index `i` means the action `i` is valid,
40    /// and `false` means it's invalid.
41    ///
42    /// # Examples
43    /// ```rust
44    /// use simple_mcts::Game;
45    /// use simple_mcts::test_utils::*;
46    /// let game = GameTest::new();
47    /// let actions = game.get_actions();
48    /// // For GameTest, initially all actions are valid: [true, true, true, true]
49    /// assert_eq!(actions, [true, true, true, true]);
50    /// ```
51    fn get_actions(&self) -> [bool; N];
52
53    /// Determines if the game has reached a terminal state (i.e., it's over).
54    ///
55    /// A game is finished if no more moves can be made, or if a win/loss/draw
56    /// condition has been met.
57    ///
58    /// # Returns
59    /// `true` if the game is finished, `false` otherwise.
60    ///
61    /// # Examples
62    /// ```rust
63    /// use simple_mcts::Game;
64    /// use simple_mcts::test_utils::GameTest;
65    /// let mut game = GameTest::new();
66    /// assert!(!game.is_finish());
67    /// game.play(0); game.play(1); game.play(2); game.play(3);
68    /// assert!(game.is_finish());
69    /// ```
70    fn is_finish(&self) -> bool;
71
72    /// Applies a given action to the game, transitioning it to a new state.
73    ///
74    /// This method modifies the current game instance. It's assumed that the
75    /// `action` provided is valid according to `get_actions`.
76    ///
77    /// # Parameters
78    /// - `action`: The index of the action to be played.
79    ///
80    /// # Panics
81    /// This method typically assumes `action` is valid. If `action` is out of bounds
82    /// or invalid for the current state, the behavior is implementation-defined
83    /// and may lead to a panic or incorrect state.
84    ///
85    /// # Examples
86    /// ```rust
87    /// use simple_mcts::Game;
88    /// use simple_mcts::test_utils::GameTest;
89    /// let mut game = GameTest::new();
90    /// assert_eq!(game.get_state(), [-1, -1, -1, -1]);
91    /// game.play(0);
92    /// assert_eq!(game.get_state(), [0, -1, -1, -1]);
93    /// ```
94    fn play(&mut self, action: usize);
95
96    /// Returns an immutable representation of the current game state.
97    ///
98    /// This state is typically used by `GameEvaluator` to assess the game
99    /// without needing to clone the entire `Game` instance.
100    ///
101    /// # Returns
102    /// The current state of the game.
103    ///
104    /// # Examples
105    /// ```rust
106    /// use simple_mcts::Game;
107    /// use simple_mcts::test_utils::GameTest;
108    /// let mut game = GameTest::new();
109    /// let initial_state = game.get_state();
110    /// // State for GameTest is an array [i32; 4] representing moves made.
111    /// assert_eq!(initial_state, [-1, -1, -1, -1]);
112    /// ```
113    fn get_state(&self) -> Self::State;
114
115    /// Returns the final result of the game if it has finished.
116    ///
117    /// The result is typically a value representing the outcome from the perspective
118    /// of the player whose turn it was when the game finished.
119    /// Common values include:
120    /// - `1.0`: Win for the current player.
121    /// - `0.0`: Draw.
122    /// - `-1.0`: Loss for the current player / win for opponent.
123    ///
124    /// # Returns
125    /// An `Option<f64>`:
126    /// - `Some(value)` if the game is finished and a result can be determined.
127    /// - `None` if the game is not yet finished.
128    ///
129    /// # Examples
130    /// ```rust
131    /// use simple_mcts::Game;
132    /// use simple_mcts::test_utils::GameTest;
133    /// let mut game = GameTest::new();
134    /// assert_eq!(game.get_result(), None);
135    /// game.play(0); game.play(1); game.play(2); game.play(3); // Finish the game
136    /// // The exact result depends on GameTest's internal logic.
137    /// // assert_eq!(game.get_result(), Some(...));
138    /// ```
139    fn get_result(&self) -> Option<f64>;
140    
141    /// Creates a deep copy of the current game instance.
142    ///
143    /// This is crucial for MCTS as simulations often require creating independent
144    /// branches from a given game state without affecting the original.
145    ///
146    /// # Returns
147    /// A new, independent instance of the game with the same state.
148    ///
149    /// # Examples
150    /// ```rust
151    /// use simple_mcts::Game;
152    /// use simple_mcts::test_utils::*;
153    /// let original_game = GameTest::new();
154    /// let cloned_game = original_game.clone();
155    /// assert_eq!(original_game.get_state(), cloned_game.get_state());
156    /// ```
157    fn clone(&self) -> Self;
158}
159/// Trait for evaluating game states and providing policy suggestions.
160///
161/// Implementations of this trait are typically used by the MCTS algorithm
162/// during the simulation and expansion phases to assess game states and
163/// determine probabilities for subsequent actions. This trait allows for
164/// plugging in external evaluation models (e.g., neural networks).
165///
166/// # Type Parameters
167/// - `T`: The game type that this evaluator is designed for, implementing the `Game` trait.
168/// - `N`: The number of possible actions in the game, a constant generic parameter.
169
170pub trait GameEvaluator<T: Game<N>, const N: usize>{
171    /// Evaluates a given game state and returns an estimated value and
172    /// a probability distribution over possible actions (policy).
173    ///
174    /// The value estimate typically represents the likelihood of winning from this state,
175    /// often from the perspective of the current player, and commonly in the range `[-1.0, 1.0]`.
176    /// The policy array gives probabilities for each action, where `policy[i]` is the probability
177    /// of taking action `i`. The sum of probabilities for valid actions should ideally be 1.0.
178    ///
179    /// # Parameters
180    /// - `state`: The game state to evaluate. This is typically a `Game::State` type.
181    ///
182    /// # Returns
183    /// A tuple containing:
184    /// - `f64`: The estimated value of the state (e.g., from the current player's perspective).
185    /// - `[f64; N]`: An array of action probabilities (policy), where each element corresponds
186    ///   to the probability of taking that action from the given state.
187    ///
188    /// # Examples
189    /// ```rust
190    /// use simple_mcts::{test_utils::{GameTest, GameEvaluatorTest2}, Game, GameEvaluator};
191    /// let evaluator = GameEvaluatorTest2::new();
192    /// let game = GameTest::new();
193    /// let initial_state = game.get_state();
194    /// let (value, policy) = evaluator.evaluate(initial_state);
195    /// println!("Initial state value: {}, policy: {:?}", value, policy);
196    /// ```
197    fn evaluate(&self, state: T::State) -> (f64, [f64; N]);
198}