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}