1
0
Fork 0
This commit is contained in:
Paul-Nicolas Madelaine 2024-04-29 02:00:26 +02:00
commit faccfbc1c5
16 changed files with 5154 additions and 0 deletions

202
src/lookup.rs Normal file
View file

@ -0,0 +1,202 @@
//! Lookup tables initialisation.
//!
//! Move generation in eschac requires about 1MB of precomputed lookup tables.
use crate::bitboard::*;
use crate::board::*;
use crate::magics::*;
use crate::rays::*;
pub(crate) use init::InitialisedLookup;
/// Forces the initialisation of the lookup tables.
///
/// It is not necessary to call this function, as lookup tables are initialised lazily, but it can
/// be used to ensure that they are initialised before a given time.
pub fn init() {
InitialisedLookup::init();
}
pub(crate) struct Lookup {
rays: Rays,
lines: BySquare<BySquare<Bitboard>>,
segments: BySquare<BySquare<Bitboard>>,
pawn_attacks: ByColor<BySquare<Bitboard>>,
king_moves: BySquare<Bitboard>,
knight_moves: BySquare<Bitboard>,
pub(crate) magics: Magics,
}
impl Lookup {
#[inline]
pub(crate) fn line(&self, a: Square, b: Square) -> Bitboard {
*self.lines.get(a).get(b)
}
#[inline]
pub(crate) fn segment(&self, a: Square, b: Square) -> Bitboard {
*self.segments.get(a).get(b)
}
#[inline]
pub(crate) fn ray(&self, square: Square, direction: Direction) -> Bitboard {
self.rays.ray(square, direction)
}
#[inline]
pub(crate) fn king(&self, square: Square) -> Bitboard {
*self.king_moves.get(square)
}
#[inline]
pub(crate) fn knight(&self, square: Square) -> Bitboard {
*self.knight_moves.get(square)
}
#[inline]
pub(crate) fn pawn_attack(&self, color: Color, square: Square) -> Bitboard {
*self.pawn_attacks.get(color).get(square)
}
#[inline]
pub(crate) fn bishop(&self, square: Square, blockers: Bitboard) -> Bitboard {
self.magics.bishop(square, blockers)
}
#[inline]
pub(crate) fn rook(&self, square: Square, blockers: Bitboard) -> Bitboard {
self.magics.rook(square, blockers)
}
/// `role != Pawn`
#[inline]
pub(crate) fn targets(&self, role: Role, from: Square, blockers: Bitboard) -> Bitboard {
match role {
Role::Pawn => unreachable!(),
Role::Knight => self.knight(from),
Role::Bishop => self.bishop(from, blockers),
Role::Rook => self.rook(from, blockers),
Role::Queen => self.bishop(from, blockers) | self.rook(from, blockers),
Role::King => self.king(from),
}
}
pub(crate) fn compute() -> Self {
let rays = Rays::new();
let lines = BySquare::new(|a| {
BySquare::new(|b| {
for d in Direction::all() {
let r = rays.ray(a, d);
if r.contains(b) {
return r;
}
}
Bitboard::new()
})
});
let segments = BySquare::new(|a| {
BySquare::new(|b| {
for d in Direction::all() {
let r = rays.ray(a, d);
if r.contains(b) {
return r & !rays.ray(b, d);
}
}
b.bitboard()
})
});
let pawn_attacks = ByColor::new(|color| {
let direction = match color {
Color::White => Direction::North,
Color::Black => Direction::South,
};
BySquare::new(|square| {
let mut res = Bitboard::new();
if let Some(square) = square.trans(direction) {
square.trans(Direction::East).map(|s| res.insert(s));
square.trans(Direction::West).map(|s| res.insert(s));
}
res
})
});
let king_moves = BySquare::new(|square| {
let mut res = Bitboard::new();
for direction in Direction::all() {
if let Some(x) = square.trans(direction) {
res |= x.bitboard();
}
}
res
});
let knight_moves = BySquare::new(|s| {
let mut res = Bitboard::new();
if let Some(s) = s.trans(Direction::North) {
s.trans(Direction::NorthEast).map(|s| res.insert(s));
s.trans(Direction::NorthWest).map(|s| res.insert(s));
}
if let Some(s) = s.trans(Direction::West) {
s.trans(Direction::NorthWest).map(|s| res.insert(s));
s.trans(Direction::SouthWest).map(|s| res.insert(s));
}
if let Some(s) = s.trans(Direction::South) {
s.trans(Direction::SouthWest).map(|s| res.insert(s));
s.trans(Direction::SouthEast).map(|s| res.insert(s));
}
if let Some(s) = s.trans(Direction::East) {
s.trans(Direction::SouthEast).map(|s| res.insert(s));
s.trans(Direction::NorthEast).map(|s| res.insert(s));
}
res
});
let magics = Magics::compute(&rays);
Self {
rays,
lines,
segments,
pawn_attacks,
king_moves,
knight_moves,
magics,
}
}
}
mod init {
use std::{mem::MaybeUninit, sync::LazyLock};
use super::Lookup;
static mut LOOKUP: MaybeUninit<Lookup> = MaybeUninit::uninit();
#[allow(static_mut_refs)]
static INIT: LazyLock<()> = LazyLock::new(|| unsafe {
LOOKUP.write(Lookup::compute());
});
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct InitialisedLookup(());
impl InitialisedLookup {
#[inline]
pub(crate) fn init() -> Self {
LazyLock::force(&INIT);
Self(())
}
}
impl std::ops::Deref for InitialisedLookup {
type Target = Lookup;
#[allow(static_mut_refs)]
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { LOOKUP.assume_init_ref() }
}
}
}