2024-04-25 13:28:24 -07:00
|
|
|
// Eryn Wells <eryn@erynwells.me>
|
|
|
|
|
2025-05-08 17:37:51 -07:00
|
|
|
use crate::{
|
2025-06-17 08:28:39 -07:00
|
|
|
PieceSet, castle,
|
2025-05-08 17:37:51 -07:00
|
|
|
display::DiagramFormatter,
|
|
|
|
piece_sets::{PlacePieceError, PlacePieceStrategy},
|
2025-06-05 08:21:32 -07:00
|
|
|
zobrist::{ZobristHash, ZobristState},
|
2025-05-08 17:37:51 -07:00
|
|
|
};
|
2024-04-25 13:28:24 -07:00
|
|
|
use chessfriend_bitboard::BitBoard;
|
2025-06-03 20:25:53 -07:00
|
|
|
use chessfriend_core::{Color, Piece, Shape, Square, Wing};
|
2025-06-05 08:21:32 -07:00
|
|
|
use std::sync::Arc;
|
2024-04-25 13:28:24 -07:00
|
|
|
|
2025-05-23 09:53:29 -07:00
|
|
|
pub type HalfMoveClock = u32;
|
|
|
|
pub type FullMoveClock = u32;
|
|
|
|
|
2025-05-08 17:37:51 -07:00
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
2024-04-25 13:28:24 -07:00
|
|
|
pub struct Board {
|
2025-06-02 17:29:52 -07:00
|
|
|
active_color: Color,
|
2025-06-02 15:46:10 -07:00
|
|
|
pieces: PieceSet,
|
2025-06-03 20:25:53 -07:00
|
|
|
castling_rights: castle::Rights,
|
|
|
|
en_passant_target: Option<Square>,
|
2025-05-23 09:53:29 -07:00
|
|
|
pub half_move_clock: HalfMoveClock,
|
|
|
|
pub full_move_number: FullMoveClock,
|
2025-06-05 08:21:32 -07:00
|
|
|
zobrist_hash: Option<ZobristHash>,
|
2024-04-25 13:28:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Board {
|
|
|
|
/// An empty board
|
|
|
|
#[must_use]
|
2025-06-05 08:21:32 -07:00
|
|
|
pub fn empty(zobrist: Option<Arc<ZobristState>>) -> Self {
|
|
|
|
let mut board = Self {
|
|
|
|
zobrist_hash: zobrist.map(ZobristHash::new),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
board.recompute_zobrist_hash();
|
|
|
|
|
|
|
|
board
|
2024-04-25 13:28:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The starting position
|
|
|
|
#[must_use]
|
2025-06-05 08:21:32 -07:00
|
|
|
pub fn starting(zobrist: Option<Arc<ZobristState>>) -> Self {
|
2024-04-25 13:28:24 -07:00
|
|
|
const BLACK_PIECES: [BitBoard; Shape::NUM] = [
|
|
|
|
BitBoard::new(0b0000_0000_1111_1111 << 48),
|
|
|
|
BitBoard::new(0b0100_0010_0000_0000 << 48),
|
|
|
|
BitBoard::new(0b0010_0100_0000_0000 << 48),
|
|
|
|
BitBoard::new(0b1000_0001_0000_0000 << 48),
|
|
|
|
BitBoard::new(0b0000_1000_0000_0000 << 48),
|
|
|
|
BitBoard::new(0b0001_0000_0000_0000 << 48),
|
|
|
|
];
|
|
|
|
|
|
|
|
const WHITE_PIECES: [BitBoard; Shape::NUM] = [
|
|
|
|
BitBoard::new(0b1111_1111_0000_0000),
|
|
|
|
BitBoard::new(0b0000_0000_0100_0010),
|
|
|
|
BitBoard::new(0b0000_0000_0010_0100),
|
|
|
|
BitBoard::new(0b0000_0000_1000_0001),
|
|
|
|
BitBoard::new(0b0000_0000_0000_1000),
|
|
|
|
BitBoard::new(0b0000_0000_0001_0000),
|
|
|
|
];
|
|
|
|
|
2025-06-05 08:21:32 -07:00
|
|
|
let mut board = Self {
|
2024-07-13 08:15:14 -07:00
|
|
|
pieces: PieceSet::new([WHITE_PIECES, BLACK_PIECES]),
|
2025-06-05 08:21:32 -07:00
|
|
|
zobrist_hash: zobrist.map(ZobristHash::new),
|
2024-04-25 13:28:24 -07:00
|
|
|
..Default::default()
|
2025-06-05 08:21:32 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
board.recompute_zobrist_hash();
|
|
|
|
|
|
|
|
board
|
2024-04-25 13:28:24 -07:00
|
|
|
}
|
2025-05-08 17:37:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Board {
|
2025-06-02 17:29:52 -07:00
|
|
|
#[must_use]
|
|
|
|
pub fn active_color(&self) -> Color {
|
|
|
|
self.active_color
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_active_color(&mut self, color: Color) {
|
|
|
|
if color == self.active_color {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.active_color = color;
|
2025-06-05 08:21:32 -07:00
|
|
|
|
|
|
|
if let Some(zobrist) = self.zobrist_hash.as_mut() {
|
|
|
|
zobrist.update_setting_active_color(color);
|
|
|
|
}
|
2025-06-02 17:29:52 -07:00
|
|
|
}
|
2025-06-05 08:21:32 -07:00
|
|
|
}
|
2025-06-02 17:29:52 -07:00
|
|
|
|
2025-06-03 20:25:53 -07:00
|
|
|
impl Board {
|
|
|
|
#[must_use]
|
|
|
|
pub fn castling_rights(&self) -> castle::Rights {
|
|
|
|
self.castling_rights
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_castling_rights(&mut self, rights: castle::Rights) {
|
2025-06-05 08:21:32 -07:00
|
|
|
if rights == self.castling_rights {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let old_rights = self.castling_rights;
|
2025-06-03 20:25:53 -07:00
|
|
|
self.castling_rights = rights;
|
2025-06-05 08:21:32 -07:00
|
|
|
self.update_zobrist_hash_castling_rights(old_rights);
|
2025-06-03 20:25:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
pub fn active_color_has_castling_right(&self, wing: Wing) -> bool {
|
2025-06-17 08:28:39 -07:00
|
|
|
self.color_has_castling_right_unwrapped(self.active_color, wing)
|
2025-06-03 20:25:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
2025-06-17 08:28:39 -07:00
|
|
|
pub fn color_has_castling_right(&self, color: Option<Color>, wing: Wing) -> bool {
|
|
|
|
self.color_has_castling_right_unwrapped(self.unwrap_color(color), wing)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
pub fn color_has_castling_right_unwrapped(&self, color: Color, wing: Wing) -> bool {
|
2025-06-03 20:25:53 -07:00
|
|
|
self.castling_rights.color_has_right(color, wing)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn grant_castling_right(&mut self, color: Color, wing: Wing) {
|
2025-06-05 08:21:32 -07:00
|
|
|
let old_rights = self.castling_rights;
|
2025-06-03 20:25:53 -07:00
|
|
|
self.castling_rights.grant(color, wing);
|
2025-06-05 08:21:32 -07:00
|
|
|
self.update_zobrist_hash_castling_rights(old_rights);
|
2025-06-03 20:25:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn revoke_all_castling_rights(&mut self) {
|
2025-06-05 08:21:32 -07:00
|
|
|
let old_rights = self.castling_rights;
|
2025-06-03 20:25:53 -07:00
|
|
|
self.castling_rights.revoke_all();
|
2025-06-05 08:21:32 -07:00
|
|
|
self.update_zobrist_hash_castling_rights(old_rights);
|
2025-06-03 20:25:53 -07:00
|
|
|
}
|
|
|
|
|
2025-06-17 08:28:39 -07:00
|
|
|
pub fn revoke_castling_right(&mut self, color: Option<Color>, wing: Wing) {
|
|
|
|
let color = self.unwrap_color(color);
|
|
|
|
self.revoke_castling_right_unwrapped(color, wing);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn revoke_castling_right_unwrapped(&mut self, color: Color, wing: Wing) {
|
2025-06-05 08:21:32 -07:00
|
|
|
let old_rights = self.castling_rights;
|
2025-06-03 20:25:53 -07:00
|
|
|
self.castling_rights.revoke(color, wing);
|
2025-06-05 08:21:32 -07:00
|
|
|
self.update_zobrist_hash_castling_rights(old_rights);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_zobrist_hash_castling_rights(&mut self, old_rights: castle::Rights) {
|
|
|
|
let new_rights = self.castling_rights;
|
|
|
|
if old_rights == new_rights {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(zobrist) = self.zobrist_hash.as_mut() {
|
|
|
|
zobrist.update_modifying_castling_rights(new_rights, old_rights);
|
|
|
|
}
|
2025-06-03 20:25:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Board {
|
|
|
|
/// Returns a copy of the current en passant square, if one exists.
|
|
|
|
#[must_use]
|
|
|
|
pub fn en_passant_target(&self) -> Option<Square> {
|
|
|
|
self.en_passant_target
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_en_passant_target(&mut self, square: Square) {
|
|
|
|
self.set_en_passant_target_option(Some(square));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_en_passant_target_option(&mut self, square: Option<Square>) {
|
2025-06-05 08:21:32 -07:00
|
|
|
let old_target = self.en_passant_target;
|
2025-06-03 20:25:53 -07:00
|
|
|
self.en_passant_target = square;
|
2025-06-05 08:21:32 -07:00
|
|
|
self.update_zobrist_hash_en_passant_target(old_target);
|
2025-06-03 20:25:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clear_en_passant_target(&mut self) {
|
2025-06-05 08:21:32 -07:00
|
|
|
let old_target = self.en_passant_target;
|
2025-06-03 20:25:53 -07:00
|
|
|
self.en_passant_target = None;
|
2025-06-05 08:21:32 -07:00
|
|
|
self.update_zobrist_hash_en_passant_target(old_target);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_zobrist_hash_en_passant_target(&mut self, old_target: Option<Square>) {
|
|
|
|
let new_target = self.en_passant_target;
|
|
|
|
|
|
|
|
if old_target == new_target {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(zobrist) = self.zobrist_hash.as_mut() {
|
|
|
|
zobrist.update_setting_en_passant_target(old_target, new_target);
|
|
|
|
}
|
2025-06-03 20:25:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Board {
|
2025-05-08 17:37:51 -07:00
|
|
|
#[must_use]
|
|
|
|
pub fn get_piece(&self, square: Square) -> Option<Piece> {
|
|
|
|
self.pieces.get(square)
|
|
|
|
}
|
|
|
|
|
2025-05-23 18:39:18 -07:00
|
|
|
pub fn find_pieces(&self, piece: Piece) -> BitBoard {
|
|
|
|
self.pieces.find_pieces(piece)
|
|
|
|
}
|
|
|
|
|
2025-05-08 17:37:51 -07:00
|
|
|
/// Place a piece on the board.
|
|
|
|
///
|
|
|
|
/// ## Errors
|
|
|
|
///
|
2025-05-31 15:14:24 -07:00
|
|
|
/// When is called with [`PlacePieceStrategy::PreserveExisting`], and a
|
|
|
|
/// piece already exists on `square`, this method returns a
|
|
|
|
/// [`PlacePieceError::ExistingPiece`] error.
|
2025-05-08 17:37:51 -07:00
|
|
|
///
|
|
|
|
pub fn place_piece(
|
|
|
|
&mut self,
|
|
|
|
piece: Piece,
|
|
|
|
square: Square,
|
|
|
|
strategy: PlacePieceStrategy,
|
2025-06-02 15:54:00 -07:00
|
|
|
) -> Result<Option<Piece>, PlacePieceError> {
|
2025-06-05 08:21:32 -07:00
|
|
|
let place_result = self.pieces.place(piece, square, strategy);
|
|
|
|
|
|
|
|
if let Ok(Some(existing_piece)) = place_result.as_ref() {
|
|
|
|
if let Some(zobrist) = self.zobrist_hash.as_mut() {
|
|
|
|
zobrist.update_removing_piece(square, *existing_piece);
|
|
|
|
zobrist.update_adding_piece(square, piece);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
place_result
|
2025-05-08 17:37:51 -07:00
|
|
|
}
|
2024-04-25 13:28:24 -07:00
|
|
|
|
2025-05-08 17:37:51 -07:00
|
|
|
pub fn remove_piece(&mut self, square: Square) -> Option<Piece> {
|
2025-06-05 08:21:32 -07:00
|
|
|
let removed_piece = self.pieces.remove(square);
|
|
|
|
|
|
|
|
if let Some(piece) = removed_piece {
|
|
|
|
if let Some(zobrist) = self.zobrist_hash.as_mut() {
|
|
|
|
zobrist.update_removing_piece(square, piece);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
removed_piece
|
2025-05-08 17:37:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Board {
|
2025-06-01 17:28:47 -07:00
|
|
|
pub fn iter(&self) -> impl Iterator<Item = (Square, Piece)> {
|
|
|
|
self.pieces.iter()
|
|
|
|
}
|
|
|
|
|
2025-05-19 16:50:30 -07:00
|
|
|
/// A [`BitBoard`] of squares occupied by pieces of all colors.
|
2025-05-16 07:47:28 -07:00
|
|
|
pub fn occupancy(&self) -> BitBoard {
|
|
|
|
self.pieces.occpuancy()
|
|
|
|
}
|
|
|
|
|
2025-05-19 16:50:30 -07:00
|
|
|
/// A [`BitBoard`] of squares that are vacant.
|
2025-05-16 07:47:28 -07:00
|
|
|
pub fn vacancy(&self) -> BitBoard {
|
|
|
|
!self.occupancy()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn friendly_occupancy(&self, color: Color) -> BitBoard {
|
|
|
|
self.pieces.friendly_occupancy(color)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn opposing_occupancy(&self, color: Color) -> BitBoard {
|
|
|
|
self.pieces.opposing_occupancy(color)
|
|
|
|
}
|
2025-05-23 18:39:18 -07:00
|
|
|
|
|
|
|
pub fn enemies(&self, color: Color) -> BitBoard {
|
|
|
|
self.pieces.opposing_occupancy(color)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return a [`BitBoard`] of all pawns of a given color.
|
|
|
|
pub fn pawns(&self, color: Color) -> BitBoard {
|
|
|
|
self.pieces.find_pieces(Piece::pawn(color))
|
|
|
|
}
|
2025-05-25 11:05:10 -07:00
|
|
|
|
|
|
|
pub fn knights(&self, color: Color) -> BitBoard {
|
|
|
|
self.find_pieces(Piece::knight(color))
|
|
|
|
}
|
[bitboard, board, core, moves] Implement SliderMoveGenerator
This generator produces moves for slider pieces: bishops, rooks, and queens. All
of these pieces behave identically, though with different sets of rays that
emanate from the origin square. Claude helped me significantly with the
implementation and unit testing. All the unit tests that took advantage of Claude
for implementation are marked as such with an _ai_claude suffix to the test name.
One unique aspect of this move generator that Claude suggested to me was to use
loop { } instead of a recursive call to next() when the internal iterators expire.
I may try to port this to the other move generators in the future.
To support this move generator, implement a Slider enum in core that represents
one of the three slider pieces.
Add Board::bishops(), Board::rooks() and Board::queens() to return BitBoards of
those pieces. These are analogous to the pawns() and knights() methods that return
their corresponding pieces.
Also in the board create, replace the separate sight method implementations with
a macro. These are all the same, but with a different sight method called under
the hood.
Finally, derive Clone and Debug for the bit_scanner types.
2025-05-26 17:41:43 -07:00
|
|
|
|
|
|
|
pub fn bishops(&self, color: Color) -> BitBoard {
|
|
|
|
self.find_pieces(Piece::bishop(color))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn rooks(&self, color: Color) -> BitBoard {
|
|
|
|
self.find_pieces(Piece::rook(color))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn queens(&self, color: Color) -> BitBoard {
|
|
|
|
self.find_pieces(Piece::queen(color))
|
|
|
|
}
|
2025-05-16 07:47:28 -07:00
|
|
|
|
[board, moves, position] Implement KingMoveGenerator
Implement a move generator that emits moves for the king(s) of a particular color.
There will, of course, only ever be one king per side in any valid board, but
this iterator can (in theory) handle multiple kings on the board. This iterator
is almost entirely copypasta of the SliderMoveGenerator. The major difference is
castling.
Castle moves are emitted by a helper CastleIterator type. This struct collects
information about whether the given color can castle on each side of the board
and then emits moves for each side, if indicated.
Do some light refactoring of the castle-related methods on Board to accommodate
this move generator. Remove the dependency on internal state and rename the
"can_castle" method to color_can_castle.
In order to facilitate creating castling moves without relying on Board, remove
the origin and target squares from the encoded castling move. Code that makes
a castling move already looks up castling parameters to move the king and rook to
the right squares, so encoding those squares was redundant. This change
necessitated some updates to position.
Lastly, bring in a handful of unit tests courtesy of Claude. Apparently, it's my
new best coding friend. 🙃
2025-05-26 23:37:33 -07:00
|
|
|
pub fn kings(&self, color: Color) -> BitBoard {
|
|
|
|
self.find_pieces(Piece::king(color))
|
2025-05-19 16:50:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-05 08:21:32 -07:00
|
|
|
impl Board {
|
|
|
|
pub fn zobrist_hash(&self) -> Option<u64> {
|
|
|
|
self.zobrist_hash.as_ref().map(ZobristHash::hash_value)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn recompute_zobrist_hash(&mut self) {
|
|
|
|
// Avoid overlapping borrows when borrowing zobrist_hash.as_mut() and
|
|
|
|
// then also borrowing self to update the board hash by computing the
|
|
|
|
// hash with the static function first, and then setting the hash value
|
|
|
|
// on the zobrist instance. Unfortuantely this requires unwrapping
|
|
|
|
// self.zobrist_hash twice. C'est la vie.
|
|
|
|
|
|
|
|
let new_hash = self.zobrist_hash.as_ref().map(|zobrist| {
|
|
|
|
let state = zobrist.state();
|
|
|
|
ZobristHash::compute_board_hash(self, state.as_ref())
|
|
|
|
});
|
|
|
|
|
|
|
|
if let (Some(new_hash), Some(zobrist)) = (new_hash, self.zobrist_hash.as_mut()) {
|
|
|
|
zobrist.set_hash_value(new_hash);
|
|
|
|
}
|
|
|
|
}
|
2025-06-08 16:49:55 -07:00
|
|
|
|
|
|
|
pub fn zobrist_state(&self) -> Option<Arc<ZobristState>> {
|
|
|
|
self.zobrist_hash.as_ref().map(ZobristHash::state)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_zobrist_state(&mut self, state: Arc<ZobristState>) {
|
|
|
|
self.zobrist_hash = Some(ZobristHash::new(state));
|
|
|
|
self.recompute_zobrist_hash();
|
|
|
|
}
|
2025-06-05 08:21:32 -07:00
|
|
|
}
|
|
|
|
|
2025-05-16 07:47:28 -07:00
|
|
|
impl Board {
|
2025-05-08 17:37:51 -07:00
|
|
|
pub fn display(&self) -> DiagramFormatter<'_> {
|
|
|
|
DiagramFormatter::new(self)
|
2024-04-25 13:28:24 -07:00
|
|
|
}
|
2025-05-08 17:37:51 -07:00
|
|
|
}
|
2024-04-25 13:28:24 -07:00
|
|
|
|
2025-05-23 18:37:13 -07:00
|
|
|
impl Board {
|
|
|
|
#[must_use]
|
|
|
|
pub fn unwrap_color(&self, color: Option<Color>) -> Color {
|
|
|
|
color.unwrap_or(self.active_color)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-25 13:28:24 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::test_board;
|
2025-06-06 21:45:48 -07:00
|
|
|
use chessfriend_core::{piece, random::RandomNumberGenerator};
|
2024-04-25 13:28:24 -07:00
|
|
|
|
|
|
|
#[test]
|
2025-05-08 17:37:51 -07:00
|
|
|
fn get_piece_on_square() {
|
|
|
|
let board = test_board![
|
2024-04-25 13:28:24 -07:00
|
|
|
Black Bishop on F7,
|
|
|
|
];
|
|
|
|
|
2025-05-08 17:37:51 -07:00
|
|
|
assert_eq!(board.get_piece(Square::F7), Some(piece!(Black Bishop)));
|
2024-04-25 13:28:24 -07:00
|
|
|
}
|
2025-06-06 21:45:48 -07:00
|
|
|
|
|
|
|
// MARK: - Zobrist Hashing
|
|
|
|
|
|
|
|
fn test_state() -> ZobristState {
|
|
|
|
let mut rng = RandomNumberGenerator::default();
|
|
|
|
ZobristState::new(&mut rng)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn zobrist_hash_set_for_empty_board() {
|
|
|
|
let state = Arc::new(test_state());
|
|
|
|
let board = Board::empty(Some(state.clone()));
|
|
|
|
let hash = board.zobrist_hash();
|
|
|
|
assert_eq!(hash, Some(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn zobrist_hash_set_for_starting_position_board() {
|
|
|
|
let state = Arc::new(test_state());
|
|
|
|
let board = Board::starting(Some(state.clone()));
|
|
|
|
let hash = board.zobrist_hash();
|
|
|
|
assert!(hash.is_some());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn zobrist_hash_updated_when_changing_active_color() {
|
|
|
|
let state = Arc::new(test_state());
|
|
|
|
|
|
|
|
let mut board = Board::empty(Some(state.clone()));
|
|
|
|
board.set_active_color(Color::Black);
|
|
|
|
|
|
|
|
// Just verify that the value is real and has changed. The actual value
|
|
|
|
// computation is covered by the tests in zobrist.rs.
|
|
|
|
let hash = board.zobrist_hash();
|
|
|
|
assert!(hash.is_some());
|
|
|
|
assert_ne!(hash, Some(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn zobrist_hash_updated_when_changing_en_passant_target() {
|
|
|
|
let state = Arc::new(test_state());
|
|
|
|
|
|
|
|
let mut board = Board::empty(Some(state.clone()));
|
|
|
|
board.set_en_passant_target(Square::C3);
|
|
|
|
|
|
|
|
// Just verify that the value is real and has changed. The actual value
|
|
|
|
// computation is covered by the tests in zobrist.rs.
|
|
|
|
let hash = board.zobrist_hash();
|
|
|
|
assert!(hash.is_some());
|
|
|
|
assert_ne!(hash, Some(0));
|
|
|
|
}
|
2024-04-25 13:28:24 -07:00
|
|
|
}
|