Browse Source

provisorisch hashing

Nicolas Winkler 3 năm trước cách đây
mục cha
commit
81cb242267
6 tập tin đã thay đổi với 189 bổ sung14 xóa
  1. 3 0
      src/engine.rs
  2. 122 6
      src/game.rs
  3. 25 6
      src/hash.rs
  4. 1 1
      src/movegen.rs
  5. 27 0
      src/search.rs
  6. 11 1
      src/zobrist.rs

+ 3 - 0
src/engine.rs

@@ -89,6 +89,9 @@ impl Engine {
     }
 
     fn start_search(&mut self, si: &SearchInfo) {
+        if let None = self.game.zobrist {
+            self.game.zobrist = Some((self.zobrist_table.clone(), self.game.calculate_zobrist(&self.zobrist_table)));
+        }
         match si {
             SearchInfo::Depth(depth) => {
                 let receiver = &mut self.r;

+ 122 - 6
src/game.rs

@@ -527,6 +527,15 @@ impl Game {
         return hash;
     }
 
+    pub fn is_zobrist_correct(&self) -> bool {
+        if let Some((ref zt, zv)) = self.zobrist {
+            self.calculate_zobrist(zt) == zv
+        }
+        else {
+            false
+        }
+    }
+
     pub fn apply(&mut self, mov: Move) -> MoveUndo {
 
         // save irrecoverable values
@@ -538,6 +547,8 @@ impl Game {
         let friends = self.get_all_side(side);
         let others = self.get_all_side(!side);
 
+        let with_zobrist = self.zobrist.is_some();
+
         match mov {
             Move::Default{ mov, piece_type: pt, captured: _ } => {
 
@@ -564,19 +575,20 @@ impl Game {
                 // if it is a capture
                 if from_square(mov.to) & others != 0 {
                     let (other_piece, other_side) = self.get_square(mov.to);
-                    if let Some((ref zt, ref _zv)) = self.zobrist {
+
+                    // update zobrist
+                    if let Some((ref zt, ref mut zv)) = self.zobrist {
                         let hup = zt.piece_hash(other_piece, other_side, mov.to);
-                        self.update_zobrist(hup);
+                        *zv ^= hup;
                     }
                     *self.get_piece_mut(other_piece, other_side) &= !from_square(mov.to);
                 }
 
                 let moved_piece = mov.apply_to(self.get_piece(pt, side));
                 self.set_piece(pt, side, moved_piece);
-
-                if let Some((ref zt, ref _zv)) = self.zobrist {
+                if let Some((ref zt, ref mut zv)) = self.zobrist {
                     let hup = zt.piece_hash(pt, side, mov.from) ^ zt.piece_hash(pt, side, mov.to);
-                    self.update_zobrist(hup);
+                    *zv ^= hup;
                 }
             },
             Move::Castling { side, left } => {
@@ -598,20 +610,43 @@ impl Game {
                 self.set_piece(KING, side, new_king);
                 *self.get_piece_mut(ROOK, side) &= !rook;
                 *self.get_piece_mut(ROOK, side) |= new_rook;
+                if let Some((ref zt, ref mut zv)) = self.zobrist {
+                    let hupk = zt.piece_hash(KING, side, square(king)) ^ zt.piece_hash(KING, side, square(new_king));
+                    let hup = zt.piece_hash(ROOK, side, square(rook)) ^ zt.piece_hash(ROOK, side, square(new_rook));
+                    *zv ^= hup ^ hupk;
+                }
             },
             Move::EnPassant { mov, beaten } => {
                 if let Some(_ep) = self.en_passant {
                     *self.get_piece_mut(PAWN, side) &= !from_square(mov.from);
                     *self.get_piece_mut(PAWN, side) |= from_square(mov.to);
                     *self.get_piece_mut(PAWN, !side) &= !from_square(beaten);
+
+                    if let Some((ref zt, ref mut zv)) = self.zobrist {
+                        let hup = zt.piece_hash(PAWN, side, mov.from) ^ zt.piece_hash(PAWN, side, mov.to);
+                        let hupo = zt.piece_hash(PAWN, !side, beaten);
+                        *zv ^= hup ^ hupo;
+                    }
+                }
+                else {
+                    panic!("internal en passant error");
                 }
             },
             Move::Promotion { mov, promote_to, captured } => {
                 //if from_square(mov.to) & others != 0 {
                 if let Some(pt) = captured {
                     *self.get_piece_mut(pt, !side) &= !from_square(mov.to);
+
+                    if let Some((ref zt, ref mut zv)) = self.zobrist {
+                        let hup = zt.piece_hash(pt, !side, mov.to);
+                        *zv ^= hup;
+                    }
+                }
+                *self.get_piece_mut(PAWN, side) &= !from_square(mov.from);
+                if let Some((ref zt, ref mut zv)) = self.zobrist {
+                    let hup = zt.piece_hash(PAWN, side, mov.from);
+                    *zv ^= hup;
                 }
-                self.apply_mask(!from_square(mov.from));
 
                 match promote_to {
                     QUEEN => { *self.queens_mut(side) |= from_square(mov.to); }
@@ -622,13 +657,34 @@ impl Game {
                         info!("internal error");
                     }
                 }
+                if let Some((ref zt, ref mut zv)) = self.zobrist {
+                    let hup = zt.piece_hash(promote_to, side, mov.to);
+                    *zv ^= hup;
+                }
             },
         }
 
         if self.turn == BLACK {
             self.turn_number += 1;
         }
+
         self.turn = !self.turn;
+        if let Some((ref zt, ref mut zv)) = self.zobrist {
+            let hup = zt.turn_hash();
+            let castling_hup = zt.all_castling_rights_hash(castling_rights_before) ^ zt.all_castling_rights_hash(self.castling_rights);
+            let enpass_hup = en_passant_before.map(|f| zt.en_passant_hash(f)).unwrap_or(0) ^ self.en_passant.map(|f| zt.en_passant_hash(f)).unwrap_or(0);
+            *zv ^= hup ^ castling_hup ^ enpass_hup;
+        }
+
+        if !self.is_zobrist_correct() {
+            println!("incorrect zobrist after apply");
+            let val = if let Some((ref zt, _zv)) = self.zobrist {
+                self.calculate_zobrist(zt)
+            } else { 0 };
+            if let Some((ref _zt, ref mut zv)) = self.zobrist {
+                *zv = val;
+            }
+        }
 
         MoveUndo {
             castling_rights_before,
@@ -639,6 +695,13 @@ impl Game {
     }
 
     pub fn undo_move(&mut self, umov: MoveUndo) {
+
+        if let Some((ref zt, ref mut zv)) = self.zobrist {
+            let crhup = zt.all_castling_rights_hash(self.castling_rights) ^ zt.all_castling_rights_hash(umov.castling_rights_before);
+            let enpass_hup = self.en_passant.map(|f| zt.en_passant_hash(f)).unwrap_or(0) ^ umov.en_passant_before.map(|f| zt.en_passant_hash(f)).unwrap_or(0);
+            *zv ^= crhup ^ enpass_hup;
+        }
+
         self.castling_rights = umov.castling_rights_before;
         self.halfmoves_since_last_event = umov.halfmoves_since_last_event_before;
         self.en_passant = umov.en_passant_before;
@@ -653,8 +716,18 @@ impl Game {
                 *moved_piece_bb &= !from_square(mov.to);
                 *moved_piece_bb |= from_square(mov.from);
 
+                if let Some((ref zt, ref mut zv)) = self.zobrist {
+                    let hup = zt.piece_hash(piece_type, side, mov.from) ^ zt.piece_hash(piece_type, side, mov.to);
+                    *zv ^= hup;
+                }
+
                 if let Some(pt) = captured {
                     *self.get_piece_mut(pt, !side) |= from_square(mov.to);
+
+                    if let Some((ref zt, ref mut zv)) = self.zobrist {
+                        let hup = zt.piece_hash(pt, !side, mov.to);
+                        *zv ^= hup;
+                    }
                 }
             },
             Move::Castling { side, left } => {
@@ -673,12 +746,24 @@ impl Game {
                 self.set_piece(KING, side, old_king);
                 *self.get_piece_mut(ROOK, side) &= !rook;
                 *self.get_piece_mut(ROOK, side) |= old_rook;
+
+                if let Some((ref zt, ref mut zv)) = self.zobrist {
+                    let khup = zt.piece_hash(KING, side, square(old_king)) ^ zt.piece_hash(KING, side, square(king));
+                    let rhup = zt.piece_hash(ROOK, side, square(rook)) ^ zt.piece_hash(ROOK, side, square(old_rook));
+                    *zv ^= khup ^ rhup;
+                }
             },
             Move::EnPassant { mov, beaten } => {
                 *self.get_piece_mut(PAWN, side) |= from_square(mov.from);
                 *self.get_piece_mut(PAWN, side) &= !from_square(mov.to);
                 *self.get_piece_mut(PAWN, !side) |= from_square(beaten);
 
+                if let Some((ref zt, ref mut zv)) = self.zobrist {
+                    let phup = zt.piece_hash(PAWN, side, mov.from) ^ zt.piece_hash(PAWN, side, mov.to);
+                    let chup = zt.piece_hash(PAWN, !side, beaten);
+                    *zv ^= phup ^ chup;
+                }
+
                 // should already be reverted
                 //self.en_passant = Some(indices_from_square(beaten).0);
             },
@@ -694,8 +779,18 @@ impl Game {
                 }
                 *self.pawns_mut(side) |= from_square(mov.from);
 
+                if let Some((ref zt, ref mut zv)) = self.zobrist {
+                    let hup = zt.piece_hash(PAWN, side, mov.from) ^ zt.piece_hash(promote_to, side, mov.to);
+                    *zv ^= hup;
+                }
+
                 if let Some(pt) = captured {
                     *self.get_piece_mut(pt, !side) |= from_square(mov.to);
+
+                    if let Some((ref zt, ref mut zv)) = self.zobrist {
+                        let hup = zt.piece_hash(pt, !side, mov.to);
+                        *zv ^= hup;
+                    }
                 }
             }
         }
@@ -704,5 +799,26 @@ impl Game {
         if side == BLACK {
             self.turn_number -= 1;
         }
+
+        if let Some((ref zt, ref mut zv)) = self.zobrist {
+            *zv ^= zt.turn_hash();
+        }
+
+        /*let val = if let Some((ref zt, _zv)) = self.zobrist {
+            self.calculate_zobrist(zt)
+        } else { 0 };
+        if let Some((ref _zt, ref mut zv)) = self.zobrist {
+            *zv = val;
+        }*/
+
+        if !self.is_zobrist_correct() {
+            println!("incorrect zobrist after undo {}!", umov.mov.to_string());
+            let val = if let Some((ref zt, _zv)) = self.zobrist {
+                self.calculate_zobrist(zt)
+            } else { 0 };
+            if let Some((ref _zt, ref mut zv)) = self.zobrist {
+                *zv = val;
+            }
+        }
     }
 }

+ 25 - 6
src/hash.rs

@@ -3,6 +3,7 @@ use evaluate::PosValue;
 use std::collections::{HashMap};
 use std::hash::{BuildHasher, Hasher, Hash};
 use log::info;
+use zobrist;
 
 #[derive(Clone)]
 pub enum EntryType {
@@ -26,30 +27,48 @@ impl CacheEntry {
             value
         }
     }
+
+    pub fn new_beta(depth: i32, beta: PosValue) -> Self {
+        CacheEntry {
+            entry_type: EntryType::UpperBound,
+            depth,
+            value: beta
+        }
+    }
+
+    pub fn new_alpha(depth: i32, alpha: PosValue) -> Self {
+        CacheEntry {
+            entry_type: EntryType::LowerBound,
+            depth,
+            value: alpha
+        }
+    }
 }
 
 pub struct Cache {
-    hashmap: HashMap<Game, CacheEntry>,
+    hashmap: HashMap<zobrist::Hash, CacheEntry>,
+    max_capacity: usize
 }
 
 impl Cache {
     pub fn new() -> Self {
         Cache {
-            hashmap: HashMap::new()
+            hashmap: HashMap::new(),
+            max_capacity: 10000000
         }
     }
 
     pub fn lookup(&self, game_pos: &Game) -> Option<CacheEntry> {
-        self.hashmap.get(game_pos).map(|x| x.clone())
+        self.hashmap.get(&game_pos.zobrist.clone().unwrap().1).map(|x| x.clone())
     }
     
     pub fn cache(&mut self, game_pos: &Game, ce: CacheEntry) {
-        if self.hashmap.len() > 1000000 {
+        if self.hashmap.len() > self.max_capacity {
             let first_key = self.hashmap.keys().next().unwrap().clone();
             self.hashmap.remove(&first_key);
         }
-        self.hashmap.insert(game_pos.clone(), ce);
-        if self.hashmap.len() % 1024 == 0 {
+        self.hashmap.insert(game_pos.zobrist.clone().unwrap().1, ce);
+        if self.hashmap.len() % (1024 * 32) == 0 {
             info!("hash contains {} items", self.hashmap.len());
         }
     }

+ 1 - 1
src/movegen.rs

@@ -176,7 +176,7 @@ pub fn generate_legal_moves(game: &Game, side: Side) -> Vec<Move> {
     let moves = generate_moves(game, side);
 
     moves.into_iter().filter(|mov| {
-        let tryout = super::search::apply_move(game, *mov);
+        let tryout = crate::search::apply_move(game, *mov);
         !is_check(&tryout, side)
     }).collect::<Vec<Move>>()
 }

+ 27 - 0
src/search.rs

@@ -141,6 +141,21 @@ fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha:
         return (quiescence_search(game, sc, alpha, beta, 9), false);
     }
 
+    let mut is_corr: PosValue = -1;
+
+
+    let hash_entry = hash.lookup(game);
+    if let Some(e) = hash_entry {
+        if e.depth >= depth {
+            //println!("TABLE HIT!");
+            match e.entry_type {
+                EntryType::Value => { return (e.value, false); },
+                EntryType::LowerBound => { if e.value > alpha { return (e.value, false) } },
+                EntryType::UpperBound => { if e.value >= beta { return (beta, false); } },
+            }
+        }
+    }
+
     sc.nodes += 1;
     if sc.nodes % 128 == 0 {
         if (sc.check)() {
@@ -167,8 +182,11 @@ fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha:
         }
     }
 
+    let mut alpha_is_exact = false;
+
     for mov in moves {
         let undo = game.apply(mov);
+
         let (mut val, ret) = negamax(game, sc, hash, -beta, -alpha, depth - 1);
         val = -val;
 
@@ -185,10 +203,13 @@ fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha:
 
         if val >= beta {
             game.undo_move(undo);
+            hash.cache(game, CacheEntry::new_beta(depth, beta));
+            //println!("but ret {}: {}", depth, beta);
             return (beta, false);
         }
         if val > alpha {
             alpha = val;//(val as f64 * 0.95) as _;
+            alpha_is_exact = true;
         }
         //info!(" -> negamaxed {} -> {}", mov.to_string(), depth - 1);
         if val > best {
@@ -199,6 +220,12 @@ fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha:
     }
 
 
+    if alpha_is_exact {
+        hash.cache(game, CacheEntry::new_value(depth, alpha));
+    }
+    else {
+        hash.cache(game, CacheEntry::new_alpha(depth, alpha));
+    }
     //info!("best alpha {}", best);
     return (alpha, false);
 }

+ 11 - 1
src/zobrist.rs

@@ -1,6 +1,6 @@
 use rand::prelude::*;
 
-use movegen::{PieceType};
+use movegen::{PieceType, Side, BLACK};
 use bitboard::Square;
 
 pub type Hash = u64;
@@ -60,6 +60,16 @@ impl ZobristTable {
         self.castling_rights[cr as usize]
     }
 
+    pub fn all_castling_rights_hash(&self, cr: [bool; 4]) -> Hash {
+        let mut val: Hash = 0;
+        for i in 0..4 {
+            if cr[i] {
+                val ^= self.castling_rights[i as usize]
+            }
+        }
+        val
+    }
+
     pub fn turn_hash(&self) -> Hash {
         self.turn
     }