From 06bfc4ac57e087d41161aac4aa088d5cf502a6ff Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Mon, 1 Jan 2024 09:25:31 -0800 Subject: [PATCH] [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. --- board/src/bitboard/bitboard.rs | 8 +++- board/src/bitboard/library.rs | 87 ++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/board/src/bitboard/bitboard.rs b/board/src/bitboard/bitboard.rs index 96cc21b..7c86e09 100644 --- a/board/src/bitboard/bitboard.rs +++ b/board/src/bitboard/bitboard.rs @@ -1,6 +1,6 @@ // Eryn Wells -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()) } diff --git a/board/src/bitboard/library.rs b/board/src/bitboard/library.rs index 46f54b1..421f59c 100644 --- a/board/src/bitboard/library.rs +++ b/board/src/bitboard/library.rs @@ -1,6 +1,8 @@ // Eryn Wells 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] + } +}