Add time limits, use anyhow crate to improve error handling, remove depth limit on quiescence search
131 lines
3.7 KiB
Rust
131 lines
3.7 KiB
Rust
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(())
|
|
}
|
|
}
|