1use chess::{Board, Color, Piece, Square};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Copy)]
11pub struct PieceValues {
12 pub pawn: i32,
13 pub knight: i32,
14 pub bishop: i32,
15 pub rook: i32,
16 pub queen: i32,
17 pub king: i32, }
19
20impl Default for PieceValues {
21 fn default() -> Self {
22 Self {
23 pawn: 100,
24 knight: 300,
25 bishop: 300,
26 rook: 500,
27 queen: 900,
28 king: 0,
29 }
30 }
31}
32
33pub trait EvaluationComponent {
35 fn evaluate(&self, board: &Board) -> i32; fn component_name(&self) -> &'static str;
37}
38
39#[derive(Debug)]
41pub struct MaterialEvaluator {
42 piece_values: PieceValues,
43}
44
45impl MaterialEvaluator {
46 pub fn new(piece_values: PieceValues) -> Self {
47 Self { piece_values }
48 }
49
50 fn count_material(&self, board: &Board, color: Color) -> i32 {
51 let mut material = 0;
52
53 for square in chess::ALL_SQUARES {
54 if let Some(piece) = board.piece_on(square) {
55 if board.color_on(square) == Some(color) {
56 material += self.get_piece_value(piece);
57 }
58 }
59 }
60
61 material
62 }
63
64 fn get_piece_value(&self, piece: Piece) -> i32 {
65 match piece {
66 Piece::Pawn => self.piece_values.pawn,
67 Piece::Knight => self.piece_values.knight,
68 Piece::Bishop => self.piece_values.bishop,
69 Piece::Rook => self.piece_values.rook,
70 Piece::Queen => self.piece_values.queen,
71 Piece::King => self.piece_values.king,
72 }
73 }
74}
75
76impl EvaluationComponent for MaterialEvaluator {
77 fn evaluate(&self, board: &Board) -> i32 {
78 let white_material = self.count_material(board, Color::White);
79 let black_material = self.count_material(board, Color::Black);
80
81 let material_diff = white_material - black_material;
82
83 if board.side_to_move() == Color::White {
85 material_diff
86 } else {
87 -material_diff
88 }
89 }
90
91 fn component_name(&self) -> &'static str {
92 "Material"
93 }
94}
95
96#[derive(Debug)]
98pub struct PositionalEvaluator {
99 center_control_weight: i32,
100 development_weight: i32,
101 king_safety_weight: i32,
102}
103
104impl PositionalEvaluator {
105 pub fn new() -> Self {
106 Self {
107 center_control_weight: 10, development_weight: 15, king_safety_weight: 20, }
111 }
112
113 fn evaluate_center_control(&self, board: &Board, color: Color) -> i32 {
114 let center_squares = [
115 Square::D4, Square::D5, Square::E4, Square::E5
116 ];
117
118 let mut control_count = 0;
119
120 for &square in ¢er_squares {
121 if let Some(piece_color) = board.color_on(square) {
122 if piece_color == color {
123 control_count += 1;
124 }
125 }
126 }
127
128 control_count * self.center_control_weight
129 }
130
131 fn evaluate_development(&self, board: &Board, color: Color) -> i32 {
132 let mut developed_pieces = 0;
133
134 for square in chess::ALL_SQUARES {
136 if let Some(piece) = board.piece_on(square) {
137 if board.color_on(square) == Some(color) {
138 match piece {
139 Piece::Knight | Piece::Bishop => {
140 let rank = square.get_rank();
142 let back_rank = if color == Color::White {
143 chess::Rank::First
144 } else {
145 chess::Rank::Eighth
146 };
147
148 if rank != back_rank {
149 developed_pieces += 1;
150 }
151 }
152 _ => {}
153 }
154 }
155 }
156 }
157
158 developed_pieces * self.development_weight
159 }
160
161 fn evaluate_king_safety(&self, board: &Board, color: Color) -> i32 {
162 let castling_rights = board.castle_rights(color);
164
165 if castling_rights.has_kingside() || castling_rights.has_queenside() {
166 0 } else {
168 let king_square = board.king_square(color);
170 let expected_king_square = if color == Color::White {
171 Square::E1
172 } else {
173 Square::E8
174 };
175
176 if king_square == expected_king_square {
177 self.king_safety_weight } else {
179 0 }
181 }
182 }
183}
184
185impl EvaluationComponent for PositionalEvaluator {
186 fn evaluate(&self, board: &Board) -> i32 {
187 let white_positional = self.evaluate_center_control(board, Color::White)
188 + self.evaluate_development(board, Color::White)
189 + self.evaluate_king_safety(board, Color::White);
190
191 let black_positional = self.evaluate_center_control(board, Color::Black)
192 + self.evaluate_development(board, Color::Black)
193 + self.evaluate_king_safety(board, Color::Black);
194
195 let positional_diff = white_positional - black_positional;
196
197 if board.side_to_move() == Color::White {
199 positional_diff
200 } else {
201 -positional_diff
202 }
203 }
204
205 fn component_name(&self) -> &'static str {
206 "Positional"
207 }
208}
209
210#[derive(Debug, Clone)]
212pub struct CalibrationConfig {
213 pub material_weight: f32,
214 pub positional_weight: f32,
215 pub pattern_weight: f32,
216 pub scale_factor: f32, }
218
219impl Default for CalibrationConfig {
220 fn default() -> Self {
221 Self {
222 material_weight: 1.0, positional_weight: 0.3, pattern_weight: 0.2, scale_factor: 1.0, }
227 }
228}
229
230pub struct CalibratedEvaluator {
232 components: Vec<Box<dyn EvaluationComponent>>,
233 config: CalibrationConfig,
234}
235
236impl CalibratedEvaluator {
237 pub fn new(config: CalibrationConfig) -> Self {
238 let mut components: Vec<Box<dyn EvaluationComponent>> = Vec::new();
239
240 components.push(Box::new(MaterialEvaluator::new(PieceValues::default())));
242 components.push(Box::new(PositionalEvaluator::new()));
243
244 Self { components, config }
245 }
246
247 pub fn add_component(&mut self, component: Box<dyn EvaluationComponent>) {
249 self.components.push(component);
250 }
251
252 pub fn evaluate_centipawns(&self, board: &Board) -> i32 {
254 let mut total_evaluation = 0;
255
256 for component in &self.components {
257 let component_eval = component.evaluate(board);
258
259 let weighted_eval = match component.component_name() {
261 "Material" => (component_eval as f32 * self.config.material_weight) as i32,
262 "Positional" => (component_eval as f32 * self.config.positional_weight) as i32,
263 _ => component_eval, };
265
266 total_evaluation += weighted_eval;
267 }
268
269 (total_evaluation as f32 * self.config.scale_factor) as i32
271 }
272
273 pub fn evaluate_detailed(&self, board: &Board) -> EvaluationBreakdown {
275 let mut breakdown = EvaluationBreakdown::new();
276
277 for component in &self.components {
278 let component_eval = component.evaluate(board);
279 breakdown.add_component(component.component_name(), component_eval);
280 }
281
282 breakdown.total = self.evaluate_centipawns(board);
283 breakdown
284 }
285
286 pub fn calibrate_pattern_evaluation(&self, pattern_eval: f32) -> i32 {
288 let centipawn_range = 200; let calibrated = pattern_eval * centipawn_range as f32 * self.config.pattern_weight;
291 calibrated as i32
292 }
293}
294
295#[derive(Debug)]
297pub struct EvaluationBreakdown {
298 pub components: HashMap<String, i32>,
299 pub total: i32,
300}
301
302impl EvaluationBreakdown {
303 fn new() -> Self {
304 Self {
305 components: HashMap::new(),
306 total: 0,
307 }
308 }
309
310 fn add_component(&mut self, name: &str, value: i32) {
311 self.components.insert(name.to_string(), value);
312 }
313
314 pub fn display(&self) -> String {
315 let mut result = String::new();
316 result.push_str(&format!("Total: {} cp\n", self.total));
317 result.push_str("Breakdown:\n");
318
319 for (component, value) in &self.components {
320 result.push_str(&format!(" {}: {} cp\n", component, value));
321 }
322
323 result
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330 use std::str::FromStr;
331
332 #[test]
333 fn test_material_evaluator() {
334 let evaluator = MaterialEvaluator::new(PieceValues::default());
335
336 let board = Board::default();
338 assert_eq!(evaluator.evaluate(&board), 0);
339
340 let board = Board::from_str("rnbqkb1r/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
342 assert_eq!(evaluator.evaluate(&board), 300); }
344
345 #[test]
346 fn test_positional_evaluator() {
347 let evaluator = PositionalEvaluator::new();
348
349 let board = Board::default();
351 let eval = evaluator.evaluate(&board);
352 assert_eq!(eval, 0); }
354
355 #[test]
356 fn test_calibrated_evaluator() {
357 let evaluator = CalibratedEvaluator::new(CalibrationConfig::default());
358
359 let board = Board::default();
361 let eval = evaluator.evaluate_centipawns(&board);
362 assert!(eval.abs() <= 50); let breakdown = evaluator.evaluate_detailed(&board);
366 assert!(breakdown.components.contains_key("Material"));
367 assert!(breakdown.components.contains_key("Positional"));
368 }
369
370 #[test]
371 fn test_pattern_calibration() {
372 let evaluator = CalibratedEvaluator::new(CalibrationConfig::default());
373
374 let pattern_eval = 0.5; let calibrated = evaluator.calibrate_pattern_evaluation(pattern_eval);
377 assert!(calibrated > 0 && calibrated <= 50); }
379}