diff --git a/src/evaluation.rs b/src/evaluation.rs new file mode 100644 index 0000000..8a03298 --- /dev/null +++ b/src/evaluation.rs @@ -0,0 +1,116 @@ +use u64 as Bitboard; + +use crate::{ + bitboard::lsb, + board::{Board, Color, Kind}, + psqt::{mirror_index, piece_square_score}, +}; + +const fn piece_score(kind: Kind) -> i32 { + match kind { + Kind::Pawn => 100, + Kind::Knight => 320, + Kind::Bishop => 330, + Kind::Rook => 500, + Kind::Queen => 900, + Kind::King => 20000, + } +} + +fn evaluate_side(board: &Board, color: Color) -> i32 { + let mut total_score = 0; + let pieces = match color { + Color::White => &board.white_pieces, + Color::Black => &board.black_pieces, + }; + + for piece in pieces { + let (kind, mut bitboard): (Kind, Bitboard) = (piece.kind, 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 += piece_score(kind); + score += piece_square_score(kind, psqt_index); + bitboard &= bitboard - 1; + } + total_score += score; + } + total_score +} + +pub fn evaluate_position(board: &Board, color: Color) -> i32 { + let total_white_score = evaluate_side(board, Color::White); + let total_black_score = evaluate_side(board, Color::Black); + let evaluation = total_white_score - total_black_score; + + match color { + Color::White => evaluation, + Color::Black => -evaluation, + } +} + +#[cfg(test)] +mod tests { + use crate::{ + board::{Color, Kind}, + evaluation::{evaluate_position, evaluate_side, piece_score}, + fen::from_fen, + }; + + 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_piece_score() -> Result<(), String> { + assert_eq!(100, piece_score(Kind::Pawn)); + assert_eq!(320, piece_score(Kind::Knight)); + assert_eq!(330, piece_score(Kind::Bishop)); + assert_eq!(500, piece_score(Kind::Rook)); + assert_eq!(900, piece_score(Kind::Queen)); + assert_eq!(20000, piece_score(Kind::King)); + + Ok(()) + } + + #[test] + fn test_evaluate_side() -> Result<(), String> { + let game = from_fen(FEN_QUIET[0])?; + + assert_eq!( + evaluate_side(&game.board, Color::White), + evaluate_side(&game.board, Color::Black) + ); + + let game_2 = from_fen(FEN_QUIET[1])?; + let evaluate_white = evaluate_side(&game_2.board, Color::White); + let evaluate_black = evaluate_side(&game_2.board, Color::Black); + + assert_eq!(24005, evaluate_white); + assert_eq!(23965, evaluate_black); + + Ok(()) + } + + #[test] + fn test_evaluate() -> Result<(), String> { + let game = from_fen(FEN_QUIET[0])?; + + assert_eq!( + evaluate_position(&game.board, Color::White), + evaluate_position(&game.board, Color::Black) + ); + + let game_2 = from_fen(FEN_QUIET[1])?; + + assert_eq!(40, evaluate_position(&game_2.board, Color::White)); + assert_eq!(-40, evaluate_position(&game_2.board, Color::Black)); + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index d92fa04..9cf8484 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,14 @@ pub mod attack; pub mod bitboard; pub mod board; +pub mod evaluation; pub mod fen; pub mod game; pub mod magic; pub mod r#move; pub mod movegen; pub mod perft; +pub mod psqt; pub mod search; pub mod square; pub mod state; diff --git a/src/psqt.rs b/src/psqt.rs new file mode 100644 index 0000000..1eac735 --- /dev/null +++ b/src/psqt.rs @@ -0,0 +1,143 @@ +use crate::board::Kind; + +pub const fn piece_square_score(kind: Kind, index: usize) -> i32 { + match kind { + Kind::Pawn => PAWN_PSQT[index], + Kind::Knight => KNIGHT_PSQT[index], + Kind::Bishop => BISHOP_PSQT[index], + Kind::Rook => ROOK_PSQT[index], + Kind::Queen => QUEEN_PSQT[index], + Kind::King => KING_MIDGAME_PSQT[index], + } +} + +pub const fn piece_square_score_endgame(kind: Kind, index: usize) -> i32 { + match kind { + Kind::Pawn => PAWN_PSQT[index], + Kind::Knight => KNIGHT_PSQT[index], + Kind::Bishop => BISHOP_PSQT[index], + Kind::Rook => ROOK_PSQT[index], + Kind::Queen => QUEEN_PSQT[index], + Kind::King => KING_ENDGAME_PSQT[index], + } +} + +pub const fn mirror_index(idx: usize) -> usize { + 63 - idx +} + +#[rustfmt::skip] +const PAWN_PSQT: [i32; 64] = [ + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 10, 10,-20,-20, 10, 10, 5, + 5, -5,-10, 0, 0,-10, -5, 5, + 0, 0, 0, 20, 20, 0, 0, 0, + 5, 5, 10, 25, 25, 10, 5, 5, + 10, 10, 20, 30, 30, 20, 10, 10, + 50, 50, 50, 50, 50, 50, 50, 50, + 0, 0, 0, 0, 0, 0, 0, 0 +]; + +#[rustfmt::skip] +const KNIGHT_PSQT: [i32; 64] = [ + -50,-40,-30,-30,-30,-30,-40,-50, + -40,-20, 0, 5, 5, 0,-20,-40, + -30, 5, 10, 15, 15, 10, 5,-30, + -30, 0, 15, 20, 20, 15, 0,-30, + -30, 5, 15, 20, 20, 15, 5,-30, + -30, 0, 10, 15, 15, 10, 0,-30, + -40,-20, 0, 0, 0, 0,-20,-40, + -50,-40,-30,-30,-30,-30,-40,-50, +]; + +#[rustfmt::skip] +const BISHOP_PSQT: [i32; 64] = [ + -20,-10,-10,-10,-10,-10,-10,-20, + -10, 5, 0, 0, 0, 0, 5,-10, + -10, 10, 10, 10, 10, 10, 10,-10, + -10, 0, 10, 10, 10, 10, 0,-10, + -10, 5, 5, 10, 10, 5, 5,-10, + -10, 0, 5, 10, 10, 5, 0,-10, + -10, 0, 0, 0, 0, 0, 0,-10, + -20,-10,-10,-10,-10,-10,-10,-20, +]; + +#[rustfmt::skip] +const ROOK_PSQT: [i32; 64] = [ + 0, 0, 0, 5, 5, 0, 0, 0, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + 5, 10, 10, 10, 10, 10, 10, 5, + 0, 0, 0, 0, 0, 0, 0, 0 +]; + +#[rustfmt::skip] +const QUEEN_PSQT: [i32; 64] = [ + -20,-10,-10, -5, -5,-10,-10,-20, + -10, 0, 5, 0, 0, 0, 0,-10, + -10, 5, 5, 5, 5, 5, 0,-10, + -5, 0, 5, 5, 5, 5, 0, -5, + 0, 0, 5, 5, 5, 5, 0, -5, + -10, 0, 5, 5, 5, 5, 0,-10, + -10, 0, 0, 0, 0, 0, 0,-10, + -20,-10,-10, -5, -5,-10,-10,-20 +]; + +#[rustfmt::skip] +const KING_MIDGAME_PSQT: [i32; 64] = [ + 20, 30, 10, 0, 0, 10, 30, 20, + 20, 20, 0, 0, 0, 0, 20, 20, + -10,-20,-20,-20,-20,-20,-20,-10, + -20,-30,-30,-40,-40,-30,-30,-20, + -30,-40,-40,-50,-50,-40,-40,-30, + -30,-40,-40,-50,-50,-40,-40,-30, + -30,-40,-40,-50,-50,-40,-40,-30, + -30,-40,-40,-50,-50,-40,-40,-30 +]; + +#[rustfmt::skip] +const KING_ENDGAME_PSQT: [i32; 64] = [ + -50,-30,-30,-30,-30,-30,-30,-50, + -30,-30, 0, 0, 0, 0,-30,-30, + -30,-10, 20, 30, 30, 20,-10,-30, + -30,-10, 30, 40, 40, 30,-10,-30, + -30,-10, 30, 40, 40, 30,-10,-30, + -30,-10, 20, 30, 30, 20,-10,-30, + -30,-20,-10, 0, 0,-10,-20,-30, + -50,-40,-30,-20,-20,-30,-40,-50 +]; + +#[cfg(test)] +mod tests { + use crate::{ + board::Kind, + psqt::{mirror_index, piece_square_score}, + square::Square, + }; + + #[test] + fn test_piece_square_score() -> Result<(), String> { + assert_eq!(50, piece_square_score(Kind::Pawn, Square::A7)); + assert_eq!(-40, piece_square_score(Kind::Knight, Square::B1)); + assert_eq!(0, piece_square_score(Kind::Bishop, Square::D2)); + assert_eq!(-5, piece_square_score(Kind::Rook, Square::A2)); + assert_eq!(5, piece_square_score(Kind::Queen, Square::D3)); + assert_eq!(30, piece_square_score(Kind::King, Square::G1)); + + Ok(()) + } + + #[test] + fn test_mirror_index() -> Result<(), String> { + let a1_mirror = mirror_index(Square::A1); + assert_eq!(Square::H8, a1_mirror); + + let d4_mirror = mirror_index(Square::D4); + assert_eq!(Square::E5, d4_mirror); + + Ok(()) + } +}