diff options
author | eug-vs <eugene@eug-vs.xyz> | 2023-08-22 20:28:16 +0300 |
---|---|---|
committer | eug-vs <eugene@eug-vs.xyz> | 2023-08-22 20:28:16 +0300 |
commit | fb53287ba6c120ced37b9dd44f3c589cfa1455cd (patch) | |
tree | 7953421f2120acb86908dbda1643d2def76ecf05 | |
parent | 50e9cdcd6977c09aa8c2aba1059a3d0dacd80d51 (diff) | |
download | chessnost-fb53287ba6c120ced37b9dd44f3c589cfa1455cd.tar.gz |
feat: count passers in evaluation
-rw-r--r-- | src/grossmeister/evaluation.rs | 141 |
1 files changed, 45 insertions, 96 deletions
diff --git a/src/grossmeister/evaluation.rs b/src/grossmeister/evaluation.rs index 29c1128..dafc33a 100644 --- a/src/grossmeister/evaluation.rs +++ b/src/grossmeister/evaluation.rs @@ -101,11 +101,11 @@ const KING_BONUS_ENGAME: [f32; 64] = [ ]; impl Grossmeister { - pub fn passer_mask(&self, color: Color) -> Bitboard { + pub fn passer_count(&self, color: Color) -> f32 { let black_pawns = self.board.piece_sets[Piece::PawnBlack as usize]; let white_pawns = self.board.piece_sets[Piece::Pawn as usize]; - match color { + let mask = match color { Color::Black => { let mut front_fill = white_pawns.nort_fill().nort_one(); front_fill |= front_fill.east_one() | front_fill.west_one(); @@ -116,7 +116,12 @@ impl Grossmeister { front_fill |= front_fill.east_one() | front_fill.west_one(); white_pawns & !front_fill } - } + }; + + (mask & match color { + Color::White => white_pawns, + Color::Black => black_pawns, + }).pop_count() as f32 } pub fn is_dead_position(&self) -> bool { @@ -171,32 +176,6 @@ impl Grossmeister { islands_west_files.pop_count() as f32 } - // 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() - }) - } - /// Compute total bonus for well-positioned pieces /// according to Piece-Square Tables pub fn pst_bonus(&self, color: Color, is_endgame: bool) -> f32 { @@ -228,6 +207,40 @@ impl Grossmeister { }) } + /// Count raw material of the given color + pub fn material(&self, color: Color) -> f32 { + self.board.pieces_by_color(color).iter().enumerate().fold(0., |acc, (piece_index, bitboard)| { + acc + Piece::from(piece_index).static_eval() * bitboard.pop_count() as f32 + }) + } + + // 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() { @@ -239,18 +252,21 @@ impl Grossmeister { let material_advantage = self.material(color) - self.material(opponent_color); let mobility_advantage = self.mobility(color) - self.mobility(opponent_color); let pawn_shield_advantage = self.pawn_shield(color) - self.pawn_shield(opponent_color); + let passer_advantage = self.passer_count(color) - self.passer_count(opponent_color); let pawn_islands_advantage = self.pawn_islands(opponent_color) - self.pawn_islands(color); let middlegame_eval = mobility_advantage * 0.05 + pawn_shield_advantage * 0.15 + pawn_islands_advantage * 0.10 + + passer_advantage * 0.05 + (self.pst_bonus(color, false) - self.pst_bonus(opponent_color, false)); let endgame_eval = mobility_advantage * 0.03 + pawn_shield_advantage * 0.20 + pawn_islands_advantage * 0.15 + + passer_advantage * 0.10 + (self.pst_bonus(color, true) - self.pst_bonus(opponent_color, true)); let phase = self.phase(); @@ -259,73 +275,6 @@ impl Grossmeister { material_advantage + tapered_eval } - /// Count raw material of the given color - pub fn material(&self, color: Color) -> f32 { - self.board.pieces_by_color(color).iter().enumerate().fold(0., |acc, (piece_index, bitboard)| { - acc + Piece::from(piece_index).static_eval() * bitboard.pop_count() as f32 - }) - } - - /// Returns sum of the doubled, blocked and isolated pawns - /// The greater result is, the worse is the pawn structure - pub fn pawn_structure_penalty(&self, color: Color) -> f32 { - let mut result = 0.0; - - let pawns = match color { - Color::White => self.board.piece_sets[Piece::Pawn as usize], - Color::Black => self.board.piece_sets[Piece::PawnBlack as usize], - }; - - for file in 0..8 { - let file_mask = A_FILE << file; - let pawns_on_file = (pawns & file_mask).pop_count() as f32; - - // Doubled pawns (-1 because one pawn on a file is ok) - result += (pawns_on_file - 1.).max(0.0); - - // Isolated pawns (no pawns on neighbor files) - if [ - A_FILE << (file - 1).max(0), // File to the left (if any) - A_FILE << (file + 1).min(7), // File to the right (if any) - ].iter().all(|file| file & pawns == 0) { - result += pawns_on_file; - } - } - - // Blocked pawns - let blocked_mask = match color { - Color::White => self.board.occupancy >> 8, - Color::Black => self.board.occupancy << 8, - }; - result += (pawns & blocked_mask).pop_count() as f32; - - result - } - - /// Returns the weighted sum of distances from attacking pieces to a king - /// The higher this value, the safer is the king - pub fn king_tropism(&self, color: Color) -> f32 { - let mut result = 0.0; - - let king_square = match color { - Color::White => self.board.piece_sets[Piece::King as usize], - Color::Black => self.board.piece_sets[Piece::KingBlack as usize], - }.bitscan(); - - for (piece_type, bitboard) in self.board.pieces_by_color(color.flip()).iter().enumerate() { - if piece_type != Piece::King as usize && piece_type != Piece::Pawn as usize { - for square in bitboard.serialize() { - let distance = - (king_square.rank() as f32 - square.rank() as f32).abs() + - (king_square.file() as f32 - square.file() as f32).abs(); - - result += distance / Piece::from(piece_type).static_eval(); - } - } - } - result - } - /// Count pseudo-legal moves without actually generating them /// Also exclude all moves that put a piece under attack of a pawn - so called safe mobility pub fn mobility(&self, color: Color) -> f32 { |