[board] Implement a BitBoard MoveLibrary

This struct computes and stores piece moves per square to speed up computation
of move lists.

Initialize a `static mut` instance once via std::sync::Once, and return a static
reference to it. So far, it computes king and knight moves.

Make BitBoard::empty() and MoveLibrary::new() const functions, enabling static
construction of the MOVE_LIBRARY instance without needing to wrap it in an
Option.
This commit is contained in:
Eryn Wells 2024-01-01 09:25:31 -08:00
parent 4a54d8b877
commit 06bfc4ac57
2 changed files with 93 additions and 2 deletions

View file

@ -1,6 +1,6 @@
// Eryn Wells <eryn@erynwells.me>
use super::library::{FILES, RANKS};
use super::library::{library, FILES, RANKS};
use super::BitScanner;
use crate::Square;
use std::fmt;
@ -10,7 +10,7 @@ use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
pub(crate) struct BitBoard(pub(super) u64);
impl BitBoard {
pub fn empty() -> BitBoard {
pub const fn empty() -> BitBoard {
BitBoard(0)
}
@ -28,6 +28,10 @@ impl BitBoard {
FILES[file]
}
pub fn knight_moves(sq: Square) -> BitBoard {
library().knight_moves(sq)
}
pub fn from_square(sq: Square) -> BitBoard {
BitBoard(1 << sq.index())
}

View file

@ -1,6 +1,8 @@
// Eryn Wells <eryn@erynwells.me>
use super::BitBoard;
use crate::Square;
use std::sync::Once;
pub(super) const RANKS: [BitBoard; 8] = [
BitBoard(0xFF << 0 * 8),
@ -23,3 +25,88 @@ pub(super) const FILES: [BitBoard; 8] = [
BitBoard(0x0101010101010101 << 6),
BitBoard(0x0101010101010101 << 7),
];
static mut MOVE_LIBRARY: MoveLibrary = MoveLibrary::new();
static MOVE_LIBRARY_INIT: Once = Once::new();
pub(super) fn library() -> &'static MoveLibrary {
unsafe {
MOVE_LIBRARY_INIT.call_once(|| {
MOVE_LIBRARY.initialize_if_needed();
});
&MOVE_LIBRARY
}
}
pub(super) struct MoveLibrary {
is_initialized: bool,
// Piecewise move tables
king_moves: [BitBoard; 64],
knight_moves: [BitBoard; 64],
}
impl MoveLibrary {
const fn new() -> MoveLibrary {
MoveLibrary {
is_initialized: false,
king_moves: [BitBoard::empty(); 64],
knight_moves: [BitBoard::empty(); 64],
}
}
fn initialize_if_needed(&mut self) {
if self.is_initialized {
return;
}
self.do_initialization();
self.is_initialized = true;
}
fn do_initialization(&mut self) {
self.initialize_king_moves();
self.initialize_knight_moves();
}
fn initialize_king_moves(&mut self) {
for i in 0..64 {
let king = BitBoard::new(1 << i);
let mut attacks = king.shift_east_one() | king.shift_west_one();
let king = king | attacks;
attacks |= king.shift_north_one() | king.shift_south_one();
self.king_moves[i] = attacks;
}
}
/// Calculate bitboards representing knight moves from each square on the
/// board. The algorithm is described on the [Chess Programming Wiki][cpw].
///
/// [cpw]: https://www.chessprogramming.org/Knight_Pattern
fn initialize_knight_moves(&mut self) {
for i in 0..64 {
let knight = BitBoard::new(1 << i);
let east = knight.shift_east_one();
let west = knight.shift_west_one();
let mut attacks = (east | west).shift_north(2);
attacks |= (east | west).shift_south(2);
let east = east.shift_east_one();
let west = west.shift_west_one();
attacks |= (east | west).shift_north_one();
attacks |= (east | west).shift_south_one();
self.knight_moves[i] = attacks;
}
}
pub(super) fn knight_moves(&self, sq: Square) -> BitBoard {
self.knight_moves[sq.index() as usize]
}
}