aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2023-08-21 23:32:49 +0300
committereug-vs <eugene@eug-vs.xyz>2023-08-21 23:32:49 +0300
commit67247cb4f2985de052c1edb8b7a831c7df241719 (patch)
tree0ee7019ce123c44a9fb4909b09ccc3c70944b04a
parent2645673ae3e16ee5d7ce24a1efbf367d9fe9a8f0 (diff)
downloadchessnost-67247cb4f2985de052c1edb8b7a831c7df241719.tar.gz
feat: add tapered evaluation
-rw-r--r--src/grossmeister/evaluation.rs113
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);