use rand::{Rng, thread_rng};
use board::*;
use bitsets::*;
pub struct BoardGeometry {
pub squares_at_line: [[Bitboard; 64]; 64],
pub squares_between_including: [[Bitboard; 64]; 64],
pub squares_behind_blocker: [[Bitboard; 64]; 64],
pub pawn_attacks: [[Bitboard; 64]; 2],
}
impl BoardGeometry {
fn new() -> BoardGeometry {
let mut bg = BoardGeometry {
squares_at_line: [[0; 64]; 64],
squares_between_including: [[0; 64]; 64],
squares_behind_blocker: [[0; 64]; 64],
pawn_attacks: [[0; 64]; 2],
};
for a in 0..64 {
let lines = [bb_file(a), bb_rank(a), bb_diag(a), bb_anti_diag(a)];
for b in a + 1..64 {
for line in lines.iter() {
if *line & (1 << b) != 0 {
bg.squares_at_line[a][b] = *line;
bg.squares_at_line[b][a] = *line;
break;
}
}
}
}
for a in 0..64 {
for b in 0..64 {
let queen_attacks_from_a = bb_rook_attacks(a, 1 << a | 1 << b) |
bb_bishop_attacks(a, 1 << a | 1 << b);
bg.squares_behind_blocker[a][b] = bg.squares_at_line[a][b] & !(1 << a) &
!queen_attacks_from_a;
}
}
for a in 0..64 {
for b in 0..64 {
bg.squares_between_including[a][b] = bg.squares_at_line[a][b] &
!bg.squares_behind_blocker[a][b] &
!bg.squares_behind_blocker[b][a];
}
}
const SHIFTS: [[isize; 2]; 2] = [[7, 9], [-9, -7]];
for us in 0..2 {
for a in 0..64 {
bg.pawn_attacks[us][a] = (gen_shift(1 << a, SHIFTS[us][0]) & !BB_FILE_H) |
(gen_shift(1 << a, SHIFTS[us][1]) & !BB_FILE_A);
}
}
unsafe {
init_king_attacks();
init_knight_attacks();
let bishop_attacks_size = init_slider_map(BISHOP, &mut BISHOP_MAP, 0, false);
let total_size = init_slider_map(ROOK, &mut ROOK_MAP, bishop_attacks_size, false);
assert!(total_size == SLIDER_ATTACKS_SIZE);
}
bg
}
pub fn get() -> &'static BoardGeometry {
use std::sync::{Once, ONCE_INIT};
static INIT_GEOMETRY: Once = ONCE_INIT;
static mut GEOMETRY: Option<BoardGeometry> = None;
unsafe {
INIT_GEOMETRY.call_once(|| {
GEOMETRY = Some(BoardGeometry::new());
});
GEOMETRY.as_ref().unwrap()
}
}
#[inline]
pub fn attacks_from(&self,
piece: PieceType,
from_square: Square,
occupied: Bitboard)
-> Bitboard {
debug_assert!(piece < PAWN);
debug_assert!(from_square <= 63);
unsafe {
match piece {
QUEEN => {
BISHOP_MAP[from_square].attacks(occupied) |
ROOK_MAP[from_square].attacks(occupied)
}
ROOK => ROOK_MAP[from_square].attacks(occupied),
BISHOP => BISHOP_MAP[from_square].attacks(occupied),
KNIGHT => KNIGHT_ATTACKS[from_square],
_ => KING_ATTACKS[from_square],
}
}
}
#[inline(always)]
pub unsafe fn attacks_from_unsafe(&self,
piece: PieceType,
from_square: Square,
occupied: Bitboard)
-> Bitboard {
debug_assert!(piece < PAWN);
debug_assert!(from_square <= 63);
match piece {
QUEEN => {
BISHOP_MAP.get_unchecked(from_square).attacks(occupied) |
ROOK_MAP.get_unchecked(from_square).attacks(occupied)
}
ROOK => ROOK_MAP.get_unchecked(from_square).attacks(occupied),
BISHOP => BISHOP_MAP.get_unchecked(from_square).attacks(occupied),
KNIGHT => *KNIGHT_ATTACKS.get_unchecked(from_square),
_ => *KING_ATTACKS.get_unchecked(from_square),
}
}
}
const SLIDER_ATTACKS_SIZE: usize = 107648;
static mut SLIDER_ATTACKS: [Bitboard; SLIDER_ATTACKS_SIZE] = [0; SLIDER_ATTACKS_SIZE];
static mut KING_ATTACKS: [Bitboard; 64] = [0; 64];
static mut KNIGHT_ATTACKS: [Bitboard; 64] = [0; 64];
static mut BISHOP_MAP: [AttacksMagic; 64] = [AttacksMagic {
offset: 0,
mask: 0,
magic: 0,
shift: 0,
}; 64];
static mut ROOK_MAP: [AttacksMagic; 64] = [AttacksMagic {
offset: 0,
mask: 0,
magic: 0,
shift: 0,
}; 64];
#[derive(Copy, Clone)]
struct AttacksMagic {
pub offset: usize,
pub mask: Bitboard,
pub magic: u64,
pub shift: u32,
}
impl AttacksMagic {
#[inline(always)]
pub unsafe fn attacks(&self, occupied: Bitboard) -> Bitboard {
let index = (self.magic.wrapping_mul(occupied & self.mask)) >> self.shift;
*SLIDER_ATTACKS.get_unchecked(self.offset.wrapping_add(index as usize))
}
}
unsafe fn init_knight_attacks() {
let offsets = vec![(-1, -2), (-2, -1), (-2, 1), (-1, 2), (1, -2), (2, -1), (2, 1), (1, 2)];
for (i, attacks) in KNIGHT_ATTACKS.iter_mut().enumerate() {
let (r, c) = ((i / 8) as isize, (i % 8) as isize);
for &(dr, dc) in &offsets {
if r + dr >= 0 && c + dc >= 0 && r + dr < 8 && c + dc < 8 {
*attacks |= 1 << ((r + dr) * 8 + c + dc);
}
}
}
}
unsafe fn init_king_attacks() {
let offsets = vec![(1, -1), (1, 0), (1, 1), (0, -1), (0, 1), (-1, -1), (-1, 0), (-1, 1)];
for (i, attacks) in KING_ATTACKS.iter_mut().enumerate() {
let (r, c) = ((i / 8) as isize, (i % 8) as isize);
for &(dr, dc) in &offsets {
if r + dr >= 0 && c + dc >= 0 && r + dr < 8 && c + dc < 8 {
*attacks |= 1 << ((r + dr) * 8 + c + dc);
}
}
}
}
unsafe fn init_slider_map(piece: PieceType,
piece_map: &mut [AttacksMagic; 64],
mut offset: usize,
from_scratch: bool)
-> usize {
assert!(piece == BISHOP || piece == ROOK);
let mut rng = thread_rng();
for (sq, entry) in piece_map.iter_mut().enumerate() {
let attacks: fn(Square, Bitboard) -> Bitboard = if piece == BISHOP {
bb_bishop_attacks
} else {
bb_rook_attacks
};
let edges = ((BB_RANK_1 | BB_RANK_8) & !bb_rank(sq)) |
((BB_FILE_A | BB_FILE_H) & !bb_file(sq));
let mask = attacks(sq, 1 << sq) & !edges;
let num_ones = mask.count_ones();
let shift = 64 - num_ones;
let mut occupancy = vec![0; 1 << num_ones];
let mut reference = vec![0; 1 << num_ones];
let mut size = 0;
let mut occ = 0;
loop {
occupancy[size] = occ;
reference[size] = attacks(sq, occ | (1 << sq));
size += 1;
occ = occ.wrapping_sub(mask) & mask;
if occ == 0 {
break;
}
}
let mut magic = if piece == BISHOP {
BISHOP_MAGICS[sq]
} else {
ROOK_MAGICS[sq]
};
'outer: loop {
if from_scratch {
loop {
magic = rng.gen::<u64>() & rng.gen::<u64>() & rng.gen::<u64>();
if ((magic * mask) >> 56).count_ones() >= 6 {
break;
}
}
}
let mut attacks = vec![0; size];
for i in 0..size {
let index = magic.wrapping_mul(occupancy[i]) >> shift;
let attack = &mut attacks[index as usize];
if *attack != 0 && *attack != reference[i] {
assert!(from_scratch,
"Error: Precalculated magic is incorrect. Square {}, for {} magic",
sq,
if piece == BISHOP {
"bishop"
} else {
"rook"
});
continue 'outer;
}
*attack = reference[i];
}
*entry = AttacksMagic {
offset: offset,
mask: mask,
magic: magic,
shift: shift,
};
for (i, &att) in attacks.iter().enumerate() {
SLIDER_ATTACKS[offset + i] = att;
}
offset += size;
break;
}
}
offset
}
fn bb_rook_attacks(from_square: Square, occupied: Bitboard) -> Bitboard {
bb_line_attacks(bb_file(from_square), from_square, occupied) |
bb_line_attacks(bb_rank(from_square), from_square, occupied)
}
fn bb_bishop_attacks(from_square: Square, occupied: Bitboard) -> Bitboard {
bb_line_attacks(bb_diag(from_square), from_square, occupied) |
bb_line_attacks(bb_anti_diag(from_square), from_square, occupied)
}
fn bb_line_attacks(line: Bitboard, from_square: Square, occupied: Bitboard) -> Bitboard {
let from_square_bb = 1u64 << from_square;
debug_assert!(from_square_bb & line != 0);
let potential_blockers = occupied & line;
let forward = potential_blockers.wrapping_sub(from_square_bb.wrapping_mul(2));
let rev = reverse(reverse(potential_blockers)
.wrapping_sub(reverse(from_square_bb).wrapping_mul(2)));
(forward ^ rev) & line
}
fn reverse(mut v: u64) -> u64 {
v = ((v >> 1) & 0x5555555555555555) | ((v & 0x5555555555555555) << 1);
v = ((v >> 2) & 0x3333333333333333) | ((v & 0x3333333333333333) << 2);
v = ((v >> 4) & 0x0F0F0F0F0F0F0F0F) | ((v & 0x0F0F0F0F0F0F0F0F) << 4);
v = ((v >> 8) & 0x00FF00FF00FF00FF) | ((v & 0x00FF00FF00FF00FF) << 8);
v = ((v >> 16) & 0x0000FFFF0000FFFF) | ((v & 0x0000FFFF0000FFFF) << 16);
((v >> 32) & 0x00000000FFFFFFFF) | ((v & 0x00000000FFFFFFFF) << 32)
}
const BISHOP_MAGICS: [u64; 64] = [306397059236266368,
6638343277122827280,
10377420549504106496,
9193021019258913,
2306408226914042898,
10379110636817760276,
27167319028441088,
7566153073497751552,
1513227076520969216,
301917653126479936,
72075465430409232,
2343002121441460228,
36033212782477344,
9223373154083475456,
6935629192638251008,
72621648200664064,
2310506081245267984,
2533291987569153,
146934404644733024,
1838417834950912,
579856052833622016,
1729946448243595776,
705208029025040,
2886877732040869888,
10092575566416331020,
5635409948247040,
738739924278198804,
4648849515743289408,
9233786889293807616,
1155253577929753088,
435164712050360592,
3026700562025580641,
4612284839965491969,
10448650511900137472,
571823356120080,
40569782189687936,
148620986995048708,
4901113822871308288,
4612077461748908288,
10204585674276944,
2534512027246592,
5766297627561820676,
13809969191200768,
1153062656578422784,
9318235838682899712,
11533824475839595776,
433770548762247233,
92326036501692936,
9227053213059129360,
577024872779350852,
108087561569959936,
582151826703646856,
81404176367767,
316415319130374273,
9113856212762624,
145453328103440392,
441392350330618400,
1126492748710916,
2309220790581891072,
3026423624667006980,
18019391702696464,
4516931289817600,
1450317422841301124,
9246488805123342592];
const ROOK_MAGICS: [u64; 64] = [36028867955671040,
2395917338224361536,
936757656041832464,
648535942831284356,
36037595259731970,
13943151043426386048,
432349966580056576,
4683745813775001856,
1191624314978336800,
4611756662317916160,
4625338105090543616,
140806208356480,
1688987371057664,
9288708641522688,
153403870897537280,
281550411726850,
2401883155071024,
1206964838111645696,
166705754384925184,
36039792408011264,
10376580514281768960,
9148486532465664,
578787319189340418,
398007816633254020,
2341872150903791616,
2314850762536009728,
297238127310798880,
2251868801728768,
2594082183614301184,
820222482337235456,
37717655469424904,
577596144088011012,
1152991874030502016,
3171026856472219648,
20415869351890944,
4611844348286345472,
2455605323386324224,
140754676613632,
1740713828645089416,
58361257132164,
70370893791232,
9227880322828615684,
72092778695295040,
577023839834341392,
4723150143565660416,
563087661073408,
651083773116450,
72128789630550047,
153192758223054976,
869194865525653568,
4972009250306933248,
1031325449119138048,
1297041090863464576,
580401419157405824,
1657992643584,
306245066729521664,
15206439601351819394,
14143290885479661953,
1688988407201810,
18065251325837538,
1152927311403745429,
162411078742050817,
334255838724676,
27323018585852550];
#[cfg(test)]
mod tests {
use super::*;
use super::{bb_bishop_attacks, bb_rook_attacks};
use board::*;
use squares::*;
use bitsets::pop_count;
#[test]
fn board_geometry() {
let g = BoardGeometry::new();
assert_eq!(g.squares_at_line[B1][C3], 0);
assert_eq!(g.squares_at_line[B1][G1], 0b11111111);
assert_eq!(g.squares_at_line[G8][B8], 0b11111111 << 56);
assert_eq!(g.squares_between_including[B1][C3], 0);
assert_eq!(g.squares_between_including[B1][G1], 0b01111110);
assert_eq!(g.squares_between_including[G8][B8], 0b01111110 << 56);
assert_eq!(g.squares_behind_blocker[B1][G1], 1 << H1);
assert_eq!(g.squares_behind_blocker[G8][B8], 1 << A8);
assert_eq!(g.squares_behind_blocker[A1][G7], 1 << H8);
assert_eq!(g.squares_behind_blocker[H1][B7], 1 << A8);
assert_eq!(g.squares_behind_blocker[B7][G2], 1 << H1);
assert_eq!(g.squares_behind_blocker[G7][B2], 1 << A1);
assert_eq!(g.squares_behind_blocker[D7][D7], 0);
assert_eq!(g.squares_behind_blocker[D7][F8], 0);
assert_eq!(g.squares_between_including[A1][A4] | g.squares_behind_blocker[A1][A4],
g.squares_at_line[A1][A4]);
}
#[test]
fn attacks_from() {
use rand::{Rng, thread_rng};
let mut rng = thread_rng();
let g = BoardGeometry::new();
for piece in KING..PAWN {
for square in 0..64 {
let occupied = rng.gen::<u64>();
assert_eq!(g.attacks_from(piece, square, occupied & !(1 << square)),
g.attacks_from(piece, square, occupied | (1 << square)));
}
}
for square in 0..64 {
let occupied = rng.gen::<u64>();
let bishop_attacks = g.attacks_from(BISHOP, square, occupied);
let rook_attacks = g.attacks_from(ROOK, square, occupied);
let queen_attacks = g.attacks_from(QUEEN, square, occupied);
let king_attacks = g.attacks_from(KING, square, occupied);
let kinght_attacks = g.attacks_from(KNIGHT, square, occupied);
assert!(pop_count(king_attacks) >= 3);
assert!(pop_count(king_attacks) <= 8);
assert!(pop_count(kinght_attacks) >= 2);
assert!(pop_count(kinght_attacks) <= 8);
assert_eq!(kinght_attacks & queen_attacks, 0);
assert_eq!(king_attacks & queen_attacks, king_attacks);
assert_eq!(king_attacks, g.attacks_from(KING, square, 0));
assert_eq!(kinght_attacks, g.attacks_from(KNIGHT, square, 0));
assert_eq!(bishop_attacks | rook_attacks, queen_attacks);
assert_eq!(bishop_attacks, bb_bishop_attacks(square, occupied));
assert_eq!(rook_attacks, bb_rook_attacks(square, occupied));
}
}
}