#![allow(clippy::inline_always)]
use crate::types::Bitboard;
use crate::types::{Color, Piece, PieceType, Square};
#[derive(Clone, Copy, Debug, Default)]
pub struct BitboardSet {
by_piece: [Bitboard; PieceType::COUNT],
by_color: [Bitboard; Color::COUNT],
occupied: Bitboard,
golds_cache: Bitboard,
hdk_cache: Bitboard,
bishop_horse_cache: Bitboard,
rook_dragon_cache: Bitboard,
silver_hdk_cache: Bitboard,
golds_hdk_cache: Bitboard,
}
impl BitboardSet {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[inline(always)]
pub(crate) fn refresh_derived(&mut self) {
let king = self.by_piece[PieceType::KING.to_index()];
let horse = self.by_piece[PieceType::HORSE.to_index()];
let dragon = self.by_piece[PieceType::DRAGON.to_index()];
self.hdk_cache = king | horse | dragon;
self.golds_cache = self.by_piece[PieceType::GOLD.to_index()]
| self.by_piece[PieceType::PRO_PAWN.to_index()]
| self.by_piece[PieceType::PRO_LANCE.to_index()]
| self.by_piece[PieceType::PRO_KNIGHT.to_index()]
| self.by_piece[PieceType::PRO_SILVER.to_index()];
self.bishop_horse_cache = self.by_piece[PieceType::BISHOP.to_index()] | horse;
self.rook_dragon_cache = self.by_piece[PieceType::ROOK.to_index()] | dragon;
self.silver_hdk_cache = self.by_piece[PieceType::SILVER.to_index()] | self.hdk_cache;
self.golds_hdk_cache = self.golds_cache | self.hdk_cache;
}
#[inline(always)]
pub(crate) fn set_piece_no_refresh(&mut self, sq: Square, piece_type: PieceType, color: Color) {
if piece_type == PieceType::NONE {
return;
}
self.by_piece[piece_type.to_index()].set(sq);
self.by_color[color.to_index()].set(sq);
self.occupied.set(sq);
}
#[inline(always)]
pub(crate) fn clear_piece_no_refresh(
&mut self,
sq: Square,
piece_type: PieceType,
color: Color,
) {
if piece_type == PieceType::NONE {
return;
}
self.by_piece[piece_type.to_index()].clear(sq);
self.by_color[color.to_index()].clear(sq);
self.occupied.clear(sq);
}
#[inline(always)]
pub(crate) unsafe fn set_piece_no_refresh_unchecked(
&mut self,
sq: Square,
piece_type: PieceType,
color: Color,
) {
unsafe {
debug_assert!(piece_type != PieceType::NONE);
debug_assert!(sq.is_on_board());
self.by_piece.get_unchecked_mut(piece_type.to_index()).set(sq);
self.by_color.get_unchecked_mut(color.to_index()).set(sq);
self.occupied.set(sq);
}
}
#[inline(always)]
pub(crate) unsafe fn clear_piece_no_refresh_unchecked(
&mut self,
sq: Square,
piece_type: PieceType,
color: Color,
) {
unsafe {
debug_assert!(piece_type != PieceType::NONE);
debug_assert!(sq.is_on_board());
self.by_piece.get_unchecked_mut(piece_type.to_index()).clear(sq);
self.by_color.get_unchecked_mut(color.to_index()).clear(sq);
self.occupied.clear(sq);
}
}
#[inline(always)]
pub(crate) fn set_no_refresh(&mut self, sq: Square, piece: Piece) {
if piece.is_empty() {
return;
}
self.set_piece_no_refresh(sq, piece.piece_type(), piece.color());
}
#[inline(always)]
pub(crate) fn clear_no_refresh(&mut self, sq: Square, piece: Piece) {
if piece.is_empty() {
return;
}
self.clear_piece_no_refresh(sq, piece.piece_type(), piece.color());
}
#[inline(always)]
pub(crate) unsafe fn set_no_refresh_unchecked(&mut self, sq: Square, piece: Piece) {
unsafe {
debug_assert!(!piece.is_empty());
self.set_piece_no_refresh_unchecked(sq, piece.piece_type(), piece.color());
}
}
#[inline(always)]
pub(crate) unsafe fn clear_no_refresh_unchecked(&mut self, sq: Square, piece: Piece) {
unsafe {
debug_assert!(!piece.is_empty());
self.clear_piece_no_refresh_unchecked(sq, piece.piece_type(), piece.color());
}
}
pub fn set_piece(&mut self, sq: Square, piece_type: PieceType, color: Color) {
self.set_piece_no_refresh(sq, piece_type, color);
self.refresh_derived();
}
pub fn clear_piece(&mut self, sq: Square, piece_type: PieceType, color: Color) {
self.clear_piece_no_refresh(sq, piece_type, color);
self.refresh_derived();
}
pub fn move_piece(&mut self, from: Square, to: Square, piece_type: PieceType, color: Color) {
let piece_bb = &mut self.by_piece[piece_type.to_index()];
piece_bb.clear(from);
piece_bb.set(to);
let color_bb = &mut self.by_color[color.to_index()];
color_bb.clear(from);
color_bb.set(to);
self.occupied.clear(from);
self.occupied.set(to);
self.refresh_derived();
}
#[inline]
#[must_use]
pub const fn pieces(&self, piece_type: PieceType) -> Bitboard {
self.by_piece[piece_type.to_index()]
}
#[inline]
#[must_use]
pub const fn color_pieces(&self, color: Color) -> Bitboard {
self.by_color[color.to_index()]
}
#[inline]
#[must_use]
pub fn pieces_for(&self, piece_type: PieceType, color: Color) -> Bitboard {
self.pieces(piece_type).and(self.color_pieces(color))
}
#[inline]
#[must_use]
pub const fn golds(&self) -> Bitboard {
self.golds_cache
}
#[inline]
#[must_use]
pub const fn horse_dragon_king(&self) -> Bitboard {
self.hdk_cache
}
#[inline]
#[must_use]
pub const fn bishop_horse(&self) -> Bitboard {
self.bishop_horse_cache
}
#[inline]
#[must_use]
pub const fn rook_dragon(&self) -> Bitboard {
self.rook_dragon_cache
}
#[inline]
#[must_use]
pub const fn silver_hdk(&self) -> Bitboard {
self.silver_hdk_cache
}
#[inline]
#[must_use]
pub const fn golds_hdk(&self) -> Bitboard {
self.golds_hdk_cache
}
#[inline]
#[must_use]
pub const fn occupied(&self) -> Bitboard {
self.occupied
}
pub fn set(&mut self, sq: Square, piece: Piece) {
self.set_no_refresh(sq, piece);
self.refresh_derived();
}
pub fn clear(&mut self, sq: Square, piece: Piece) {
self.clear_no_refresh(sq, piece);
self.refresh_derived();
}
#[inline]
#[must_use]
pub const fn empties(&self) -> Bitboard {
self.occupied.not()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{Color, PieceType, SQ_16, SQ_17, SQ_82};
#[test]
fn test_bitboard_set_operations() {
let mut bbs = BitboardSet::new();
bbs.set_piece(SQ_17, PieceType::PAWN, Color::BLACK);
assert!(bbs.pieces(PieceType::PAWN).test(SQ_17));
assert!(bbs.color_pieces(Color::BLACK).test(SQ_17));
assert!(bbs.occupied().test(SQ_17));
bbs.set_piece(SQ_82, PieceType::ROOK, Color::WHITE);
assert!(bbs.pieces(PieceType::ROOK).test(SQ_82));
assert!(bbs.color_pieces(Color::WHITE).test(SQ_82));
let black_pawns = bbs.pieces_for(PieceType::PAWN, Color::BLACK);
assert!(black_pawns.test(SQ_17));
assert!(!black_pawns.test(SQ_82));
bbs.move_piece(SQ_17, SQ_16, PieceType::PAWN, Color::BLACK);
assert!(!bbs.pieces(PieceType::PAWN).test(SQ_17));
assert!(bbs.pieces(PieceType::PAWN).test(SQ_16));
bbs.clear_piece(SQ_82, PieceType::ROOK, Color::WHITE);
assert!(!bbs.pieces(PieceType::ROOK).test(SQ_82));
assert!(!bbs.occupied().test(SQ_82));
}
#[test]
fn test_composite_caches() {
let mut bbs = BitboardSet::new();
bbs.set_piece(SQ_17, PieceType::GOLD, Color::BLACK);
assert!(bbs.golds().test(SQ_17));
bbs.set_piece(SQ_16, PieceType::PRO_PAWN, Color::BLACK);
assert!(bbs.golds().test(SQ_16));
bbs.set_piece(SQ_82, PieceType::HORSE, Color::WHITE);
assert!(bbs.horse_dragon_king().test(SQ_82));
assert!(bbs.bishop_horse().test(SQ_82));
assert!(!bbs.rook_dragon().test(SQ_82));
let sq_11 = Square::from_file_rank(crate::types::File::FILE_1, crate::types::Rank::RANK_1);
bbs.set_piece(sq_11, PieceType::DRAGON, Color::WHITE);
assert!(bbs.horse_dragon_king().test(sq_11));
assert!(bbs.rook_dragon().test(sq_11));
assert!(!bbs.bishop_horse().test(sq_11));
bbs.clear_piece(SQ_82, PieceType::HORSE, Color::WHITE);
assert!(!bbs.horse_dragon_king().test(SQ_82));
assert!(!bbs.bishop_horse().test(SQ_82));
}
}