Explorar o código

eval and search tuning

Nicolas Winkler %!s(int64=2) %!d(string=hai) anos
pai
achega
dea0bdf988
Modificáronse 3 ficheiros con 75 adicións e 39 borrados
  1. 1 2
      src/evaluate.rs
  2. 12 4
      src/movegen.rs
  3. 62 33
      src/search.rs

+ 1 - 2
src/evaluate.rs

@@ -223,8 +223,7 @@ pub fn evaluate(game: &Board) -> PosValue {
 
     let ge = (mge * (100 - lateness) + lge * lateness) / 100;
 
-
-    return mat_val + ge;
+    return mat_val + ge + 10 /* turn bonus */;
 }
 
 pub fn game_lateness(game: &Board) -> i32 {

+ 12 - 4
src/movegen.rs

@@ -169,6 +169,13 @@ impl Move {
         }
     }
 
+    pub fn is_promotion(&self) -> bool {
+        match self {
+            Move::Promotion { mov: _, pc: _ } => true,
+            _ => false
+        }
+    }
+
     pub fn to_simple(&self) -> SimpleMove {
         match self {
             Move::Default{ mov, pc: _ } |
@@ -194,6 +201,7 @@ impl Move {
             },
         }
     }
+
 }
 
 /**
@@ -356,15 +364,15 @@ impl Iterator for MoveGenerator {
 pub fn generate_moves(game: &Board, side: Side) -> Vec<Move> {
 
     let mut moves: Vec<Move> = Vec::with_capacity(128);
-    generate_pawn_pushes(game, side, &mut moves);
-    generate_pawn_doublepushes(game, side, &mut moves);
-    generate_pawn_captures(game, side, &mut moves);
     generate_promotions(game, side, &mut moves);
+    generate_pawn_captures(game, side, &mut moves);
     generate_en_passant(game, side, &mut moves);
+    generate_pawn_pushes(game, side, &mut moves);
+    generate_pawn_doublepushes(game, side, &mut moves);
     generate_knight_moves(game, side, &mut moves, false);
+    generate_queen_moves(game, side, &mut moves, false);
     generate_bishop_moves(game, side, &mut moves, false);
     generate_rook_moves(game, side, &mut moves, false);
-    generate_queen_moves(game, side, &mut moves, false);
     generate_king_moves(game, side, &mut moves, false);
     generate_castling_moves(game, side, &mut moves);
     return moves;

+ 62 - 33
src/search.rs

@@ -51,6 +51,7 @@ pub struct SearchControl {
 
 
 
+#[derive(Debug)]
 pub enum SearchResult {
     Finished(Move, PosValue),
     NoMove(PosValue),
@@ -151,13 +152,52 @@ impl SearchControl {
             return true;
         }
 
+        if self.stopping {
+            return true;
+        }
+
         return false;
     }
 
-    pub fn iterative_deepening(&mut self, max_depth: Option<i32>) -> bool {
-        let mut alpha = MIN_VALUE;
-        let mut beta = MAX_VALUE;
+    fn windowed_search(&mut self, depth: i32, expected_value: PosValue, initial_window_size: i32) -> SearchResult {
+        let mut alpha_coeff = 1;
+        let mut beta_coeff = 1;
+        let mut alpha = expected_value - initial_window_size * alpha_coeff;
+        let mut beta = expected_value + initial_window_size * beta_coeff;
+
+        let mut result = search(self.board.clone(), self, self.hash.clone(), alpha, beta, depth);
+
+        loop {
+            if let SearchResult::Finished(_mov, val) = result {
+                if val > alpha && val < beta {
+                    // successful search within bounds
+                    break;
+                }
+                if val <= alpha {
+                    alpha_coeff *= 3;
+                    alpha = expected_value - initial_window_size * alpha_coeff;
+                    result = search(self.board.clone(), self, self.hash.clone(), alpha, beta, depth);
+                }
+                if val >= beta {
+                    beta_coeff *= 3;
+                    beta = expected_value + initial_window_size * beta_coeff;
+                    result = search(self.board.clone(), self, self.hash.clone(), alpha, beta, depth);
+                }
+            }
+            else if let SearchResult::Invalid = result {
+                alpha = MIN_VALUE; beta = MAX_VALUE;
+                result = search(self.board.clone(), self, self.hash.clone(), alpha, beta, depth);
+            }
+            else {
+                break;
+            }
+        }
+
 
+        return result;
+    }
+
+    pub fn iterative_deepening(&mut self, max_depth: Option<i32>) -> bool {
         let mut best_move: Option<Move> = None;
         let mut best_val: Option<PosValue> = None;
 
@@ -165,40 +205,15 @@ impl SearchControl {
         self.halfmove_age = self.board.ply_number() as _;
 
         for depth in 1.. {
-            let board = self.board.clone();
-            let hash = self.hash.clone();
 
             self.set_depth(depth);
 
             let result =
                 if let Some(bv) = best_val {
-                    const WINDOW_RADIUS: PosValue = 30;
-                    alpha = bv - WINDOW_RADIUS;
-                    beta = bv + WINDOW_RADIUS;
-
-                    let mut result = search(board, self, hash, alpha, beta, depth);
-                    loop {
-                        if let SearchResult::Finished(_mov, val) = result {
-                            if val <= alpha || val >= beta {
-                                alpha = MIN_VALUE; beta = MAX_VALUE;
-                                result = search(self.board.clone(), self, self.hash.clone(), alpha, beta, depth)
-                            }
-                            else {
-                                break;
-                            }
-                        }
-                        else if let SearchResult::Invalid = result {
-                            alpha = MIN_VALUE; beta = MAX_VALUE;
-                            result = search(self.board.clone(), self, self.hash.clone(), alpha, beta, depth)
-                        }
-                        else {
-                            break;
-                        }
-                    }
-                    result
+                    const WINDOW_RADIUS: PosValue = 10;
+                    self.windowed_search(depth, bv, WINDOW_RADIUS)
                 } else {
-                    alpha = MIN_VALUE; beta = MAX_VALUE;
-                    search(board, self, hash, alpha, beta, depth)
+                    search(self.board.clone(), self, self.hash.clone(), MIN_VALUE, MAX_VALUE, depth)
                 };
 
 
@@ -336,7 +351,12 @@ pub fn search(mut board: Board, sc: &mut SearchControl, hashs: Arc<RwLock<Cache>
         }
     }
     else {
-        return SearchResult::Invalid;
+        if cancelled {
+            return SearchResult::Cancelled(None);
+        }
+        else {
+            return SearchResult::Invalid;
+        }
     }
 }
 
@@ -429,6 +449,15 @@ pub fn negamax(game: &mut Board, sc: &mut SearchControl, hash: &mut Cache, mut a
         }
     }
 
+    // futility pruning
+    if false && depth == 1 && !check && game_lateness(game) < 60 {
+        const FUTILITY_THRESHOLD: PosValue = 240;
+        let curr_eval = quiescence_search(game, sc, hash, alpha, beta, 9);
+        if curr_eval + FUTILITY_THRESHOLD <= alpha {
+            return alpha;
+        }
+    }
+
     let mut best_move: Option<Move> = None;
 
     while let Some(mov) = moves.next() {
@@ -529,7 +558,7 @@ fn quiescence_search(board: &mut Board, sc: &mut SearchControl, hash: &mut Cache
             ];
             const SAFETY_MARGIN: i32 = 200;
             let target_sq = mov.to_simple().to;
-            if let Some((piece_type, side)) = board.get_square(target_sq) {
+            if let Some((piece_type, _side)) = board.get_square(target_sq) {
                 if val + PIECE_VALUES[piece_type as usize] + SAFETY_MARGIN < alpha {
                     continue;
                 }