game_player/
state.rs

1//! Game State Module
2//!
3//! This module implements the state components and traits, providing the necessary interface for the game-specific state and
4//! logic.
5
6/// IDs of the players in a two-player game.
7///
8/// This enumeration defines the two possible players. The numeric values (0 and 1) can be used for array indexing and other
9/// performance-critical operations. These are provided for convenience and are not required to be used.
10///
11/// # Examples
12///
13/// ```rust
14/// # use game_player::PlayerId;
15/// let current_player = PlayerId::ALICE;
16/// let player_index = current_player as usize; // 0
17/// ```
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum PlayerId {
20    ALICE = 0,
21    BOB = 1,
22}
23
24impl PlayerId {
25    /// Returns the other player
26    ///
27    /// # Examples
28    ///
29    /// ```rust
30    /// # use game_player::PlayerId;
31    /// assert_eq!(PlayerId::ALICE.other(), PlayerId::BOB);
32    /// assert_eq!(PlayerId::BOB.other(), PlayerId::ALICE);
33    /// ```
34    pub fn other(self) -> Self {
35        match self {
36            PlayerId::ALICE => PlayerId::BOB,
37            PlayerId::BOB => PlayerId::ALICE,
38        }
39    }
40}
41
42/// A trait representing the state.
43///
44/// This trait defines the core interface that a game-specific state must implement.
45///
46/// # Core Concepts
47/// ## Fingerprinting
48/// A state must provide a unique fingerprint (hash) that can be used for:
49/// - Transposition tables in game tree search
50/// - Duplicate position detection
51/// - State caching and memoization
52///
53/// ## Turn Management
54/// The trait tracks which player should move next, enabling:
55/// - Alternating play enforcement
56/// - Player-specific evaluation functions
57/// - Turn-based game logic
58///
59/// ## State Transitions
60/// Game states can store references to expected responses, allowing:
61/// - Pre-computed move sequences
62/// - Principal variation storage
63/// - Game tree navigation
64///
65/// # Examples
66///
67/// ```rust
68/// # use game_player::{State, PlayerId};
69///
70/// #[derive(Clone, Default)]
71/// struct MyAction;
72///
73/// #[derive(Clone, Copy)]
74/// struct MyGameState {
75///     board: [u8; 64],
76///     current_player: PlayerId,
77///     game_over: bool,
78///     // other game-specific fields...
79/// }
80///
81/// impl State for MyGameState {
82///    type Action = MyAction;
83///     fn fingerprint(&self) -> u64 {
84///         // Generate unique hash for this position
85///         // Implementation depends on game specifics
86///         42 // placeholder
87///     }
88///
89///     fn whose_turn(&self) -> u8 {
90///         self.current_player as u8
91///     }
92///
93///     fn is_terminal(&self) -> bool {
94///         self.game_over
95///     }
96///
97///     fn apply(&self, _action: &MyAction) -> Self {
98///         MyGameState {
99///             board: self.board,
100///             current_player: self.current_player.other(),
101///             game_over: false,
102///         }
103///     }
104/// }
105/// ```
106pub trait State: Sized {
107    /// The type representing actions/moves in this game
108    type Action: Clone;
109
110    /// Returns a unique fingerprint (hash) for this state.
111    ///
112    /// The fingerprint must be statistically unique across all possible game states to avoid hash collisions in transposition
113    /// tables and state caches. Identical game positions must always produce identical fingerprints.
114    ///
115    /// # Implementation Requirements
116    /// - **Deterministic**: Same position always produces same fingerprint
117    /// - **Collision-resistant**: Different positions should produce different and uncorrelated fingerprints
118    /// - **Fast**: Called frequently during game tree search
119    /// - **Position-dependent**: Only depends on the current state and independent of move history.
120    ///
121    /// # Returns
122    /// A 64-bit unsigned integer representing the unique fingerprint
123    ///
124    /// # Examples
125    ///
126    /// ```rust
127    /// # use game_player::{State, PlayerId};
128    /// # #[derive(Clone, Default)]
129    /// # struct MyAction;
130    /// # #[derive(Clone, Copy)]
131    /// # struct MyGameState { current_player: PlayerId }
132    /// # impl State for MyGameState {
133    /// #     type Action = MyAction;
134    /// #     fn fingerprint(&self) -> u64 { 42 }
135    /// #     fn whose_turn(&self) -> u8 { self.current_player as u8 }
136    /// #     fn is_terminal(&self) -> bool { false }
137    /// #     fn apply(&self, _action: &Self::Action) -> Self { *self }
138    /// # }
139    /// # fn create_initial_state() -> MyGameState { MyGameState { current_player: PlayerId::ALICE } }
140    /// let state = create_initial_state();
141    /// let fingerprint = state.fingerprint();
142    ///
143    /// // Same position should produce same fingerprint
144    /// let same_state = create_initial_state();
145    /// assert_eq!(fingerprint, same_state.fingerprint());
146    /// ```
147    fn fingerprint(&self) -> u64;
148
149    /// Returns the ID of the player whose turn it is to move.
150    ///
151    /// # Returns
152    /// The id of the player of the player who should move next
153    ///
154    /// # Examples
155    /// ```rust
156    /// # use game_player::{State, PlayerId};
157    /// # #[derive(Clone, Default)]
158    /// # struct MyAction;
159    /// # #[derive(Clone, Copy)]
160    /// # struct MyGameState { current_player: PlayerId }
161    /// # impl State for MyGameState {
162    /// #     type Action = MyAction;
163    /// #     fn fingerprint(&self) -> u64 { 42 }
164    /// #     fn whose_turn(&self) -> u8 { self.current_player as u8 }
165    /// #     fn is_terminal(&self) -> bool { false }
166    /// #     fn apply(&self, _action: &Self::Action) -> Self { *self }
167    /// # }
168    /// let state = MyGameState { current_player: PlayerId::ALICE };
169    /// match state.whose_turn() {
170    ///     0 => println!("Alice to move"), // PlayerId::ALICE as u8
171    ///     1 => println!("Bob to move"),   // PlayerId::BOB as u8
172    ///     _ => unreachable!(),
173    /// }
174    /// ```
175    fn whose_turn(&self) -> u8;
176
177    /// Checks if the game cannot continue.
178    ///
179    /// # Returns
180    /// `true` if the game cannot continue, `false` otherwise.
181    ///
182    /// # Examples
183    /// ```rust
184    /// # use game_player::{State, PlayerId};
185    /// # #[derive(Clone, Default)]
186    /// # struct MyAction;
187    /// # #[derive(Clone, Copy)]
188    /// # struct MyGameState { game_is_over: bool }
189    /// # impl State for MyGameState {
190    /// #     type Action = MyAction;
191    /// #     fn fingerprint(&self) -> u64 { 42 }
192    /// #     fn whose_turn(&self) -> u8 { 0 }
193    /// #     fn is_terminal(&self) -> bool { self.game_is_over }
194    /// #     fn apply(&self, _action: &Self::Action) -> Self { *self }
195    /// # }
196    /// let state = MyGameState { game_is_over: true };
197    /// assert!(state.is_terminal());
198    /// ```
199    fn is_terminal(&self) -> bool;
200
201    /// Applies an action to the current state, returning a new state as a result of the action.
202    ///
203    /// This method creates a new state by applying the given action to the current state.
204    /// The original state remains unchanged (immutable transformation).
205    ///
206    /// # Arguments
207    /// * `action` - The action to apply to the current state
208    ///
209    /// # Returns
210    /// A new state representing the position after applying the action
211    ///
212    /// # Examples
213    /// ```rust
214    /// # use game_player::{State, PlayerId};
215    /// #
216    /// # #[derive(Debug, Clone, Default)]
217    /// # struct MyAction { move_type: String }
218    /// #
219    /// # struct MyGameState {
220    /// #     current_player: PlayerId,
221    /// #     move_count: u32,
222    /// #     game_over: bool
223    /// # }
224    /// #
225    /// # impl State for MyGameState {
226    /// #     type Action = MyAction;
227    /// #     fn fingerprint(&self) -> u64 {
228    /// #         (self.current_player as u64) << 32 | self.move_count as u64
229    /// #     }
230    /// #     fn whose_turn(&self) -> u8 { self.current_player as u8 }
231    /// #     fn is_terminal(&self) -> bool { self.game_over }
232    /// #     fn apply(&self, action: &Self::Action) -> Self {
233    /// #         MyGameState {
234    /// #             current_player: self.current_player.other(),
235    /// #             move_count: self.move_count + 1,
236    /// #             game_over: self.move_count >= 10,
237    /// #         }
238    /// #     }
239    /// # }
240    ///
241    /// let initial_state = MyGameState {
242    ///     current_player: PlayerId::ALICE,
243    ///     move_count: 0,
244    ///     game_over: false
245    /// };
246    /// let action = MyAction { move_type: "play_tile".to_string() };
247    ///
248    /// let new_state = initial_state.apply(&action);
249    ///
250    /// // State should be updated
251    /// assert_eq!(new_state.whose_turn(), PlayerId::BOB as u8);
252    /// assert_ne!(new_state.fingerprint(), initial_state.fingerprint());
253    ///
254    /// // Original state unchanged
255    /// assert_eq!(initial_state.whose_turn(), PlayerId::ALICE as u8);
256    /// ```
257    fn apply(&self, action: &Self::Action) -> Self;
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    #[test]
265    fn test_player_id_other() {
266        assert_eq!(PlayerId::ALICE.other(), PlayerId::BOB);
267        assert_eq!(PlayerId::BOB.other(), PlayerId::ALICE);
268    }
269
270    #[test]
271    fn test_player_id_values() {
272        assert_eq!(PlayerId::ALICE as u8, 0);
273        assert_eq!(PlayerId::BOB as u8, 1);
274    }
275}