use u64 as Bitboard; use crate::{ board::{ bitboard::{self, lsb}, board::{Board, Color, PieceType}, }, evaluation::psqt::{mirror_index, piece_square_score}, }; use super::psqt::piece_square_score_endgame; pub const fn material_score(piece_type: PieceType) -> i32 { match piece_type { PieceType::Pawn => 100, PieceType::Knight => 320, PieceType::Bishop => 330, PieceType::Rook => 500, PieceType::Queen => 900, PieceType::King => 0, } } fn is_end_game(board: &Board) -> bool { let white_pieces = board.white_pieces.iter().fold(0, |acc, p| { acc + bitboard::bit_count(p.bitboard) * material_score(p.piece_type) as usize }); let black_pieces = board.black_pieces.iter().fold(0, |acc, p| { acc + bitboard::bit_count(p.bitboard) * material_score(p.piece_type) as usize }); (white_pieces + black_pieces) < 2000 } fn evaluate_side_for(board: &Board, color: Color) -> i32 { let mut total_score = 0; let pieces = match color { Color::White => &board.white_pieces, Color::Black => &board.black_pieces, }; let psqt = if is_end_game(board) { piece_square_score_endgame } else { piece_square_score }; for piece in pieces { let (piece_type, mut bitboard): (PieceType, Bitboard) = (piece.piece_type, piece.bitboard); let mut score = 0; while bitboard != 0 { let psqt_index = match color { Color::White => lsb(bitboard), Color::Black => mirror_index(lsb(bitboard)), }; score += material_score(piece_type); score += psqt(piece_type, psqt_index); bitboard &= bitboard - 1; } total_score += score; } total_score } pub fn evaluate_position(board: &Board) -> i32 { let total_white_score = evaluate_side_for(board, Color::White); let total_black_score = evaluate_side_for(board, Color::Black); match board.state.current_player() { Color::White => total_white_score - total_black_score, Color::Black => total_black_score - total_white_score, } } #[cfg(test)] mod tests { use crate::{ board::{ board::{Color, PieceType}, fen::from_fen, }, evaluation::evaluation::{evaluate_position, evaluate_side_for, material_score}, }; const FEN_QUIET: [&str; 2] = [ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "rnbqkbnr/ppp2ppp/4p3/3pN3/3P4/8/PPP1PPPP/RNBQKB1R w KQkq - 0 1", ]; #[test] fn test_material_score() -> Result<(), String> { assert_eq!(100, material_score(PieceType::Pawn)); assert_eq!(320, material_score(PieceType::Knight)); assert_eq!(330, material_score(PieceType::Bishop)); assert_eq!(500, material_score(PieceType::Rook)); assert_eq!(900, material_score(PieceType::Queen)); assert_eq!(0, material_score(PieceType::King)); Ok(()) } #[test] fn test_evaluate_side_for() -> Result<(), String> { let game = from_fen(FEN_QUIET[0])?; assert_eq!( evaluate_side_for(&game.board, Color::White), evaluate_side_for(&game.board, Color::Black) ); let game_2 = from_fen(FEN_QUIET[1])?; let evaluate_white = evaluate_side_for(&game_2.board, Color::White); let evaluate_black = evaluate_side_for(&game_2.board, Color::Black); assert_eq!(4005, evaluate_white); assert_eq!(3965, evaluate_black); Ok(()) } #[test] fn test_evaluate_position() -> Result<(), String> { let game = from_fen(FEN_QUIET[1])?; assert_eq!(40, evaluate_position(&game.board)); Ok(()) } }