aboutsummaryrefslogtreecommitdiff
path: root/src/grossmeister
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2023-09-03 19:29:54 +0300
committereug-vs <eugene@eug-vs.xyz>2023-09-03 19:29:54 +0300
commite2934b95cbad65bc89cbc285c3ece2124bf44a55 (patch)
tree99252dd59ee15cace85730f68db0cd85ea628330 /src/grossmeister
parent9927af55dd12bc5eca7ff17e9dbc2665c42fcfb7 (diff)
downloadchessnost-e2934b95cbad65bc89cbc285c3ece2124bf44a55.tar.gz
feat: improved scoring for (semi)dead positions
Diffstat (limited to 'src/grossmeister')
-rw-r--r--src/grossmeister/evaluation.rs44
-rw-r--r--src/grossmeister/search.rs20
2 files changed, 28 insertions, 36 deletions
diff --git a/src/grossmeister/evaluation.rs b/src/grossmeister/evaluation.rs
index b78c49a..6fc4149 100644
--- a/src/grossmeister/evaluation.rs
+++ b/src/grossmeister/evaluation.rs
@@ -1,3 +1,5 @@
+use std::f32::INFINITY;
+
use crate::{board::{piece::Piece, color::Color, CastlingSide}, bitboard::{Bitboard, BitboardFns}, square::Square};
use super::Grossmeister;
@@ -117,34 +119,6 @@ impl Grossmeister {
}
}
- pub fn is_dead_position(&self) -> bool {
- let non_minor_exists = [
- Piece::Pawn,
- Piece::PawnBlack,
- Piece::Rook,
- Piece::RookBlack,
- Piece::Queen,
- Piece::QueenBlack
- ].iter().any(|&piece| {
- self.board.piece_sets[piece as usize].pop_count() > 0
- });
-
- if non_minor_exists {
- return false;
- }
-
- let minor_pieces = [
- Piece::Knight,
- Piece::KnightBlack,
- Piece::Bishop,
- Piece::BishopBlack
- ].iter().fold(0, |acc, &piece| {
- acc + self.board.piece_sets[piece as usize].pop_count()
- });
-
- minor_pieces <= 1
- }
-
/// Return number of pawns above the king (+left/right)
pub fn pawn_shield(&self, color: Color) -> f32 {
let behind_pawns = match color {
@@ -236,12 +210,16 @@ impl Grossmeister {
/// 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 winnable = self.board.is_theoretically_winnable(color);
+ let loseable = self.board.is_theoretically_winnable(opponent_color);
+
+ if !winnable && !loseable {
+ return 0.0
+ }
+
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);
@@ -265,7 +243,9 @@ impl Grossmeister {
let phase = self.phase();
let tapered_eval = (middlegame_eval * phase as f32 + endgame_eval * (240 - phase) as f32) / 240.;
- material_advantage + tapered_eval
+ (material_advantage + tapered_eval)
+ .min(if winnable { INFINITY } else { 0.0 }) // Can not score > 0 if not winnable
+ .max(if loseable { -INFINITY } else { 0.0 }) // Can not score < 0 if not loseable
}
/// Count pseudo-legal moves without actually generating them
diff --git a/src/grossmeister/search.rs b/src/grossmeister/search.rs
index 2d20e3a..cad2b99 100644
--- a/src/grossmeister/search.rs
+++ b/src/grossmeister/search.rs
@@ -71,16 +71,16 @@ impl Grossmeister {
}
}
- if depth_left == 0 {
- return (self.quiscence(alpha, beta, root_distance), principal_variation);
- }
-
// Mate distance pruning
let mating_score = Grossmeister::MDP(&mut alpha, &mut beta, root_distance);
if mating_score != 0.0 {
return (mating_score, principal_variation)
}
+ if depth_left == 0 {
+ return (self.quiscence(alpha, beta, root_distance), principal_variation);
+ }
+
let mut should_pv_search = true;
let mut legal_move_found = false;
@@ -509,4 +509,16 @@ mod tests {
dbg!(score, pv);
assert_eq!(SCORE_MATE - score, 3.0); // Mate in 3 plies
}
+
+ #[test]
+ fn unwinnable() {
+ let fen = String::from("8/8/2P1k3/1K1n4/8/8/8/8 b - - 0 55");
+ let board = Board::from_FEN(fen);
+
+ let mut gm = Grossmeister::new(board);
+ gm.debug = true;
+ let (score, pv) = gm.iterative_deepening(5);
+ dbg!(score, pv);
+ assert!(score <= 0.0);
+ }
}