aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2023-08-22 20:28:16 +0300
committereug-vs <eugene@eug-vs.xyz>2023-08-22 20:28:16 +0300
commitfb53287ba6c120ced37b9dd44f3c589cfa1455cd (patch)
tree7953421f2120acb86908dbda1643d2def76ecf05
parent50e9cdcd6977c09aa8c2aba1059a3d0dacd80d51 (diff)
downloadchessnost-fb53287ba6c120ced37b9dd44f3c589cfa1455cd.tar.gz
feat: count passers in evaluation
-rw-r--r--src/grossmeister/evaluation.rs141
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 {