#![allow(dead_code)]
use shogi_core::Square;
use super::magics::{DIAGONAL_DOWN_MULTIPLIERS, DIAGONAL_UP_MULTIPLIERS, RANK_MULTIPLIERS};
use super::{DIAGONAL_DOWN_LINE, DIAGONAL_UP_LINE, LineKind, RANK_LINE, relevant_mask, walk_line};
use crate::bitboard::Bitboard;
pub(crate) struct Magic {
pub(crate) mask: u64,
pub(crate) magic: u64,
pub(crate) shift_in: u32,
pub(crate) shift_out: u32,
}
const NO_MAGIC: Magic = Magic {
mask: 0,
magic: 0,
shift_in: 0,
shift_out: 63,
};
static RANK_MAGICS: [Magic; 81] = derive_magics(&RANK_MULTIPLIERS, RANK_LINE);
static DIAGONAL_UP_MAGICS: [Magic; 81] = derive_magics(&DIAGONAL_UP_MULTIPLIERS, DIAGONAL_UP_LINE);
static DIAGONAL_DOWN_MAGICS: [Magic; 81] =
derive_magics(&DIAGONAL_DOWN_MULTIPLIERS, DIAGONAL_DOWN_LINE);
const fn derive_magics(multipliers: &[u64; 81], line: LineKind) -> [Magic; 81] {
let mut magics = [NO_MAGIC; 81];
let mut index = 0;
while index < 81 {
let relevant = relevant_mask(index, line);
let shift_in = if relevant == 0 {
0
} else {
relevant.trailing_zeros()
};
assert!(
128 - relevant.leading_zeros() - shift_in <= 64,
"relevant squares do not fit a u64 once normalized"
);
let mask = (relevant >> shift_in) as u64;
let bits = mask.count_ones();
assert!(bits <= 7, "line has more blockable squares than expected");
let shift_out = 64 - if bits == 0 { 1 } else { bits };
magics[index] = Magic {
mask,
magic: multipliers[index],
shift_in,
shift_out,
};
index += 1;
}
magics
}
type LineTable = [[Bitboard; 128]; 81];
static RANK_ATTACKS: LineTable = line_table(&RANK_MAGICS, RANK_LINE);
static DIAGONAL_UP_ATTACKS: LineTable = line_table(&DIAGONAL_UP_MAGICS, DIAGONAL_UP_LINE);
static DIAGONAL_DOWN_ATTACKS: LineTable = line_table(&DIAGONAL_DOWN_MAGICS, DIAGONAL_DOWN_LINE);
const fn line_table(magics: &[Magic; 81], line: LineKind) -> LineTable {
let mut table = [[Bitboard::EMPTY; 128]; 81];
let mut index = 0;
while index < 81 {
let magic = &magics[index];
let mut filled = [false; 128];
let mut occupancy = 0u64;
loop {
let occupied = (occupancy as u128) << magic.shift_in;
let slot = (occupancy.wrapping_mul(magic.magic) >> magic.shift_out) as usize;
let attacks = walk_line(index, occupied, line);
assert!(
!filled[slot] || table[index][slot].bits() == attacks,
"magic multiplier maps occupancies with different attacks onto one slot"
);
filled[slot] = true;
table[index][slot] = Bitboard::from_bits(attacks);
occupancy = occupancy.wrapping_sub(magic.mask) & magic.mask;
if occupancy == 0 {
break;
}
}
index += 1;
}
table
}
#[inline(always)]
fn line_attacks(index: usize, occupied: Bitboard, magics: &[Magic; 81], table: &LineTable) -> u128 {
let magic = &magics[index];
let relevant = ((occupied.bits() >> magic.shift_in) as u64) & magic.mask;
let slot = (relevant.wrapping_mul(magic.magic) >> magic.shift_out) as usize;
table[index][slot].bits()
}
pub(crate) fn bishop_attacks(square: Square, occupied: Bitboard) -> Bitboard {
let index = square.array_index();
Bitboard::from_bits(
line_attacks(index, occupied, &DIAGONAL_UP_MAGICS, &DIAGONAL_UP_ATTACKS)
| line_attacks(
index,
occupied,
&DIAGONAL_DOWN_MAGICS,
&DIAGONAL_DOWN_ATTACKS,
),
)
}
pub(crate) fn rook_attacks(square: Square, occupied: Bitboard) -> Bitboard {
let index = square.array_index();
Bitboard::from_bits(
line_attacks(index, occupied, &RANK_MAGICS, &RANK_ATTACKS)
| super::file_attacks(square, occupied).bits(),
)
}