use crate::bitboard::{
Bitboard, bishop_effect, gold_effect, king_effect, knight_effect, lance_effect, pawn_effect,
rook_effect, silver_effect,
};
use crate::mate::cross45_step_effect;
use crate::types::{Color, File, PieceType, Rank, Square};
use std::sync::LazyLock;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PieceTypeCheck {
PawnWithNoPro = 0,
PawnWithPro = 1,
Lance = 2,
Knight = 3,
Silver = 4,
Gold = 5,
Bishop = 6,
Rook = 7,
ProBishop = 8,
ProRook = 9,
NonSlider = 10,
}
impl PieceTypeCheck {
pub const NUM: usize = 11;
#[inline]
pub const fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(Self::PawnWithNoPro),
1 => Some(Self::PawnWithPro),
2 => Some(Self::Lance),
3 => Some(Self::Knight),
4 => Some(Self::Silver),
5 => Some(Self::Gold),
6 => Some(Self::Bishop),
7 => Some(Self::Rook),
8 => Some(Self::ProBishop),
9 => Some(Self::ProRook),
10 => Some(Self::NonSlider),
_ => None,
}
}
}
pub static CHECK_CAND_BB: LazyLock<[[[Bitboard; 2]; PieceTypeCheck::NUM]; 81]> =
LazyLock::new(init_check_cand_bb);
pub static CHECK_AROUND_BB: LazyLock<[[[Bitboard; 2]; PieceType::NUM + 1]; 81]> =
LazyLock::new(init_check_around_bb);
pub static NEXT_SQUARE: LazyLock<[[Option<Square>; 81]; 81]> = LazyLock::new(init_next_square);
#[inline]
pub fn check_cand_bb(us: Color, pc: PieceTypeCheck, sq_king: Square) -> Bitboard {
CHECK_CAND_BB[sq_king.index()][pc as usize][us.index()]
}
#[inline]
pub fn check_around_bb(us: Color, pt: PieceType, sq_king: Square) -> Bitboard {
CHECK_AROUND_BB[sq_king.index()][pt.index()][us.index()]
}
fn init_check_cand_bb() -> [[[Bitboard; 2]; PieceTypeCheck::NUM]; 81] {
let mut table = [[[Bitboard::EMPTY; 2]; PieceTypeCheck::NUM]; 81];
for sq_king in Square::all() {
for &us in &[Color::Black, Color::White] {
let idx = sq_king.index();
let c = us.index();
let enemy_field = crate::mate::enemy_field(us);
let mut bb_no_pro = Bitboard::EMPTY;
let mut step = pawn_effect(!us, sq_king) & !enemy_field;
if step.is_not_empty() {
let to = step.pop();
bb_no_pro = pawn_effect(!us, to);
}
table[idx][PieceTypeCheck::PawnWithNoPro as usize][c] = bb_no_pro;
let mut bb_pro = Bitboard::EMPTY;
let mut promo_targets = gold_effect(!us, sq_king) & enemy_field;
while promo_targets.is_not_empty() {
let to = promo_targets.pop();
bb_pro |= pawn_effect(!us, to);
}
table[idx][PieceTypeCheck::PawnWithPro as usize][c] = bb_pro;
let mut lance_bb = lance_effect(!us, sq_king, Bitboard::EMPTY);
if enemy_field.contains(sq_king) {
if let Some(s) = sq_king.offset(Square::DELTA_R) {
lance_bb |= lance_effect(!us, s, Bitboard::EMPTY);
}
if let Some(s) = sq_king.offset(Square::DELTA_L) {
lance_bb |= lance_effect(!us, s, Bitboard::EMPTY);
}
}
table[idx][PieceTypeCheck::Lance as usize][c] = lance_bb;
let mut knight_bb = Bitboard::EMPTY;
let mut tmp = knight_effect(!us, sq_king);
while tmp.is_not_empty() {
let to = tmp.pop();
knight_bb |= knight_effect(!us, to);
}
let mut promo = gold_effect(!us, sq_king) & enemy_field;
while promo.is_not_empty() {
let to = promo.pop();
knight_bb |= knight_effect(!us, to);
}
table[idx][PieceTypeCheck::Knight as usize][c] = knight_bb;
let mut silver_bb = Bitboard::EMPTY;
let mut tmp = silver_effect(!us, sq_king);
while tmp.is_not_empty() {
let to = tmp.pop();
silver_bb |= silver_effect(!us, to);
}
let mut promo_gold = gold_effect(!us, sq_king) & enemy_field;
while promo_gold.is_not_empty() {
let to = promo_gold.pop();
silver_bb |= silver_effect(!us, to);
}
let special_rank = if us == Color::Black {
Rank::Rank4
} else {
Rank::Rank6
};
if sq_king.rank() == special_rank {
let r3 = if us == Color::Black {
Rank::Rank3
} else {
Rank::Rank7
};
let base = Square::new(sq_king.file(), r3);
silver_bb |= Bitboard::from_square(base);
silver_bb |= cross45_step_effect(base);
let file_idx = base.file().index() as i16;
if let Some(file_r) = File::from_u8((file_idx + 1) as u8) {
silver_bb |= Bitboard::from_square(Square::new(file_r, r3));
}
if file_idx > 0
&& let Some(file_l) = File::from_u8((file_idx - 1) as u8)
{
silver_bb |= Bitboard::from_square(Square::new(file_l, r3));
}
}
if sq_king.rank() == Rank::Rank5 {
silver_bb |= knight_effect(us, sq_king);
}
table[idx][PieceTypeCheck::Silver as usize][c] = silver_bb;
let mut gold_bb = Bitboard::EMPTY;
let mut gold_targets = gold_effect(!us, sq_king);
while gold_targets.is_not_empty() {
let to = gold_targets.pop();
gold_bb |= gold_effect(!us, to);
}
gold_bb &= !Bitboard::from_square(sq_king);
table[idx][PieceTypeCheck::Gold as usize][c] = gold_bb;
table[idx][PieceTypeCheck::Bishop as usize][c] =
bishop_effect(sq_king, Bitboard::EMPTY);
table[idx][PieceTypeCheck::Rook as usize][c] = rook_effect(sq_king, Bitboard::EMPTY);
table[idx][PieceTypeCheck::ProBishop as usize][c] =
bishop_effect(sq_king, Bitboard::EMPTY) | king_effect(sq_king);
table[idx][PieceTypeCheck::ProRook as usize][c] =
rook_effect(sq_king, Bitboard::EMPTY) | king_effect(sq_king);
let mut non_slider = Bitboard::EMPTY;
non_slider |= table[idx][PieceTypeCheck::PawnWithNoPro as usize][c];
non_slider |= table[idx][PieceTypeCheck::PawnWithPro as usize][c];
non_slider |= table[idx][PieceTypeCheck::Knight as usize][c];
non_slider |= table[idx][PieceTypeCheck::Silver as usize][c];
non_slider |= table[idx][PieceTypeCheck::Gold as usize][c];
table[idx][PieceTypeCheck::NonSlider as usize][c] = non_slider;
}
}
table
}
fn init_check_around_bb() -> [[[Bitboard; 2]; PieceType::NUM + 1]; 81] {
let mut table = [[[Bitboard::EMPTY; 2]; PieceType::NUM + 1]; 81];
for sq_king in Square::all() {
let around8 = king_effect(sq_king);
for &us in &[Color::Black, Color::White] {
let c = us.index();
for pt_idx in 1..=PieceType::NUM {
let pt = PieceType::from_u8(pt_idx as u8).unwrap();
let mut bb = Bitboard::EMPTY;
for near in around8.iter() {
let cand = match pt {
PieceType::Pawn => pawn_effect(!us, near),
PieceType::Lance => lance_effect(!us, near, Bitboard::EMPTY),
PieceType::Knight => knight_effect(!us, near),
PieceType::Silver => silver_effect(!us, near),
PieceType::Bishop => bishop_effect(near, Bitboard::EMPTY),
PieceType::Rook => rook_effect(near, Bitboard::EMPTY),
PieceType::ProPawn
| PieceType::ProLance
| PieceType::ProKnight
| PieceType::ProSilver
| PieceType::Gold => gold_effect(!us, near),
PieceType::Horse => {
bishop_effect(near, Bitboard::EMPTY) | king_effect(near)
}
PieceType::Dragon => rook_effect(near, Bitboard::EMPTY) | king_effect(near),
PieceType::King => king_effect(near),
};
bb |= cand;
}
bb &= king_effect(sq_king)
| rook_effect(sq_king, Bitboard::EMPTY)
| bishop_effect(sq_king, Bitboard::EMPTY);
bb &= !Bitboard::from_square(sq_king);
table[sq_king.index()][pt.index()][c] = bb;
}
}
}
table
}
fn init_next_square() -> [[Option<Square>; 81]; 81] {
let mut table = [[None; 81]; 81];
for s1 in Square::all() {
for s2 in Square::all() {
let f1 = s1.file().index() as i32;
let r1 = s1.rank().index() as i32;
let f2 = s2.file().index() as i32;
let r2 = s2.rank().index() as i32;
let df = (f2 - f1).signum();
let dr = (r2 - r1).signum();
if (df == 0 && dr == 0) || !(df == 0 || dr == 0 || df.abs() == dr.abs()) {
table[s1.index()][s2.index()] = None;
continue;
}
let nf = f2 + df;
let nr = r2 + dr;
if (0..=8).contains(&nf)
&& (0..=8).contains(&nr)
&& let (Some(file), Some(rank)) =
(crate::types::File::from_u8(nf as u8), crate::types::Rank::from_u8(nr as u8))
{
table[s1.index()][s2.index()] = Some(Square::new(file, rank));
}
}
}
table
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_piece_type_check_enum() {
assert_eq!(PieceTypeCheck::NUM, 11);
assert_eq!(PieceTypeCheck::from_u8(0), Some(PieceTypeCheck::PawnWithNoPro));
assert_eq!(PieceTypeCheck::from_u8(10), Some(PieceTypeCheck::NonSlider));
assert_eq!(PieceTypeCheck::from_u8(11), None);
}
#[test]
fn test_check_cand_special_cases() {
let black_pawn = CHECK_CAND_BB[Square::SQ_55.index()]
[PieceTypeCheck::PawnWithNoPro as usize][Color::Black.index()];
assert!(black_pawn.is_not_empty());
let bottom = Square::new(Square::SQ_55.file(), Rank::Rank1);
assert!(!black_pawn.contains(bottom));
let lance = CHECK_CAND_BB[Square::SQ_11.index()][PieceTypeCheck::Lance as usize]
[Color::Black.index()];
let sq_12 = Square::new(File::File1, Rank::Rank2);
assert!(lance.contains(sq_12));
}
#[test]
fn test_tables_initialization() {
let _ = &*CHECK_CAND_BB;
let _ = &*CHECK_AROUND_BB;
let _ = &*NEXT_SQUARE;
}
}