diff options
author | eug-vs <eugene@eug-vs.xyz> | 2023-08-21 23:32:49 +0300 |
---|---|---|
committer | eug-vs <eugene@eug-vs.xyz> | 2023-08-21 23:32:49 +0300 |
commit | 67247cb4f2985de052c1edb8b7a831c7df241719 (patch) | |
tree | 0ee7019ce123c44a9fb4909b09ccc3c70944b04a | |
parent | 2645673ae3e16ee5d7ce24a1efbf367d9fe9a8f0 (diff) | |
download | chessnost-67247cb4f2985de052c1edb8b7a831c7df241719.tar.gz |
feat: add tapered evaluation
-rw-r--r-- | src/grossmeister/evaluation.rs | 113 |
1 files changed, 73 insertions, 40 deletions
diff --git a/src/grossmeister/evaluation.rs b/src/grossmeister/evaluation.rs index 4512dcc..9215420 100644 --- a/src/grossmeister/evaluation.rs +++ b/src/grossmeister/evaluation.rs @@ -153,33 +153,92 @@ impl Grossmeister { minor_pieces <= 1 } - /// Evaluate a position relative to the current player - pub fn evaluate(&self) -> f32 { - if self.is_dead_position() { - return 0.0 - } - + pub fn evaluate_endgame(&self) -> f32 { let color = self.board.color(); let opponent_color = color.flip(); - let mobility_advantage = self.mobility(color) - self.mobility(opponent_color); + let opponent_material = self.material(opponent_color, true); - let opponent_material = self.material(opponent_color); - let material_advantage = self.material(color) - opponent_material; + self.material(color, true) - opponent_material + } - let pawn_structure_penalty = self.pawn_structure_penalty(color) - self.pawn_structure_penalty(opponent_color); + /// Return number of pawns above the king (+left/right) + pub fn pawn_shield(&self, color: Color) -> f32 { + let behind_pawns = match color { + Color::White => self.board.piece_sets[Piece::Pawn as usize].sout_one(), + Color::Black => self.board.piece_sets[Piece::PawnBlack as usize].nort_one(), + }; + + let king = match color { + Color::White => self.board.piece_sets[Piece::King as usize], + Color::Black => self.board.piece_sets[Piece::KingBlack as usize], + }; + + (behind_pawns & (king | king.west_one() | king.east_one())).pop_count() as f32 + } + + pub fn evaluate_middlegame(&self) -> f32 { + let color = self.board.color(); + let opponent_color = color.flip(); + + let mobility_advantage = self.mobility(color) - self.mobility(opponent_color); + let opponent_material = self.material(opponent_color, false); + let material_advantage = self.material(color, false) - opponent_material; let king_tropism_penalty = self.king_tropism(color) - self.king_tropism(opponent_color); material_advantage + 0.15 * mobility_advantage - - 0.3 * pawn_structure_penalty + king_tropism_penalty * (opponent_material / 40.0) * 0.07 } + // Returns a value in [0, 240] representing how + // much material is left in the game + // Note: not related to actual material counting + pub fn phase(&self) -> u8 { + let knight_phase = 10; + let bishop_phase = 10; + let rook_phase = 20; + let queen_phase = 40; + + let total_phase = knight_phase*4 + bishop_phase*4 + rook_phase*4 + queen_phase*2; + // If you change it, make sure to update denominator in interpolation + debug_assert_eq!(total_phase, 240); + + self.board.piece_sets.iter().enumerate().fold(0, |acc, (piece_index, &bitboard)| { + acc + match Piece::from(piece_index).without_color() { + Piece::King => 0, + Piece::Pawn => 0, + Piece::Knight => knight_phase, + Piece::Bishop => bishop_phase, + Piece::Rook => rook_phase, + Piece::Queen => queen_phase, + _ => panic!("Unreachable") + } * bitboard.pop_count() + }) + } + + /// Evaluate a position relative to the current player + pub fn evaluate(&self) -> f32 { + if self.is_dead_position() { + return 0.0 + } + let color = self.board.color(); + let opponent_color = color.flip(); + + let pawn_structure_penalty = self.pawn_structure_penalty(color) - self.pawn_structure_penalty(opponent_color); + + let middlegame_eval = self.evaluate_middlegame(); + let endgame_eval = self.evaluate_endgame(); + + let phase = self.phase(); + let tapered_eval = (middlegame_eval * phase as f32 + endgame_eval * (240 - phase) as f32) / 240.; + + tapered_eval - 0.3 * pawn_structure_penalty + } + /// Count player pieces' material, giving bonus for pieces standing well - pub fn material(&self, color: Color) -> f32 { - let is_endgame = self.is_endgame(); + pub fn material(&self, color: Color, is_endgame: bool) -> f32 { let passer_mask = self.passer_mask(color); let mut material = 0f32; @@ -430,7 +489,7 @@ mod tests { fn material() { let board = Board::new(); let gm = Grossmeister::new(board); - assert_eq!(gm.material(Color::Black), gm.material(Color::White)); + assert_eq!(gm.material(Color::Black, false), gm.material(Color::White, false)); } @@ -555,32 +614,6 @@ mod tests { } #[test] - fn not_endgame() { - let board = Board::new(); - let gm = Grossmeister::new(board); - let is_endgame = gm.is_endgame(); - assert!(!is_endgame); - } - - #[test] - fn is_endgame() { - let fen = String::from("8/1p6/p4k1K/6r1/8/8/8/8 w - - 10 49"); - let board = Board::from_FEN(fen); - let gm = Grossmeister::new(board); - let is_endgame = gm.is_endgame(); - assert!(is_endgame); - } - - #[test] - fn is_endgame_no_queens() { - let fen = String::from("rnb1kbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB1KBNR w KQkq - 0 1"); - let board = Board::from_FEN(fen); - let gm = Grossmeister::new(board); - let is_endgame = gm.is_endgame(); - assert!(is_endgame); - } - - #[test] fn passer() { let fen = String::from("rnbqkbnr/ppp3pp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); let board = Board::from_FEN(fen); |