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}