use std::sync::LazyLock;
use crate::bitboard::BitBoard;
use crate::square::Square;
const RANK_OCC_SIZE: usize = 512; const FILE_OCC_SIZE: usize = 1024;
type RankTable = [[BitBoard; RANK_OCC_SIZE]; 90];
type FileTable = [[BitBoard; FILE_OCC_SIZE]; 90];
#[inline]
pub fn rank_occ(occ: BitBoard, rank: u8) -> u16 { ((occ.raw() >> (rank as u32 * 9)) & 0x1FF) as u16 }
#[inline]
pub fn file_occ(occ: BitBoard, file: u8) -> u16 {
let x = occ.raw() >> file as u32;
let mut r = 0u16;
r |= (x & 1) as u16;
r |= (((x >> 9) & 1) as u16) << 1;
r |= (((x >> 18) & 1) as u16) << 2;
r |= (((x >> 27) & 1) as u16) << 3;
r |= (((x >> 36) & 1) as u16) << 4;
r |= (((x >> 45) & 1) as u16) << 5;
r |= (((x >> 54) & 1) as u16) << 6;
r |= (((x >> 63) & 1) as u16) << 7;
r |= (((x >> 72) & 1) as u16) << 8;
r |= (((x >> 81) & 1) as u16) << 9;
r
}
pub static ROOK_RANK: LazyLock<Box<RankTable>> = LazyLock::new(|| {
let mut table = Box::new([[BitBoard::EMPTY; RANK_OCC_SIZE]; 90]);
for sq_raw in 0..90u8 {
let rank = sq_raw / 9;
let file = sq_raw % 9;
for occ_pattern in 0..RANK_OCC_SIZE {
let occ = occ_pattern as u16;
let mut attack_files = 0u16;
let mut f = file as i32 - 1;
while f >= 0 {
attack_files |= 1 << f;
if occ & (1 << f) != 0 {
break;
}
f -= 1;
}
let mut f = file as i32 + 1;
while f <= 8 {
attack_files |= 1 << f;
if occ & (1 << f) != 0 {
break;
}
f += 1;
}
table[sq_raw as usize][occ_pattern] = expand_rank(attack_files, rank);
}
}
table
});
pub static ROOK_FILE: LazyLock<Box<FileTable>> = LazyLock::new(|| {
let mut table = Box::new([[BitBoard::EMPTY; FILE_OCC_SIZE]; 90]);
for sq_raw in 0..90u8 {
let rank = sq_raw / 9;
let file = sq_raw % 9;
for occ_pattern in 0..FILE_OCC_SIZE {
let occ = occ_pattern as u16;
let mut attack_ranks = 0u16;
let mut r = rank as i32 - 1;
while r >= 0 {
attack_ranks |= 1 << r;
if occ & (1 << r) != 0 {
break;
}
r -= 1;
}
let mut r = rank as i32 + 1;
while r <= 9 {
attack_ranks |= 1 << r;
if occ & (1 << r) != 0 {
break;
}
r += 1;
}
table[sq_raw as usize][occ_pattern] = expand_file(attack_ranks, file);
}
}
table
});
#[derive(Copy, Clone, Debug, Default)]
pub struct CannonLine {
pub quiet: BitBoard,
pub capture: BitBoard,
}
type CannonRankTable = [[CannonLine; RANK_OCC_SIZE]; 90];
type CannonFileTable = [[CannonLine; FILE_OCC_SIZE]; 90];
pub static CANNON_RANK: LazyLock<Box<CannonRankTable>> = LazyLock::new(|| {
let mut boxed: Box<CannonRankTable> =
vec![[CannonLine::default(); RANK_OCC_SIZE]; 90].into_boxed_slice().try_into().expect("vec length is 90");
for sq_raw in 0..90u8 {
let rank = sq_raw / 9;
let file = sq_raw % 9;
for occ_pattern in 0..RANK_OCC_SIZE {
let occ = occ_pattern as u16;
let (quiet_files, cap_files) = cannon_line_bits(occ, file, 0, 8);
boxed[sq_raw as usize][occ_pattern] =
CannonLine { quiet: expand_rank(quiet_files, rank), capture: expand_rank(cap_files, rank) };
}
}
boxed
});
pub static CANNON_FILE: LazyLock<Box<CannonFileTable>> = LazyLock::new(|| {
let mut boxed: Box<CannonFileTable> =
vec![[CannonLine::default(); FILE_OCC_SIZE]; 90].into_boxed_slice().try_into().expect("vec length is 90");
for sq_raw in 0..90u8 {
let rank = sq_raw / 9;
let file = sq_raw % 9;
for occ_pattern in 0..FILE_OCC_SIZE {
let occ = occ_pattern as u16;
let (quiet_ranks, cap_ranks) = cannon_line_bits(occ, rank, 0, 9);
boxed[sq_raw as usize][occ_pattern] =
CannonLine { quiet: expand_file(quiet_ranks, file), capture: expand_file(cap_ranks, file) };
}
}
boxed
});
#[inline]
pub fn rook_attacks(sq: Square, occ: BitBoard) -> BitBoard {
let r = rank_occ(occ, sq.rank());
let f = file_occ(occ, sq.file());
ROOK_RANK[sq.raw() as usize][r as usize] | ROOK_FILE[sq.raw() as usize][f as usize]
}
#[inline]
pub fn cannon_attacks(sq: Square, occ: BitBoard) -> (BitBoard, BitBoard) {
let r = rank_occ(occ, sq.rank());
let f = file_occ(occ, sq.file());
let rr = &CANNON_RANK[sq.raw() as usize][r as usize];
let ff = &CANNON_FILE[sq.raw() as usize][f as usize];
(rr.quiet | ff.quiet, rr.capture | ff.capture)
}
fn cannon_line_bits(occ: u16, pos: u8, lo: u8, hi: u8) -> (u16, u16) {
let mut quiet = 0u16;
let mut capture = 0u16;
for dir in [-1i32, 1i32] {
let mut i = pos as i32 + dir;
while i >= lo as i32 && i <= hi as i32 {
if occ & (1 << i) != 0 {
break;
}
quiet |= 1 << i;
i += dir;
}
i += dir;
while i >= lo as i32 && i <= hi as i32 {
if occ & (1 << i) != 0 {
capture |= 1 << i;
break;
}
i += dir;
}
}
(quiet, capture)
}
#[inline]
fn expand_rank(bits: u16, rank: u8) -> BitBoard { BitBoard((bits as u128) << (rank as u32 * 9)) }
#[inline]
fn expand_file(bits: u16, file: u8) -> BitBoard {
let mut out = 0u128;
let mut b = bits;
let mut r = 0u32;
while b != 0 {
if b & 1 != 0 {
out |= 1u128 << (file as u32 + 9 * r);
}
b >>= 1;
r += 1;
}
BitBoard(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rank_file_occ_roundtrip() {
for sq_raw in 0..90u8 {
let bb = BitBoard::from_square(Square::new_unchecked(sq_raw));
let rank = sq_raw / 9;
let file = sq_raw % 9;
assert_eq!(rank_occ(bb, rank), 1 << file);
assert_eq!(file_occ(bb, file), 1 << rank);
}
}
}