use chess::{Board, Color, Piece, Square};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy)]
pub struct PieceValues {
pub pawn: i32,
pub knight: i32,
pub bishop: i32,
pub rook: i32,
pub queen: i32,
pub king: i32, }
impl Default for PieceValues {
fn default() -> Self {
Self {
pawn: 100,
knight: 300,
bishop: 300,
rook: 500,
queen: 900,
king: 0,
}
}
}
pub trait EvaluationComponent {
fn evaluate(&self, board: &Board) -> i32; fn component_name(&self) -> &'static str;
}
#[derive(Debug)]
pub struct MaterialEvaluator {
piece_values: PieceValues,
}
impl MaterialEvaluator {
pub fn new(piece_values: PieceValues) -> Self {
Self { piece_values }
}
fn count_material(&self, board: &Board, color: Color) -> i32 {
let mut material = 0;
for square in chess::ALL_SQUARES {
if let Some(piece) = board.piece_on(square) {
if board.color_on(square) == Some(color) {
material += self.get_piece_value(piece);
}
}
}
material
}
fn get_piece_value(&self, piece: Piece) -> i32 {
match piece {
Piece::Pawn => self.piece_values.pawn,
Piece::Knight => self.piece_values.knight,
Piece::Bishop => self.piece_values.bishop,
Piece::Rook => self.piece_values.rook,
Piece::Queen => self.piece_values.queen,
Piece::King => self.piece_values.king,
}
}
}
impl EvaluationComponent for MaterialEvaluator {
fn evaluate(&self, board: &Board) -> i32 {
let white_material = self.count_material(board, Color::White);
let black_material = self.count_material(board, Color::Black);
let material_diff = white_material - black_material;
if board.side_to_move() == Color::White {
material_diff
} else {
-material_diff
}
}
fn component_name(&self) -> &'static str {
"Material"
}
}
#[derive(Debug)]
pub struct PositionalEvaluator {
center_control_weight: i32,
development_weight: i32,
king_safety_weight: i32,
}
impl PositionalEvaluator {
pub fn new() -> Self {
Self {
center_control_weight: 10, development_weight: 15, king_safety_weight: 20, }
}
fn evaluate_center_control(&self, board: &Board, color: Color) -> i32 {
let center_squares = [
Square::D4, Square::D5, Square::E4, Square::E5
];
let mut control_count = 0;
for &square in ¢er_squares {
if let Some(piece_color) = board.color_on(square) {
if piece_color == color {
control_count += 1;
}
}
}
control_count * self.center_control_weight
}
fn evaluate_development(&self, board: &Board, color: Color) -> i32 {
let mut developed_pieces = 0;
for square in chess::ALL_SQUARES {
if let Some(piece) = board.piece_on(square) {
if board.color_on(square) == Some(color) {
match piece {
Piece::Knight | Piece::Bishop => {
let rank = square.get_rank();
let back_rank = if color == Color::White {
chess::Rank::First
} else {
chess::Rank::Eighth
};
if rank != back_rank {
developed_pieces += 1;
}
}
_ => {}
}
}
}
}
developed_pieces * self.development_weight
}
fn evaluate_king_safety(&self, board: &Board, color: Color) -> i32 {
let castling_rights = board.castle_rights(color);
if castling_rights.has_kingside() || castling_rights.has_queenside() {
0 } else {
let king_square = board.king_square(color);
let expected_king_square = if color == Color::White {
Square::E1
} else {
Square::E8
};
if king_square == expected_king_square {
self.king_safety_weight } else {
0 }
}
}
}
impl EvaluationComponent for PositionalEvaluator {
fn evaluate(&self, board: &Board) -> i32 {
let white_positional = self.evaluate_center_control(board, Color::White)
+ self.evaluate_development(board, Color::White)
+ self.evaluate_king_safety(board, Color::White);
let black_positional = self.evaluate_center_control(board, Color::Black)
+ self.evaluate_development(board, Color::Black)
+ self.evaluate_king_safety(board, Color::Black);
let positional_diff = white_positional - black_positional;
if board.side_to_move() == Color::White {
positional_diff
} else {
-positional_diff
}
}
fn component_name(&self) -> &'static str {
"Positional"
}
}
#[derive(Debug, Clone)]
pub struct CalibrationConfig {
pub material_weight: f32,
pub positional_weight: f32,
pub pattern_weight: f32,
pub scale_factor: f32, }
impl Default for CalibrationConfig {
fn default() -> Self {
Self {
material_weight: 1.0, positional_weight: 0.3, pattern_weight: 0.2, scale_factor: 1.0, }
}
}
pub struct CalibratedEvaluator {
components: Vec<Box<dyn EvaluationComponent>>,
config: CalibrationConfig,
}
impl CalibratedEvaluator {
pub fn new(config: CalibrationConfig) -> Self {
let mut components: Vec<Box<dyn EvaluationComponent>> = Vec::new();
components.push(Box::new(MaterialEvaluator::new(PieceValues::default())));
components.push(Box::new(PositionalEvaluator::new()));
Self { components, config }
}
pub fn add_component(&mut self, component: Box<dyn EvaluationComponent>) {
self.components.push(component);
}
pub fn evaluate_centipawns(&self, board: &Board) -> i32 {
let mut total_evaluation = 0;
for component in &self.components {
let component_eval = component.evaluate(board);
let weighted_eval = match component.component_name() {
"Material" => (component_eval as f32 * self.config.material_weight) as i32,
"Positional" => (component_eval as f32 * self.config.positional_weight) as i32,
_ => component_eval, };
total_evaluation += weighted_eval;
}
(total_evaluation as f32 * self.config.scale_factor) as i32
}
pub fn evaluate_detailed(&self, board: &Board) -> EvaluationBreakdown {
let mut breakdown = EvaluationBreakdown::new();
for component in &self.components {
let component_eval = component.evaluate(board);
breakdown.add_component(component.component_name(), component_eval);
}
breakdown.total = self.evaluate_centipawns(board);
breakdown
}
pub fn calibrate_pattern_evaluation(&self, pattern_eval: f32) -> i32 {
let centipawn_range = 200; let calibrated = pattern_eval * centipawn_range as f32 * self.config.pattern_weight;
calibrated as i32
}
}
#[derive(Debug)]
pub struct EvaluationBreakdown {
pub components: HashMap<String, i32>,
pub total: i32,
}
impl EvaluationBreakdown {
fn new() -> Self {
Self {
components: HashMap::new(),
total: 0,
}
}
fn add_component(&mut self, name: &str, value: i32) {
self.components.insert(name.to_string(), value);
}
pub fn display(&self) -> String {
let mut result = String::new();
result.push_str(&format!("Total: {} cp\n", self.total));
result.push_str("Breakdown:\n");
for (component, value) in &self.components {
result.push_str(&format!(" {}: {} cp\n", component, value));
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_material_evaluator() {
let evaluator = MaterialEvaluator::new(PieceValues::default());
let board = Board::default();
assert_eq!(evaluator.evaluate(&board), 0);
let board = Board::from_str("rnbqkb1r/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
assert_eq!(evaluator.evaluate(&board), 300); }
#[test]
fn test_positional_evaluator() {
let evaluator = PositionalEvaluator::new();
let board = Board::default();
let eval = evaluator.evaluate(&board);
assert_eq!(eval, 0); }
#[test]
fn test_calibrated_evaluator() {
let evaluator = CalibratedEvaluator::new(CalibrationConfig::default());
let board = Board::default();
let eval = evaluator.evaluate_centipawns(&board);
assert!(eval.abs() <= 50);
let breakdown = evaluator.evaluate_detailed(&board);
assert!(breakdown.components.contains_key("Material"));
assert!(breakdown.components.contains_key("Positional"));
}
#[test]
fn test_pattern_calibration() {
let evaluator = CalibratedEvaluator::new(CalibrationConfig::default());
let pattern_eval = 0.5; let calibrated = evaluator.calibrate_pattern_evaluation(pattern_eval);
assert!(calibrated > 0 && calibrated <= 50); }
}