From 0d5c2eb1666d3038fa87895cc98eadc673c54a3a Mon Sep 17 00:00:00 2001 From: eug-vs Date: Tue, 21 Feb 2023 13:26:18 +0300 Subject: refactor: separate zobrist module --- src/board/io.rs | 9 ++---- src/board/mod.rs | 43 ++++------------------------ src/board/zobrist.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 43 deletions(-) create mode 100644 src/board/zobrist.rs (limited to 'src') diff --git a/src/board/io.rs b/src/board/io.rs index cb73c52..b5cd95e 100644 --- a/src/board/io.rs +++ b/src/board/io.rs @@ -1,9 +1,8 @@ use std::io::{stdin, stdout, Write}; -use rand::{rngs::StdRng,SeedableRng,Rng}; use crate::{bitboard::Bitboard, attacks::Attacks, moves::Move, square::Square}; -use super::{Board, PieceType, ttable::TTABLE_SIZE}; +use super::{Board, PieceType, ttable::TTABLE_SIZE, zobrist::Zobrist}; const PIECE_CHARS: [&str; 12] = [ "♟︎", "♞", "♝", "♜", "♛", "♚", @@ -91,8 +90,6 @@ impl IO for Board { } } - let mut rng = StdRng::seed_from_u64(228); - let zobrist_seed = [(); 781].map(|_| rng.gen()); let mut board = Self { piece_sets, @@ -103,10 +100,10 @@ impl IO for Board { ep_target: None, // TODO: parse from FEN hash: 0, transposition_table: vec![None; TTABLE_SIZE as usize], - zobrist_seed, + zobrist_seed: Board::seed(), }; board.update_occupancy(); - board.update_zobrist_hash(); + board.compute_hash(); board } diff --git a/src/board/mod.rs b/src/board/mod.rs index f7016d3..854ce0e 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -1,10 +1,12 @@ use crate::{bitboard::{Bitboard, BitboardFns}, moves::{Move, MoveKind}, attacks::Attacks, square::Square, board::io::IO}; -use self::ttable::{TranspositionTable, TTABLE_SIZE}; +use self::{ttable::{TranspositionTable, TTABLE_SIZE}, zobrist::ZobristSeed}; pub mod io; +mod zobrist; mod engine; mod ttable; +#[derive(Debug, Clone, Copy)] pub enum CastlingSide { King, Queen, @@ -26,7 +28,7 @@ pub struct Board { // TODO: remove transposition_table: TranspositionTable, - zobrist_seed: [u64; 781], + zobrist_seed: ZobristSeed, attacks: Attacks, } @@ -99,39 +101,6 @@ impl Board { self.pieces_by_color(color).iter().fold(0, |acc, bitboard| acc | bitboard) } - /// Compute and store zobrist hash of the current position - /// https://www.chessprogramming.org/Zobrist_Hashing - fn update_zobrist_hash(&mut self) { - self.hash = 0; - - if self.color() == Color::Black { - self.hash ^= match self.zobrist_seed.last() { - Some(x) => x, - None => panic!("Something is wrong with zobrist seed list"), - }; - } - - for (piece_id, bitboard) in self.piece_sets.iter().enumerate() { - for square in bitboard.serialize() { - self.hash ^= self.zobrist_seed[piece_id * 64 + square as usize]; - } - } - - for color in 0..2 { - for castle_side in 0..2 { - if self.castling_rights[color][castle_side] { - self.hash ^= self.zobrist_seed[(12 * 64) + color * 2 + castle_side]; - } - } - } - - match self.ep_target { - Some(square) => { - self.hash ^= self.zobrist_seed[(12 * 64 + 4) + square.file() as usize]; - }, - None => {}, - } - } fn ep_bitboard(&self) -> Bitboard { @@ -731,7 +700,7 @@ impl Color { #[cfg(test)] mod tests { use super::*; - use crate::{bitboard::BitboardFns, square::Square}; + use crate::{bitboard::BitboardFns, square::Square, board::zobrist::Zobrist}; #[test] fn square_enum() { @@ -813,7 +782,7 @@ mod tests { board.print(); let hash = board.hash; - board.update_zobrist_hash(); + board.compute_hash(); assert_eq!(hash, board.hash, "Hash should be correctly updated after move"); assert!(board.piece_sets[PieceType::PawnBlack as usize] & Square::F7.to_bitboard() == 0); diff --git a/src/board/zobrist.rs b/src/board/zobrist.rs new file mode 100644 index 0000000..e282074 --- /dev/null +++ b/src/board/zobrist.rs @@ -0,0 +1,79 @@ +use rand::{rngs::StdRng,SeedableRng,Rng}; + +use crate::{square::Square, bitboard::BitboardFns}; + +use super::{Board, Color, PieceType, CastlingSide}; + + +const TOTAL_PIECES: usize = 12; +const TOTAL_SQUARES: usize = 64; +const TOTAL_FILES: usize = 8; +const TOTAL_CASTLING_RIGHTS: usize = 4; +const ZOBRIST_SEED_SIZE: usize = TOTAL_SQUARES * TOTAL_PIECES + TOTAL_CASTLING_RIGHTS + TOTAL_FILES + 1; + +pub type ZobristSeed = [u64; ZOBRIST_SEED_SIZE]; + +pub trait Zobrist { + fn seed() -> ZobristSeed; + + /// Compute store zobrist hash of the current position + /// https://www.chessprogramming.org/Zobrist_Hashing + fn compute_hash(&mut self) -> (); + + fn zobrist_toggle_piece(&mut self, piece_type: PieceType, square: Square) -> (); + fn zobrist_toggle_castling_right(&mut self, color: Color, side: CastlingSide) -> (); + fn zobrist_toggle_ep_square(&mut self, ep_square: Square) -> (); + fn zobrist_toggle_color(&mut self) -> (); +} + +impl Zobrist for Board { + fn seed() -> ZobristSeed { + let mut rng = StdRng::seed_from_u64(228); + [(); ZOBRIST_SEED_SIZE].map(|_| rng.gen()) + } + + fn compute_hash(&mut self) -> () { + self.hash = 0; + + for piece_id in 0..self.piece_sets.len() { + for square in self.piece_sets[piece_id].serialize() { + self.zobrist_toggle_piece(PieceType::from(piece_id), square); + } + } + for color in [Color::White, Color::Black] { + for castle_side in [CastlingSide::King, CastlingSide::Queen] { + if self.castling_rights[color as usize][castle_side as usize] { + self.zobrist_toggle_castling_right(color, castle_side) + } + } + } + + match self.ep_target { + Some(square) => self.zobrist_toggle_ep_square(square), + None => {}, + } + + if self.color() == Color::Black { + self.zobrist_toggle_color(); + } + } + + fn zobrist_toggle_piece(&mut self, piece_type: PieceType, square: Square) -> () { + self.hash ^= self.zobrist_seed[piece_type as usize * TOTAL_SQUARES + square as usize]; + } + + fn zobrist_toggle_castling_right(&mut self, color: Color, side: CastlingSide) -> () { + self.hash ^= self.zobrist_seed[(TOTAL_PIECES * TOTAL_SQUARES) + color as usize * 2 + side as usize]; + } + + fn zobrist_toggle_ep_square(&mut self, ep_square: Square) -> () { + self.hash ^= self.zobrist_seed[(TOTAL_PIECES * TOTAL_SQUARES + TOTAL_CASTLING_RIGHTS) + ep_square.file() as usize]; + } + + fn zobrist_toggle_color(&mut self) -> () { + self.hash ^= match self.zobrist_seed.last() { + Some(x) => x, + None => panic!("Something is wrong with zobrist seed list"), + }; + } +} -- cgit v1.2.3