Compare commits

...

2 commits

Author SHA1 Message Date
182bf81126 [board] Fix a counter underflow in the piece set
During perft runs, the PieceSet counter would occasionally underflow, causing
the whole program to crash. This is because, when building a Board from a list
of bitboards, Counts::increment() was only being called once, even when the
bitboard had more than one piece in it. Fix the bug by incrementing during the
loop that sets up the mailbox.

Additionally, refactor the increment() and decrement() methods to be a little
more succinct.
2025-08-15 16:15:09 -07:00
3d73760146 [bitboard, board] Remove BitBoard::empty() and BitBoard::full()
These have been deprecated for a while. Clean up the remaining uses and remove
the methods from BitBoard.
2025-08-15 16:14:34 -07:00
6 changed files with 35 additions and 45 deletions

View file

@ -46,16 +46,6 @@ impl BitBoard {
pub const EMPTY: BitBoard = BitBoard(u64::MIN);
pub const FULL: BitBoard = BitBoard(u64::MAX);
#[deprecated(note = "Use BitBoard::EMPTY instead")]
pub const fn empty() -> BitBoard {
Self::EMPTY
}
#[deprecated(note = "Use BitBoard::FULL instead")]
pub const fn full() -> BitBoard {
Self::FULL
}
pub const fn new(bits: u64) -> BitBoard {
BitBoard(bits)
}
@ -109,7 +99,7 @@ impl BitBoard {
///
/// ```
/// use chessfriend_bitboard::BitBoard;
/// assert!(BitBoard::empty().is_empty());
/// assert!(BitBoard::EMPTY.is_empty());
/// assert!(!BitBoard::full().is_empty());
/// assert!(!BitBoard::new(0b1000).is_empty());
/// ```
@ -125,7 +115,7 @@ impl BitBoard {
///
/// ```
/// use chessfriend_bitboard::BitBoard;
/// assert!(!BitBoard::empty().is_populated());
/// assert!(!BitBoard::EMPTY.is_populated());
/// assert!(BitBoard::full().is_populated());
/// assert!(BitBoard::new(0b1).is_populated());
/// ```
@ -564,8 +554,8 @@ mod tests {
let b = bitboard![B5 G7 H3];
assert_eq!(a ^ b, bitboard![B5 C5 H3]);
assert_eq!(a ^ BitBoard::empty(), a);
assert_eq!(BitBoard::empty() ^ BitBoard::empty(), BitBoard::empty());
assert_eq!(a ^ BitBoard::EMPTY, a);
assert_eq!(BitBoard::EMPTY ^ BitBoard::EMPTY, BitBoard::EMPTY);
}
#[test]

View file

@ -110,14 +110,14 @@ pub(super) struct MoveLibrary {
impl MoveLibrary {
const fn new() -> MoveLibrary {
MoveLibrary {
rays: [[BitBoard::empty(); Direction::NUM]; Square::NUM],
pawn_attacks: [[BitBoard::empty(); Square::NUM]; Color::NUM],
pawn_pushes: [[BitBoard::empty(); Square::NUM]; Color::NUM],
knight_moves: [BitBoard::empty(); Square::NUM],
bishop_moves: [BitBoard::empty(); Square::NUM],
rook_moves: [BitBoard::empty(); Square::NUM],
queen_moves: [BitBoard::empty(); Square::NUM],
king_moves: [BitBoard::empty(); Square::NUM],
rays: [[BitBoard::EMPTY; Direction::NUM]; Square::NUM],
pawn_attacks: [[BitBoard::EMPTY; Square::NUM]; Color::NUM],
pawn_pushes: [[BitBoard::EMPTY; Square::NUM]; Color::NUM],
knight_moves: [BitBoard::EMPTY; Square::NUM],
bishop_moves: [BitBoard::EMPTY; Square::NUM],
rook_moves: [BitBoard::EMPTY; Square::NUM],
queen_moves: [BitBoard::EMPTY; Square::NUM],
king_moves: [BitBoard::EMPTY; Square::NUM],
}
}
@ -238,7 +238,7 @@ impl MoveLibrary {
}
fn _generate_ray(sq: BitBoard, shift: fn(&BitBoard) -> BitBoard) -> BitBoard {
let mut ray = BitBoard::empty();
let mut ray = BitBoard::EMPTY;
let mut iter = shift(&sq);
while !iter.is_empty() {

View file

@ -41,7 +41,7 @@ impl Movement for Piece {
let parameters = Board::castling_parameters(Wing::KingSide, color);
parameters.target.king.into()
} else {
BitBoard::empty()
BitBoard::EMPTY
};
let queenside_target_square = if board
@ -51,7 +51,7 @@ impl Movement for Piece {
let parameters = Board::castling_parameters(Wing::QueenSide, color);
parameters.target.king.into()
} else {
BitBoard::empty()
BitBoard::EMPTY
};
self.sight(square, board) | kingside_target_square | queenside_target_square
@ -99,11 +99,11 @@ mod tests {
#[test]
fn white_pushes_empty_board() {
assert_eq!(
pawn_pushes(Square::E4.into(), Color::White, BitBoard::empty()),
pawn_pushes(Square::E4.into(), Color::White, BitBoard::EMPTY),
bitboard![E5]
);
assert_eq!(
pawn_pushes(Square::E2.into(), Color::White, BitBoard::empty()),
pawn_pushes(Square::E2.into(), Color::White, BitBoard::EMPTY),
bitboard![E3 E4]
);
}
@ -111,11 +111,11 @@ mod tests {
#[test]
fn black_pawn_empty_board() {
assert_eq!(
pawn_pushes(Square::A4.into(), Color::Black, BitBoard::empty()),
pawn_pushes(Square::A4.into(), Color::Black, BitBoard::EMPTY),
bitboard![A3]
);
assert_eq!(
pawn_pushes(Square::B7.into(), Color::Black, BitBoard::empty()),
pawn_pushes(Square::B7.into(), Color::Black, BitBoard::EMPTY),
bitboard![B6 B5]
);
}
@ -124,7 +124,7 @@ mod tests {
fn white_pushes_blocker() {
assert_eq!(
pawn_pushes(Square::C5.into(), Color::White, bitboard![C6]),
BitBoard::empty()
BitBoard::EMPTY
);
assert_eq!(
pawn_pushes(Square::D2.into(), Color::White, bitboard![D4]),
@ -132,7 +132,7 @@ mod tests {
);
assert_eq!(
pawn_pushes(Square::D2.into(), Color::White, bitboard![D3]),
BitBoard::empty()
BitBoard::EMPTY
);
}
@ -140,7 +140,7 @@ mod tests {
fn black_pushes_blocker() {
assert_eq!(
pawn_pushes(Square::C5.into(), Color::Black, bitboard![C4]),
BitBoard::empty()
BitBoard::EMPTY
);
assert_eq!(
pawn_pushes(Square::D7.into(), Color::Black, bitboard![D5]),
@ -148,7 +148,7 @@ mod tests {
);
assert_eq!(
pawn_pushes(Square::D7.into(), Color::Black, bitboard![D6]),
BitBoard::empty()
BitBoard::EMPTY
);
}
}

View file

@ -51,11 +51,10 @@ impl PieceSet {
color_occupancy[color_index] |= bitboard;
shape_occupancy[shape_index] |= bitboard;
counts.increment(color, shape);
for square in bitboard.occupied_squares(&IterationDirection::default()) {
let piece = Piece::new(color, shape);
mailbox.set(piece, square);
counts.increment(color, shape);
}
}
}

View file

@ -17,20 +17,21 @@ impl Counts {
const SQUARE_NUM: u8 = Square::NUM as u8;
let updated_value = self.0[color as usize][shape as usize] + 1;
if updated_value <= SQUARE_NUM {
self.0[color as usize][shape as usize] = updated_value;
} else {
unreachable!("piece count for {color} {shape} overflowed");
if updated_value > SQUARE_NUM {
let shape_name = shape.name();
panic!("piece count for {color} {shape_name} overflowed");
}
self.0[color as usize][shape as usize] = updated_value;
}
pub fn decrement(&mut self, color: Color, shape: Shape) {
let count = self.0[color as usize][shape as usize];
if let Some(updated_count) = count.checked_sub(1) {
self.0[color as usize][shape as usize] = updated_count;
} else {
unreachable!("piece count for {color} {shape} underflowed");
}
let updated_count = count.checked_sub(1).unwrap_or_else(|| {
let shape_name = shape.name();
panic!("piece count for {color} {shape_name} should not underflow");
});
self.0[color as usize][shape as usize] = updated_count;
}
#[cfg(test)]

View file

@ -305,7 +305,7 @@ mod tests {
let piece = piece!(White Pawn);
let sight = piece.sight(Square::E4, &pos);
assert_eq!(sight, BitBoard::empty());
assert_eq!(sight, BitBoard::EMPTY);
}
#[test]