Bladeren bron

adding first parts of countermoves

Nicolas Winkler 2 jaren geleden
bovenliggende
commit
2bba89139c
4 gewijzigde bestanden met toevoegingen van 106 en 43 verwijderingen
  1. 4 3
      src/engine.rs
  2. 21 18
      src/evaluate.rs
  3. 38 5
      src/movegen.rs
  4. 43 17
      src/search.rs

+ 4 - 3
src/engine.rs

@@ -176,8 +176,8 @@ impl Engine {
                 if ce.mov == (SimpleMove{ from: 0, to: 0 }) {
                     break;
                 }
-                let movs = generate_moves(game, game.turn);
-                let bm = movs.iter().find(|m| m.to_simple() == ce.mov);
+                let movs = generate_moves(&g, g.turn);
+                let bm = movs.iter().find(|m| (**m).to_simple() == ce.mov);
                 if let Some(found) = bm {
                     g.apply(*found);
                     pv.push(*found);
@@ -316,7 +316,8 @@ impl Engine {
                 let pv = &pv_array.iter().filter(|&m| !m.is_null()).map(|m| m.to_string()).fold(String::new(), |a, b| a + " " + &b)[0..];
 
                 if let Some(plies) = crate::evaluate::is_mate_in_p1(bv) {
-                    let turns = plies / 2;
+                    println!("plies = {}", plies);
+                    let turns = (plies + 1) / 2;
                     let infostring = format!("info depth {} score mate {} time {} nodes {} nps {} hashfull {} pv{}", depth, turns, elapsed.as_millis(), nodes, nps, hashfull, pv);
                     println!("{}", infostring);
                     info!("{}", infostring);

+ 21 - 18
src/evaluate.rs

@@ -18,20 +18,23 @@ pub const MAX_VALUE: PosValue = i32::MAX;
 const MATE_VALUE: PosValue = 1i32 << 22;
 const MATE_CUTOFF: PosValue = 1i32 << 20;
 
-pub fn mate() -> PosValue {
-    mate_in_p1(1)
+///
+/// evaluation score for a position in which the player to move is checkmated
+/// 
+pub fn checkmated() -> PosValue {
+    -mate_in_p1(0)
 }
 
 /// 
 /// constructs a PosValue that indicates that from a given position mate
-/// can be achieved in turns-1 moves (not halfmoves)
+/// can be achieved in plies halfmoves
 /// 
-pub fn mate_in_p1(turns: i32) -> PosValue {
-    if turns < 0 {
-        return -mate_in_p1(-turns);
+pub fn mate_in_p1(plies: i32) -> PosValue {
+    if plies < 0 {
+        return -mate_in_p1(- plies);
     }
     else {
-        return MATE_VALUE - turns;
+        return MATE_VALUE - plies;
     }
 }
 
@@ -55,14 +58,14 @@ pub fn is_mate_in_p1(pos_val: PosValue) -> Option<i32> {
 }
 
 pub fn increase_mate_in(mut val: PosValue) -> PosValue {
-    if let Some(turns) = is_mate_in_p1(val) {
-        if turns < 0 {
-            val = mate_in_p1(turns - 1);
+    if let Some(plies) = is_mate_in_p1(val) {
+        if plies < 0 {
+            val = mate_in_p1(plies - 1);
         }
-        if turns > 0 {
-            val = mate_in_p1(turns + 1);
+        if plies > 0 {
+            val = mate_in_p1(plies + 1);
         }
-        if turns == 0 {
+        if plies == 0 {
             if val < 0 {
                 val = mate_in_p1(-1);
             }
@@ -75,12 +78,12 @@ pub fn increase_mate_in(mut val: PosValue) -> PosValue {
 }
 
 pub fn decrease_mate_in(mut val: PosValue) -> PosValue {
-    if let Some(turns) = is_mate_in_p1(val) {
-        if turns < 0 {
-            val = mate_in_p1(turns + 1);
+    if let Some(plies) = is_mate_in_p1(val) {
+        if plies < 0 {
+            val = mate_in_p1(plies + 1);
         }
-        if turns > 0 {
-            val = mate_in_p1(turns - 1);
+        if plies > 0 {
+            val = mate_in_p1(plies - 1);
         }
     }
     val

+ 38 - 5
src/movegen.rs

@@ -176,6 +176,21 @@ impl Move {
             },
         }
     }
+
+    pub fn piece_type(&self) -> PieceType {
+        match self {
+            Move::Default{ mov: _, pc } |
+            Move::Promotion{ mov: _, pc } => {
+                pc.piece_type()
+            },
+            Move::Castling{ mov, side: _, left: _ } => {
+                KING
+            },
+            Move::EnPassant{ mov, beaten: _ } => {
+                PAWN
+            },
+        }
+    }
 }
 
 /**
@@ -212,6 +227,7 @@ enum SortingState {
     PvMove,
     Captures,
     Killers,
+    Counter,
     Quiets
 }
 
@@ -227,24 +243,30 @@ pub struct MoveGenerator {
     state: SortingState,
 
     pv_move: Option<SimpleMove>,
-    killers: Vec<Move>
+    killers: Vec<Move>,
+    counter: Option<Move>
 }
 
 impl MoveGenerator {
-    pub fn generate_legal_moves(game: &mut Game, ce: Option<&CacheEntry>, killers: &[Move], side: Side) -> Self {
+    pub fn generate_legal_moves(game: &mut Game, ce: Option<&CacheEntry>, killers: &[Move], counter: Option<Move>, side: Side) -> Self {
         MoveGenerator {
             moves: generate_legal_moves(game, side, false),
             captures: Vec::with_capacity(32),
             quiets: Vec::with_capacity(32),
             state: if ce.is_some() { SortingState::PvMove } else { SortingState::Captures },
             pv_move: ce.map(|e| e.mov),
-            killers: killers.to_vec()
+            killers: killers.to_vec(),
+            counter
         }
     }
 
     pub fn is_empty(&self) -> bool {
         return self.moves.is_empty() && self.captures.is_empty() && self.quiets.is_empty();
     }
+
+    pub fn update_counter(&mut self, counter: Move) {
+        self.counter = Some(counter);
+    }
 }
 
 impl Iterator for MoveGenerator {
@@ -305,7 +327,18 @@ impl Iterator for MoveGenerator {
                             return Some(*k);
                         }
                     }
+                    self.state = SortingState::Counter;
+                },
+                SortingState::Counter => {
                     self.state = SortingState::Quiets;
+                    if let Some(c) = self.counter {
+                        if self.quiets.contains(&c) {
+                            self.quiets.retain(|m| *m != c);
+                            //println!("counter!");
+                            return Some(c);
+                        }
+                    }
+                    continue;
                 },
                 SortingState::Quiets => {
                     return self.quiets.pop();
@@ -453,8 +486,8 @@ pub fn generate_legal_moves(game: &mut Game, side: Side, captures_only: bool) ->
 }
 
 
-pub fn generate_legal_sorted_moves(game: &mut Game, _hash: &mut Cache, killers: &[Move], ce: Option<CacheEntry>, side: Side) -> Vec<Move> {
-    let mut moves = generate_legal_moves(game, side, false);
+pub fn generate_legal_sorted_moves(game: &mut Game, _hash: &mut Cache, killers: &[Move], ce: Option<CacheEntry>, captures_only: bool, side: Side) -> Vec<Move> {
+    let mut moves = generate_legal_moves(game, side, captures_only);
 
     let mov_val= |mov: &Move| {
         // if its a pv move from previously

+ 43 - 17
src/search.rs

@@ -18,6 +18,9 @@ pub struct SearchControl<'a> {
 
     pub killer_moves: Vec<[Move; 2]>,
 
+    pub last_move: Option<Move>,
+    pub countermoves: [[Move; 64]; 64],
+
     /// current halfmove clock for discarding old hash entries
     pub halfmove_age: u16,
 
@@ -43,6 +46,8 @@ impl<'a> SearchControl<'a> {
         SearchControl {
             nodes: 0,
             killer_moves: vec![[Move::nullmove(); 2]; depth as usize],
+            last_move: None,
+            countermoves: [[Move::nullmove(); 64]; 64],
             halfmove_age: 0,
             check,
             stopping: false,
@@ -71,6 +76,13 @@ impl<'a> SearchControl<'a> {
     pub fn is_killer(&self, ply_depth: usize, killer: &Move) -> bool {
         self.killer_moves[ply_depth].contains(killer)
     }
+
+    pub fn countermove_to(&self, last_move: Move) -> Move {
+        let from = last_move.to_simple().from;
+        let to = last_move.to_simple().to;
+        self.countermoves[from as usize][to as usize]
+    }
+
 }
 
 
@@ -86,7 +98,7 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alp
     let cache_entry = hash.lookup(game);
 
     let ply_depth = (sc.initial_depth - depth) as usize;
-    let moves = generate_legal_sorted_moves(game, hash, &sc.killer_moves[ply_depth], cache_entry.map(CacheEntry::clone), game.turn);
+    let moves = generate_legal_sorted_moves(game, hash, &sc.killer_moves[ply_depth], cache_entry.map(CacheEntry::clone), false, game.turn);
     //let mut moves = generate_legal_moves(game, game.turn);
     //sort_moves(game, hash, &sc.killer_moves[ply_depth], &mut moves);
     
@@ -142,6 +154,13 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alp
 
 pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, mut depth: i32) -> PosValue {
 
+    let last_move = sc.last_move;
+
+    // we can't beat an alpha this good
+    if alpha >= mate_in_p1(1) {
+        return alpha;
+    }
+
     let cache_entry = hash.lookup(game);
 
     if let Some(e) = &cache_entry {
@@ -179,10 +198,11 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
         cache_entry,
         game.turn);*/
 
-    let moves = MoveGenerator::generate_legal_moves(
+    let mut moves = MoveGenerator::generate_legal_moves(
         game,
         cache_entry,
         &sc.killer_moves[ply_depth],
+        last_move.map(|lm| sc.countermove_to(lm)),
         game.turn
     );
 
@@ -192,7 +212,7 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
     if moves.is_empty() {
         if check {
             // mate
-            return -mate();
+            return checkmated();
         }
         else {
             // stalemate
@@ -201,7 +221,7 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
     }
 
     // Nullmove
-    if sc.nullmoves_enabled && !check && depth >= 4 && game_lateness(game) < 60 {
+    if sc.nullmoves_enabled && !check && depth >= 4 && game.get_all_side(game.turn).count_ones() > 5 {
 
         let reduce = if depth > 5 { if depth > 7 { 5 } else { 4 }} else { 3 };
 
@@ -213,10 +233,6 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
         sc.nullmoves_enabled = true;
         game.undo_move(undo);
 
-        if sc.stopping {
-            return alpha;
-        }
-
         if is_mate_in_p1(val).is_none() {
             if val >= beta {
                 depth -= reduce - 1;
@@ -227,14 +243,10 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
 
     let mut best_move: Option<Move> = None;
 
-    // we can't beat an alpha this good
-    if alpha >= mate() {
-        return alpha;
-    }
-
-    for mov in moves {
+    while let Some(mov) = moves.next() {
         //println!("mov: {}", mov.to_string());
         let undo = game.apply(mov);
+        sc.last_move = Some(mov);
 
         let val = increase_mate_in(
             -negamax(game, sc, hash, -decrease_mate_in(beta), -decrease_mate_in(alpha), depth - 1)
@@ -250,12 +262,25 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
             hash.cache(game, CacheEntry::new_lower(depth as _, sc.halfmove_age as _, mov.to_simple(), val));
             if !mov.is_capture() {
                 sc.insert_killer(ply_depth, mov);
+                if depth >= 2 {
+                    sc.countermoves[mov.to_simple().from as usize][mov.to_simple().to as usize] = mov;
+                }
             }
             return val;
         }
         if val > alpha {
             alpha = val;
             best_move = Some(mov);
+            if depth >= 3 {
+                sc.countermoves[mov.to_simple().from as usize][mov.to_simple().to as usize] = mov;
+            }
+            if alpha >= mate_in_p1(1) {
+                break;
+            }
+        }
+
+        if let Some(lm) = last_move {
+            moves.update_counter(sc.countermove_to(lm));
         }
     }
 
@@ -294,9 +319,10 @@ fn quiescence_search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache,
         return alpha;
     }
 
-    if game.get_piece(KING, game.turn) == 0 { return -mate(); }
+    if game.get_piece(KING, game.turn) == 0 { return checkmated(); }
 
-    let mut moves = generate_attacking_moves(game, game.turn);
+    //let mut moves = generate_legal_sorted_moves(game, hash, &[], None, true, game.turn);
+    let mut moves = generate_legal_moves(game, game.turn, true);
 
     //sort_moves_no_hash(game, &mut moves);
     sort_moves_least_valuable_attacker(game, &mut moves);
@@ -334,7 +360,7 @@ pub fn perft(game: &mut Game, sc: &mut SearchControl, depth: i32) -> bool {
         return false;
     }
 
-    
+
     for mov in moves {
         let nodes_before = sc.nodes;
         let undo = game.apply(mov);