use super::Position;
use crate::board::state_info::{
CHECK_SQ_BISHOP, CHECK_SQ_DRAGON, CHECK_SQ_GOLD, CHECK_SQ_HORSE, CHECK_SQ_KNIGHT,
CHECK_SQ_LANCE, CHECK_SQ_PAWN, CHECK_SQ_ROOK, CHECK_SQ_SILVER, CheckSquares, StateHot,
StateInfo, TacticalCache,
};
use crate::types::Bitboard;
use crate::types::{Color, PieceType, Square};
#[derive(Clone, Copy)]
pub struct StateCacheView<'a> {
tactical: &'a TacticalCache,
}
impl<'a> StateCacheView<'a> {
#[inline(always)]
pub(crate) const fn from_hot(hot: &'a StateHot) -> Self {
Self { tactical: hot.tactical_cache() }
}
#[must_use]
#[inline(always)]
pub fn checkers(self) -> Bitboard {
self.tactical.checkers()
}
#[must_use]
#[inline(always)]
pub fn check_squares(self) -> &'a CheckSquares {
&self.tactical.check_squares
}
#[must_use]
#[inline(always)]
pub fn check_square(self, piece_type: PieceType) -> Bitboard {
self.tactical.check_square(piece_type)
}
#[must_use]
#[inline(always)]
pub fn blockers_for_king(self, color: Color) -> Bitboard {
self.tactical.blockers_for_king(color)
}
#[must_use]
#[inline(always)]
pub fn pinners(self, color: Color) -> Bitboard {
self.tactical.pinners(color)
}
}
impl Position {
#[must_use]
#[inline(always)]
pub fn current_state_cache(&self) -> StateCacheView<'_> {
StateCacheView::from_hot(self.current_hot())
}
#[must_use]
#[inline]
pub fn checkers(&self) -> Bitboard {
self.current_state_cache().checkers()
}
#[must_use]
#[inline]
pub fn check_squares(&self) -> &CheckSquares {
self.current_state_cache().check_squares()
}
#[cfg(test)]
#[must_use]
#[inline]
pub(crate) fn check_squares_cache(&self) -> &CheckSquares {
self.check_squares()
}
#[must_use]
#[inline]
pub fn check_square(&self, piece_type: PieceType) -> Bitboard {
self.current_state_cache().check_square(piece_type)
}
#[must_use]
pub fn pinned_pieces(&self, c: Color) -> Bitboard {
self.blockers_for_king(c).and(self.bitboards.color_pieces(c))
}
#[must_use]
pub fn pinned_pieces_avoid(&self, c: Color, avoid: Square) -> Bitboard {
use crate::board::attack_tables::{
bishop_step_attacks, lance_step_attacks, rook_step_attacks,
};
let king_sq = self.king_square(c);
if king_sq.is_none() {
return Bitboard::EMPTY;
}
let them = c.flip();
let rook_like = self.bitboards().pieces_for(PieceType::ROOK, them)
| self.bitboards().pieces_for(PieceType::DRAGON, them);
let bishop_like = self.bitboards().pieces_for(PieceType::BISHOP, them)
| self.bitboards().pieces_for(PieceType::HORSE, them);
let lance_like = self.bitboards().pieces_for(PieceType::LANCE, them);
let avoid_bb =
if avoid.is_none() { Bitboard::ALL } else { Bitboard::from_square(avoid).not() };
let rook_snipers = rook_like & rook_step_attacks(king_sq) & avoid_bb;
let bishop_snipers = bishop_like & bishop_step_attacks(king_sq) & avoid_bb;
let lance_snipers = lance_like & lance_step_attacks(king_sq, c) & avoid_bb;
let snipers = rook_snipers | bishop_snipers | lance_snipers;
let occupancy = self.bitboards().occupied() & avoid_bb;
let mut result = Bitboard::EMPTY;
for sniper_sq in &snipers {
let between = Bitboard::between(sniper_sq, king_sq) & occupancy;
if between.count() == 1 {
result |= between & self.bitboards.color_pieces(c);
}
}
result
}
#[must_use]
#[inline]
pub fn blockers_for_king(&self, c: Color) -> Bitboard {
self.current_state_cache().blockers_for_king(c)
}
#[must_use]
#[inline]
pub fn pinners(&self, c: Color) -> Bitboard {
self.current_state_cache().pinners(c)
}
#[must_use]
pub fn is_in_check(&self) -> bool {
!self.checkers().is_empty()
}
#[must_use]
pub fn is_discovery_check_on_king(&self, c: Color, mv: crate::types::Move32) -> bool {
self.blockers_for_king(c).test(mv.to_move().from_sq())
}
#[inline]
pub(crate) fn compute_check_squares(&self) -> CheckSquares {
match self.turn() {
Color::BLACK => self.compute_check_squares_for::<true>(),
Color::WHITE => self.compute_check_squares_for::<false>(),
}
}
#[inline]
pub(crate) fn compute_check_squares_for<const BLACK_TURN: bool>(&self) -> CheckSquares {
use crate::board::attack_tables::{
GOLD_ATTACKS, KING_ATTACKS, KNIGHT_ATTACKS, PAWN_ATTACKS, SILVER_ATTACKS,
};
use crate::board::attack_tables::{bishop_attacks, lance_step_attacks, rook_attacks};
let mut check_squares = CheckSquares::EMPTY;
let them = if BLACK_TURN { Color::WHITE } else { Color::BLACK };
let ksq = self.king_square(them);
if ksq.is_none() {
return check_squares;
}
let occupied = self.bitboards().occupied();
let pawn = PAWN_ATTACKS[ksq][them.to_index()];
let knight = KNIGHT_ATTACKS[ksq][them.to_index()];
let silver = SILVER_ATTACKS[ksq][them.to_index()];
let gold = GOLD_ATTACKS[ksq][them.to_index()];
let bishop = bishop_attacks(ksq, occupied);
let rook = rook_attacks(ksq, occupied);
let lance = rook & lance_step_attacks(ksq, them);
let king = KING_ATTACKS[ksq];
let horse = bishop | king;
let dragon = rook | king;
check_squares.set(CHECK_SQ_PAWN, pawn);
check_squares.set(CHECK_SQ_LANCE, lance);
check_squares.set(CHECK_SQ_KNIGHT, knight);
check_squares.set(CHECK_SQ_SILVER, silver);
check_squares.set(CHECK_SQ_BISHOP, bishop);
check_squares.set(CHECK_SQ_ROOK, rook);
check_squares.set(CHECK_SQ_GOLD, gold);
check_squares.set(CHECK_SQ_HORSE, horse);
check_squares.set(CHECK_SQ_DRAGON, dragon);
check_squares
}
pub(super) fn compute_checkers_for(&self, color: Color) -> Bitboard {
let us = color;
let them = us.flip();
let occupied = self.bitboards.occupied();
let king_sq = self.king_square(us);
if king_sq.is_none() {
return Bitboard::EMPTY;
}
self.attackers_to_color_fast(them, king_sq, occupied)
}
pub(super) fn recompute_caches(&mut self) {
let mut state = StateInfo::default();
self.compute_caches_for_state(&mut state);
self.sync_caches_from_state(&state);
}
pub(crate) fn compute_caches_for_state(&self, state: &mut StateInfo) {
let (black_blockers, black_pinners) = self.compute_slider_info_for::<true>();
let (white_blockers, white_pinners) = self.compute_slider_info_for::<false>();
let (checkers, check_squares) = match self.turn() {
Color::BLACK => {
(self.compute_checkers_for(Color::BLACK), self.compute_check_squares_for::<true>())
}
Color::WHITE => {
(self.compute_checkers_for(Color::WHITE), self.compute_check_squares_for::<false>())
}
};
state.replace_tactical_cache(TacticalCache::new(
checkers,
[black_pinners, white_pinners],
[black_blockers, white_blockers],
check_squares,
));
self.write_partial_keys_to_state(state);
}
pub(crate) fn sync_caches_from_state(&mut self, state: &StateInfo) {
self.state_stack.sync_caches_from_state(self.st_index, state);
}
}