use std::mem;
use std::ptr;
use SQ;
use core::masks::*;
use core::{rank_bb,file_bb};
use core::bit_twiddles::popcount64;
use tools::prng::PRNG;
const ROOK_M_SIZE: usize = 102_400;
static mut ROOK_MAGICS: [SMagic; 64] = [SMagic::init(); 64];
static mut ROOK_TABLE: [u64; ROOK_M_SIZE] = [0; ROOK_M_SIZE];
const BISHOP_M_SIZE: usize = 5248;
static mut BISHOP_MAGICS: [SMagic; 64] = [SMagic::init(); 64];
static mut BISHOP_TABLE: [u64; BISHOP_M_SIZE] = [0; BISHOP_M_SIZE];
const B_DELTAS: [i8; 4] = [7, 9, -9, -7];
const R_DELTAS: [i8; 4] = [8, 1, -8, -1];
const SEEDS: [[u64; 8]; 2] = [
[8977, 44_560, 54_343, 38_998, 5731, 95_205, 104_912, 17_020],
[728, 10_316, 55_013, 32_803, 12_281, 15_100, 16_645, 255],
];
#[cold]
pub fn init_magics() {
unsafe {
gen_magic_board(BISHOP_M_SIZE, &B_DELTAS, BISHOP_MAGICS.as_mut_ptr(), BISHOP_TABLE.as_mut_ptr());
gen_magic_board(ROOK_M_SIZE, &R_DELTAS, ROOK_MAGICS.as_mut_ptr(), ROOK_TABLE.as_mut_ptr());
}
}
#[inline]
pub fn bishop_attacks(mut occupied: u64, square: u8) -> u64 {
let magic_entry: &SMagic = unsafe { BISHOP_MAGICS.get_unchecked(square as usize)};
occupied &= magic_entry.mask;
occupied = occupied.wrapping_mul(magic_entry.magic);
occupied = occupied.wrapping_shr(magic_entry.shift);
unsafe {
*(magic_entry.ptr as *const u64).add(occupied as usize)
}
}
#[inline]
pub fn rook_attacks(mut occupied: u64, square: u8) -> u64 {
let magic_entry: &SMagic = unsafe { ROOK_MAGICS.get_unchecked(square as usize)};
occupied &= magic_entry.mask;
occupied = occupied.wrapping_mul(magic_entry.magic);
occupied = occupied.wrapping_shr(magic_entry.shift);
unsafe {
*(magic_entry.ptr as *const u64).add(occupied as usize)
}
}
#[derive(Copy, Clone)]
struct SMagic {
ptr: usize,
mask: u64,
magic: u64,
shift: u32,
}
impl SMagic {
pub const fn init() -> Self {
SMagic {
ptr: 0,
mask: 0,
magic: 0,
shift: 0,
}
}
}
struct PreSMagic {
start: usize,
len: usize,
mask: u64,
magic: u64,
shift: u32,
}
impl PreSMagic {
pub fn init() -> PreSMagic {
PreSMagic {
start: 0,
len: 0,
mask: 0,
magic: 0,
shift: 0,
}
}
pub unsafe fn init64() -> [PreSMagic; 64] {
let arr: [PreSMagic; 64] = mem::uninitialized();
arr
}
pub fn next_idx(&self) -> usize {
self.start + self.len
}
}
#[cold]
unsafe fn gen_magic_board(table_size: usize, deltas: &[i8; 4], static_magics: *mut SMagic, attacks: *mut u64) {
let mut pre_sq_table: [PreSMagic; 64] = PreSMagic::init64();
for table in pre_sq_table.iter_mut() {
*table = PreSMagic::init();
}
let mut occupancy: [u64; 4096] = [0; 4096];
let mut reference: [u64; 4096] = [0; 4096];
let mut age: [i32; 4096] = [0; 4096];
let mut size: usize;
let mut b: u64;
let mut current: i32 = 0;
let mut i: usize;
pre_sq_table[0].start = 0;
for s in 0..64 as u8 {
let mut magic: u64;
let edges: u64 = ((RANK_1 | RANK_8) & !rank_bb(s)) |
((FILE_A | FILE_H) & !file_bb(s));
let mask: u64 = sliding_attack(deltas, s, 0) & !edges;
let shift: u32 = (64 - popcount64(mask)) as u32;
b = 0;
size = 0;
'bit: loop {
occupancy[size] = b;
reference[size] = sliding_attack(deltas, s, b);
size += 1;
b = ((b).wrapping_sub(mask)) as u64 & mask;
if b == 0 {
break 'bit;
}
}
pre_sq_table[s as usize].len = size;
if s < 63 {
pre_sq_table[s as usize + 1].start = pre_sq_table[s as usize].next_idx();
}
let mut rng = PRNG::init(SEEDS[1][SQ(s).rank() as usize]);
'outer: loop {
'first_in: loop {
magic = rng.sparse_rand();
if popcount64((magic.wrapping_mul(mask)).wrapping_shr(56)) >= 6 {
break 'first_in;
}
}
current += 1;
i = 0;
while i < size {
let index: usize = ((occupancy[i as usize] & mask).wrapping_mul(magic) as
u64)
.wrapping_shr(shift) as usize;
if age[index] < current {
age[index] = current;
*attacks.offset((pre_sq_table[s as usize].start + index) as isize) = reference[i];
} else if *attacks.offset((pre_sq_table[s as usize].start + index) as isize) != reference[i] {
break;
}
i += 1;
}
if i >= size {
break 'outer;
}
}
pre_sq_table[s as usize].magic = magic;
pre_sq_table[s as usize].mask = mask;
pre_sq_table[s as usize].shift = shift;
}
let mut size = 0;
for i in 0..64 {
let beginptr = attacks.offset(size as isize);
let staticptr: *mut SMagic = static_magics.offset(i as isize);
let table_i: SMagic = SMagic {
ptr: mem::transmute::<*const u64, usize>(beginptr),
mask: pre_sq_table[i].mask,
magic: pre_sq_table[i].magic,
shift: pre_sq_table[i].shift,
};
ptr::copy::<SMagic>(&table_i, staticptr, 1);
size += pre_sq_table[i].len;
}
assert_eq!(size, table_size);
}
fn sliding_attack(deltas: &[i8; 4], sq: u8, occupied: u64) -> u64 {
assert!(sq < 64);
let mut attack: u64 = 0;
let square: i16 = sq as i16;
for delta in deltas.iter().take(4 as usize) {
let mut s: u8 = ((square as i16) + (*delta as i16)) as u8;
'inner: while s < 64 &&
SQ(s as u8).distance(SQ(((s as i16) - (*delta as i16)) as u8)) == 1
{
attack |= (1 as u64).wrapping_shl(s as u32);
if occupied & (1 as u64).wrapping_shl(s as u32) != 0 {
break 'inner;
}
s = ((s as i16) + (*delta as i16)) as u8;
}
}
attack
}