Просмотр исходного кода

Merge branch 'master' of http://git.winfor.ch/nicolas/bishop

Nicolas Winkler 2 лет назад
Родитель
Сommit
31005e7daa
5 измененных файлов с 84 добавлено и 18 удалено
  1. 1 2
      src/engine.rs
  2. 4 2
      src/evaluate.rs
  3. 1 1
      src/hash.rs
  4. 52 0
      src/movegen.rs
  5. 26 13
      src/search.rs

+ 1 - 2
src/engine.rs

@@ -230,8 +230,7 @@ impl Engine {
         //let window_size = 100;
         loop {
 
-            sc.initial_depth = depth;
-            sc.pv = vec![Move::Nullmove; depth as _];
+            sc.set_depth(depth);
 
             let search_result = search(&mut self.game, &mut sc, &mut self.hash, alpha, beta, depth as i32);
 

+ 4 - 2
src/evaluate.rs

@@ -113,7 +113,7 @@ fn knight_value(game: &Game, side: Side) -> PosValue {
 
 fn material_value(game: &Game, side: Side) -> PosValue {
     (game.get_piece(PAWN, side).count_ones() * 100
-        + game.get_piece(KNIGHT, side).count_ones() * 280
+        + game.get_piece(KNIGHT, side).count_ones() * 220
         + game.get_piece(BISHOP, side).count_ones() * 320
         + game.get_piece(ROOK, side).count_ones() * 400
         + game.get_piece(QUEEN, side).count_ones() * 700) as PosValue
@@ -156,7 +156,9 @@ pub fn evaluate(game: &Game) -> PosValue {
     let sv = side_value(game, game.turn) as PosValue - side_value(game, !game.turn) as PosValue;
     let material_value_us = material_value(game, game.turn);
     let material_value_them = material_value(game, !game.turn);
-    let mat_val = ((material_value_us as f32).powf(0.995f32) - (material_value_them as f32).powf(0.995f32)) as PosValue;
+
+    let mat_val = (material_value_us) - (material_value_them) as PosValue;
+
     let kv = knight_value(game, game.turn) - knight_value(game, !game.turn);
     let king_safety = king_safety(game, game.turn) - king_safety(game, !game.turn);
     let king_there = king_there(game, game.turn) - king_there(game, !game.turn);

+ 1 - 1
src/hash.rs

@@ -63,7 +63,7 @@ impl Cache {
     }
 
     pub fn lookup(&self, game_pos: &Game) -> Option<CacheEntry> {
-        self.hashmap.get(&game_pos.zobrist.clone().unwrap().1).map(|x| x.clone())
+        self.hashmap.get(&game_pos.zobrist.as_ref().unwrap().1).map(|x| x.clone())
     }
     
     pub fn cache(&mut self, game_pos: &Game, ce: CacheEntry) {

+ 52 - 0
src/movegen.rs

@@ -112,6 +112,16 @@ impl Move {
             }
         }
     }
+
+    pub fn is_capture(&self) -> bool {
+        match self {
+            Move::Default{ mov: _, piece_type: _, captured: c } => c.is_some(),
+            Move::Promotion{ mov: _, promote_to: _, captured: c } => c.is_some(),
+            Move::EnPassant{ mov: _, beaten: _ } => true,
+            Move::Castling{ side: _, left: _ } => false,
+            Move::Nullmove => false
+        }
+    }
 }
 
 /**
@@ -192,6 +202,48 @@ pub fn generate_legal_moves(game: &mut Game, side: Side) -> Vec<Move> {
 }
 
 
+pub fn generate_legal_sorted_moves(game: &mut Game, hash: &mut Cache, killers: &[Move], side: Side) -> Vec<Move> {
+    let moves = generate_moves(game, side);
+
+    let illegal_value = crate::evaluate::MAX_VALUE;
+
+    let mut mov_val_and_legality = |mov: &Move| {
+        let undo = game.apply(*mov);
+        let illegal = is_check(game, side);
+
+        if illegal {
+            game.undo_move(undo);
+            return illegal_value;
+        }
+
+        if let Some(e) = hash.lookup(game) {
+            game.undo_move(undo);
+            if let EntryType::Value = e.entry_type {
+                return e.value;
+            }
+            else if let EntryType::LowerBound = e.entry_type {
+                return e.value - 1000;
+            }
+            else if let EntryType::UpperBound = e.entry_type {
+                return e.value + 1000;
+            }
+            else {
+                return crate::evaluate::evaluate(game) - if killers.contains(mov) { 200 } else { 0 };
+            }
+        }
+        else {
+            let eval = crate::evaluate::evaluate(game) - if killers.contains(mov) { 200 } else { 0 };
+            game.undo_move(undo);
+            return eval;
+        }
+    };
+    let mut amovs: Vec<_> = moves.iter().map(|m| (*m, mov_val_and_legality(m))).collect();
+    amovs.sort_unstable_by_key(|x| x.1);
+
+    amovs.into_iter().filter(|x| x.1 != illegal_value).map(|(m, _v)| m).collect::<Vec<Move>>()
+}
+
+
 pub fn generate_attacking_moves(game: &Game, side: Side) -> Vec<Move> {
     let mut moves: Vec<Move> = Vec::with_capacity(32);
     generate_pawn_captures(game, side, &mut moves);

+ 26 - 13
src/search.rs

@@ -11,7 +11,7 @@ pub struct SearchControl<'a> {
     pub nodes: usize,
 
     pub pv: Vec<Move>,
-    pub killer_moves: [Move; 3],
+    pub killer_moves: Vec<[Move; 2]>,
 
     /// function to check if the search should be exited
     pub check: &'a mut dyn FnMut() -> bool,
@@ -34,7 +34,7 @@ impl<'a> SearchControl<'a> {
         SearchControl {
             nodes: 0,
             pv: Vec::with_capacity(depth as usize),
-            killer_moves: [Move::Nullmove; 3],
+            killer_moves: vec![[Move::Nullmove; 2]; depth as usize],
             check,
             stopping: false,
             move_history,
@@ -42,15 +42,25 @@ impl<'a> SearchControl<'a> {
         }
     }
 
-    pub fn insert_killer(&mut self, killer: Move) {
-        for i in 1..self.killer_moves.len() {
-            self.killer_moves[i - 1] = self.killer_moves[i];
+    pub fn set_depth(&mut self, depth: i32) {
+        self.initial_depth = depth;
+        self.killer_moves = vec![[Move::Nullmove; 2]; depth as usize];
+        self.pv = vec![Move::Nullmove; depth as _];
+    }
+
+    pub fn insert_killer(&mut self, ply_depth: usize, killer: Move) {
+        if self.is_killer(ply_depth, &killer) {
+            return;
+        }
+        let nkm = self.killer_moves[ply_depth].len();
+        for i in 1..nkm {
+            self.killer_moves[ply_depth][i - 1] = self.killer_moves[ply_depth][i];
         }
-        self.killer_moves[self.killer_moves.len() - 1] = killer;
+        self.killer_moves[ply_depth][nkm - 1] = killer;
     }
 
-    pub fn is_killer(&self, killer: &Move) -> bool {
-        self.killer_moves.contains(killer)
+    pub fn is_killer(&self, ply_depth: usize, killer: &Move) -> bool {
+        self.killer_moves[ply_depth].contains(killer)
     }
 }
 
@@ -65,7 +75,8 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alp
 
     let mut moves = generate_legal_moves(game, game.turn);
     let mut rng = rand::thread_rng();
-    sort_moves(game, hash, &sc.killer_moves, &mut moves);
+    let ply_depth = (sc.initial_depth - depth) as usize;
+    sort_moves(game, hash, &sc.killer_moves[ply_depth], &mut moves);
     
     info!("moves: {:?}", moves.iter().map(|mv| mv.to_string()).collect::<Vec<String>>());
 
@@ -168,8 +179,8 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
         }
     }
 
-
-    let mut moves = generate_legal_moves(game, game.turn);
+    let ply_depth = (sc.initial_depth - depth) as usize;
+    let moves = generate_legal_sorted_moves(game, hash, &sc.killer_moves[ply_depth], game.turn);
     //info!("nega moves: {:?}", moves.iter().map(|mv| mv.to_string()).collect::<Vec<String>>());
 
     let check = is_check(game, game.turn);
@@ -204,7 +215,7 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
     }
 
 
-    sort_moves(game, hash, &sc.killer_moves, &mut moves);
+    //sort_moves(game, hash, &sc.killer_moves, &mut moves);
     //moves.sort_by_cached_key(|m| if sc.is_killer(*m) { -1 } else { 0 });
 
     let mut alpha_is_exact = false;
@@ -231,7 +242,9 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
             hash.cache(game, CacheEntry::new_upper(depth, val));
             //println!("but ret {}: {}", depth, beta);
             //info!("{} causes beta cutoff at {} ", mov.to_string(), val);
-            sc.insert_killer(mov);
+            if !mov.is_capture() {
+                sc.insert_killer(ply_depth, mov);
+            }
             return beta;
         }
         if increase_mate_in(val) > alpha {