game_player/
static_evaluator.rs

1//! Static Evaluation Function Module
2//!
3//! This module defines the `StaticEvaluator` trait, which provides an interface for static evaluation functions.
4
5/// An interface for static evaluation functions.
6///
7/// A static evaluation function assigns a value to a game state without any lookahead. The value represents the
8/// "goodness" of the state from Alice's perspective. Alice seeks to maximize the value and Bob seeks to minimize it.
9///
10/// The values returned by the static evaluation function should be in the range [bobWinsValue(), aliceWinsValue()].
11/// If the game is over and Alice has won, then the function should return aliceWinsValue(). If the game is over and
12/// Bob has won, then the function should return bobWinsValue().
13///
14/// # Examples
15///
16/// ```
17/// use game_player::StaticEvaluator;
18///
19/// // Simple game state for demonstration
20/// #[derive(Debug)]
21/// struct MockGameState {
22///     player_score: i32,
23///     opponent_score: i32,
24///     game_over: bool,
25///     winner: Option<bool>, // true = Alice wins, false = Bob wins
26/// }
27///
28/// // Simple static evaluator implementation
29/// struct SimpleEvaluator;
30///
31/// impl StaticEvaluator<MockGameState> for SimpleEvaluator {
32///     fn evaluate(&self, state: &MockGameState) -> f32 {
33///         if state.game_over {
34///             match state.winner {
35///                 Some(true) => self.alice_wins_value(),
36///                 Some(false) => self.bob_wins_value(),
37///                 None => 0.0, // Draw
38///             }
39///         } else {
40///             // Simple score difference evaluation
41///             let diff = state.player_score - state.opponent_score;
42///             (diff as f32) * 0.1 // Scale to reasonable range
43///         }
44///     }
45///
46///     fn alice_wins_value(&self) -> f32 {
47///         1000.0
48///     }
49///
50///     fn bob_wins_value(&self) -> f32 {
51///         -1000.0
52///     }
53/// }
54///
55/// let evaluator = SimpleEvaluator;
56///
57/// // Test ongoing game evaluation
58/// let ongoing_state = MockGameState {
59///     player_score: 15,
60///     opponent_score: 10,
61///     game_over: false,
62///     winner: None,
63/// };
64/// let score = evaluator.evaluate(&ongoing_state);
65/// assert_eq!(score, 0.5); // (15 - 10) * 0.1
66///
67/// // Test Alice wins
68/// let alice_wins_state = MockGameState {
69///     player_score: 21,
70///     opponent_score: 15,
71///     game_over: true,
72///     winner: Some(true),
73/// };
74/// assert_eq!(evaluator.evaluate(&alice_wins_state), 1000.0);
75///
76/// // Test Bob wins
77/// let bob_wins_state = MockGameState {
78///     player_score: 10,
79///     opponent_score: 21,
80///     game_over: true,
81///     winner: Some(false),
82/// };
83/// assert_eq!(evaluator.evaluate(&bob_wins_state), -1000.0);
84///
85/// // Verify win values
86/// assert_eq!(evaluator.alice_wins_value(), 1000.0);
87/// assert_eq!(evaluator.bob_wins_value(), -1000.0);
88/// assert!(evaluator.alice_wins_value() > evaluator.bob_wins_value());
89/// ```
90pub trait StaticEvaluator<G> {
91    /// Evaluates the given state and returns its value from Alice's perspective.
92    ///
93    /// # Arguments
94    /// * `state` - The state to be evaluated
95    ///
96    /// # Returns
97    /// The value of the state from Alice's perspective
98    ///
99    /// # Note
100    /// This function must be implemented.
101    fn evaluate(&self, state: &G) -> f32;
102
103    /// Returns the value that indicates that Alice has won.
104    ///
105    /// # Returns
106    /// The value that indicates that Alice has won
107    ///
108    /// # Note
109    /// This function must be implemented.
110    fn alice_wins_value(&self) -> f32;
111
112    /// Returns the value that indicates that Bob has won.
113    ///
114    /// # Returns
115    /// The value that indicates that Bob has won
116    ///
117    /// # Note
118    /// This function must be implemented.
119    fn bob_wins_value(&self) -> f32;
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    // Mock game state for testing
127    #[derive(Debug, Clone)]
128    struct MockGameState {
129        value: f32,
130        terminal: bool,
131        alice_wins: bool,
132    }
133
134    // Mock static evaluator for testing
135    struct MockEvaluator;
136
137    impl StaticEvaluator<MockGameState> for MockEvaluator {
138        fn evaluate(&self, state: &MockGameState) -> f32 {
139            if state.terminal {
140                if state.alice_wins {
141                    self.alice_wins_value()
142                } else {
143                    self.bob_wins_value()
144                }
145            } else {
146                state.value
147            }
148        }
149
150        fn alice_wins_value(&self) -> f32 {
151            100.0
152        }
153
154        fn bob_wins_value(&self) -> f32 {
155            -100.0
156        }
157    }
158
159    #[test]
160    fn test_static_evaluator_trait() {
161        let evaluator = MockEvaluator;
162
163        // Test ongoing game evaluation
164        let ongoing_state = MockGameState {
165            value: 42.5,
166            terminal: false,
167            alice_wins: false,
168        };
169        assert_eq!(evaluator.evaluate(&ongoing_state), 42.5);
170
171        // Test Alice wins
172        let alice_wins_state = MockGameState {
173            value: 0.0,
174            terminal: true,
175            alice_wins: true,
176        };
177        assert_eq!(evaluator.evaluate(&alice_wins_state), 100.0);
178
179        // Test Bob wins
180        let bob_wins_state = MockGameState {
181            value: 0.0,
182            terminal: true,
183            alice_wins: false,
184        };
185        assert_eq!(evaluator.evaluate(&bob_wins_state), -100.0);
186    }
187
188    #[test]
189    fn test_win_values() {
190        let evaluator = MockEvaluator;
191
192        assert_eq!(evaluator.alice_wins_value(), 100.0);
193        assert_eq!(evaluator.bob_wins_value(), -100.0);
194        assert!(evaluator.alice_wins_value() > evaluator.bob_wins_value());
195    }
196
197    #[test]
198    fn test_evaluation_consistency() {
199        let evaluator = MockEvaluator;
200        let state = MockGameState {
201            value: 25.0,
202            terminal: false,
203            alice_wins: false,
204        };
205
206        // Multiple evaluations should be consistent
207        let result1 = evaluator.evaluate(&state);
208        let result2 = evaluator.evaluate(&state);
209        assert_eq!(result1, result2);
210    }
211
212    #[test]
213    fn test_evaluation_range() {
214        let evaluator = MockEvaluator;
215
216        let alice_wins_state = MockGameState {
217            value: 0.0,
218            terminal: true,
219            alice_wins: true,
220        };
221
222        let bob_wins_state = MockGameState {
223            value: 0.0,
224            terminal: true,
225            alice_wins: false,
226        };
227
228        let alice_score = evaluator.evaluate(&alice_wins_state);
229        let bob_score = evaluator.evaluate(&bob_wins_state);
230
231        // Alice wins should be better than Bob wins from Alice's perspective
232        assert!(alice_score > bob_score);
233
234        // Values should be within expected bounds
235        assert_eq!(alice_score, evaluator.alice_wins_value());
236        assert_eq!(bob_score, evaluator.bob_wins_value());
237    }
238}