Add Zobrist hashing to 'ChessBoard'
This commit is contained in:
parent
c850529257
commit
06b5751702
|
@ -106,7 +106,7 @@ impl TryFrom<ChessBoardBuilder> for ChessBoard {
|
||||||
|
|
||||||
let total_plies = (turn_count - 1) * 2 + if side == Color::White { 0 } else { 1 };
|
let total_plies = (turn_count - 1) * 2 + if side == Color::White { 0 } else { 1 };
|
||||||
|
|
||||||
let board = ChessBoard {
|
let mut board = ChessBoard {
|
||||||
piece_occupancy,
|
piece_occupancy,
|
||||||
color_occupancy,
|
color_occupancy,
|
||||||
combined_occupancy,
|
combined_occupancy,
|
||||||
|
@ -115,7 +115,9 @@ impl TryFrom<ChessBoardBuilder> for ChessBoard {
|
||||||
half_move_clock,
|
half_move_clock,
|
||||||
total_plies,
|
total_plies,
|
||||||
side,
|
side,
|
||||||
|
hash: 0,
|
||||||
};
|
};
|
||||||
|
board.hash = board.compute_hash();
|
||||||
|
|
||||||
board.validate()?;
|
board.validate()?;
|
||||||
Ok(board)
|
Ok(board)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::movegen;
|
use crate::movegen;
|
||||||
|
|
||||||
use super::{Bitboard, CastleRights, Color, File, Move, Piece, Rank, Square};
|
use super::{zobrist, Bitboard, CastleRights, Color, File, Move, Piece, Rank, Square};
|
||||||
|
|
||||||
mod builder;
|
mod builder;
|
||||||
pub use builder::*;
|
pub use builder::*;
|
||||||
|
@ -29,6 +29,8 @@ pub struct ChessBoard {
|
||||||
total_plies: u32, // Should be plenty.
|
total_plies: u32, // Should be plenty.
|
||||||
/// The current player turn.
|
/// The current player turn.
|
||||||
side: Color,
|
side: Color,
|
||||||
|
/// Zobrist hash for the board.
|
||||||
|
hash: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state which can't be reversed when doing/un-doing a [Move].
|
/// The state which can't be reversed when doing/un-doing a [Move].
|
||||||
|
@ -123,12 +125,42 @@ impl ChessBoard {
|
||||||
self.compute_checkers(self.current_player())
|
self.compute_checkers(self.current_player())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the Zobrist hash for the board.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn hash(&self) -> u64 {
|
||||||
|
self.hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the Zobrist hash for the board.
|
||||||
|
fn compute_hash(&self) -> u64 {
|
||||||
|
let mut res = 0;
|
||||||
|
|
||||||
|
// The side-to-move is only toggled on Black, and otherwise 0.
|
||||||
|
if self.current_player() == Color::Black {
|
||||||
|
res ^= zobrist::side_to_move();
|
||||||
|
}
|
||||||
|
if let Some(square) = self.en_passant() {
|
||||||
|
res ^= zobrist::en_passant(square.file());
|
||||||
|
}
|
||||||
|
res ^= zobrist::castling_rights(self.castle_rights);
|
||||||
|
for piece in Piece::iter() {
|
||||||
|
for color in Color::iter() {
|
||||||
|
for square in self.occupancy(piece, color) {
|
||||||
|
res ^= zobrist::moved_piece(color, piece, square);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
/// Quickly add/remove a piece on the [Bitboard]s that are part of the [ChessBoard] state.
|
/// Quickly add/remove a piece on the [Bitboard]s that are part of the [ChessBoard] state.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn xor(&mut self, color: Color, piece: Piece, square: Square) {
|
fn xor(&mut self, color: Color, piece: Piece, square: Square) {
|
||||||
*self.piece_occupancy_mut(piece) ^= square;
|
*self.piece_occupancy_mut(piece) ^= square;
|
||||||
*self.color_occupancy_mut(color) ^= square;
|
*self.color_occupancy_mut(color) ^= square;
|
||||||
self.combined_occupancy ^= square;
|
self.combined_occupancy ^= square;
|
||||||
|
self.hash ^= zobrist::moved_piece(color, piece, square);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the change of [CastleRights] from moving/taking a piece.
|
/// Compute the change of [CastleRights] from moving/taking a piece.
|
||||||
|
@ -140,9 +172,9 @@ impl ChessBoard {
|
||||||
(Piece::King, _) => CastleRights::NoSide,
|
(Piece::King, _) => CastleRights::NoSide,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
if new_rights != original {
|
self.hash ^= zobrist::castling_rights(self.castle_rights);
|
||||||
*self.castle_rights_mut(color) = new_rights;
|
*self.castle_rights_mut(color) = new_rights;
|
||||||
}
|
self.hash ^= zobrist::castling_rights(self.castle_rights);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Play the given [Move], return a copy of the board with the resulting state.
|
/// Play the given [Move], return a copy of the board with the resulting state.
|
||||||
|
@ -182,14 +214,26 @@ impl ChessBoard {
|
||||||
} else {
|
} else {
|
||||||
self.half_move_clock += 1;
|
self.half_move_clock += 1;
|
||||||
}
|
}
|
||||||
if is_double_step {
|
let en_passant = if is_double_step {
|
||||||
let target_square = Square::new(
|
let target_square = Square::new(
|
||||||
chess_move.destination().file(),
|
chess_move.destination().file(),
|
||||||
self.current_player().third_rank(),
|
self.current_player().third_rank(),
|
||||||
);
|
);
|
||||||
self.en_passant = Some(target_square);
|
Some(target_square)
|
||||||
} else {
|
} else {
|
||||||
self.en_passant = None;
|
None
|
||||||
|
};
|
||||||
|
if self.en_passant() != en_passant {
|
||||||
|
self.hash ^= self
|
||||||
|
.en_passant()
|
||||||
|
.map(Square::file)
|
||||||
|
.map(zobrist::en_passant)
|
||||||
|
.unwrap_or(0);
|
||||||
|
self.hash ^= en_passant
|
||||||
|
.map(Square::file)
|
||||||
|
.map(zobrist::en_passant)
|
||||||
|
.unwrap_or(0);
|
||||||
|
self.en_passant = en_passant;
|
||||||
}
|
}
|
||||||
self.update_castling(self.current_player(), move_piece, chess_move.start().file());
|
self.update_castling(self.current_player(), move_piece, chess_move.start().file());
|
||||||
if let Some(piece) = captured_piece {
|
if let Some(piece) = captured_piece {
|
||||||
|
@ -203,6 +247,7 @@ impl ChessBoard {
|
||||||
self.xor(self.current_player(), move_piece, chess_move.destination());
|
self.xor(self.current_player(), move_piece, chess_move.destination());
|
||||||
self.total_plies += 1;
|
self.total_plies += 1;
|
||||||
self.side = !self.side;
|
self.side = !self.side;
|
||||||
|
self.hash ^= zobrist::side_to_move();
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
@ -212,8 +257,24 @@ impl ChessBoard {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn unplay_move(&mut self, chess_move: Move, previous: NonReversibleState) {
|
pub fn unplay_move(&mut self, chess_move: Move, previous: NonReversibleState) {
|
||||||
// Restore non-revertible state
|
// Restore non-revertible state
|
||||||
self.castle_rights = previous.castle_rights;
|
if self.castle_rights != previous.castle_rights {
|
||||||
self.en_passant = previous.en_passant;
|
self.hash ^= zobrist::castling_rights(self.castle_rights);
|
||||||
|
self.hash ^= zobrist::castling_rights(previous.castle_rights);
|
||||||
|
self.castle_rights = previous.castle_rights;
|
||||||
|
}
|
||||||
|
if self.en_passant != previous.en_passant {
|
||||||
|
self.hash ^= self
|
||||||
|
.en_passant()
|
||||||
|
.map(Square::file)
|
||||||
|
.map(zobrist::en_passant)
|
||||||
|
.unwrap_or(0);
|
||||||
|
self.hash ^= previous
|
||||||
|
.en_passant
|
||||||
|
.map(Square::file)
|
||||||
|
.map(zobrist::en_passant)
|
||||||
|
.unwrap_or(0);
|
||||||
|
self.en_passant = previous.en_passant;
|
||||||
|
}
|
||||||
self.half_move_clock = previous.half_move_clock;
|
self.half_move_clock = previous.half_move_clock;
|
||||||
|
|
||||||
let move_piece = Piece::iter()
|
let move_piece = Piece::iter()
|
||||||
|
@ -231,6 +292,7 @@ impl ChessBoard {
|
||||||
self.xor(!self.current_player(), move_piece, chess_move.start());
|
self.xor(!self.current_player(), move_piece, chess_move.start());
|
||||||
self.total_plies -= 1;
|
self.total_plies -= 1;
|
||||||
self.side = !self.side;
|
self.side = !self.side;
|
||||||
|
self.hash ^= zobrist::side_to_move();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the current state of the board looks valid, false if something is definitely
|
/// Return true if the current state of the board looks valid, false if something is definitely
|
||||||
|
@ -421,7 +483,7 @@ impl ChessBoard {
|
||||||
/// "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" FEN string
|
/// "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" FEN string
|
||||||
impl Default for ChessBoard {
|
impl Default for ChessBoard {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
let mut res = Self {
|
||||||
piece_occupancy: [
|
piece_occupancy: [
|
||||||
// King
|
// King
|
||||||
Square::E1 | Square::E8,
|
Square::E1 | Square::E8,
|
||||||
|
@ -449,7 +511,10 @@ impl Default for ChessBoard {
|
||||||
half_move_clock: 0,
|
half_move_clock: 0,
|
||||||
total_plies: 0,
|
total_plies: 0,
|
||||||
side: Color::White,
|
side: Color::White,
|
||||||
}
|
hash: 0,
|
||||||
|
};
|
||||||
|
res.hash = res.compute_hash();
|
||||||
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue