diff --git a/board/src/bitboard/bitboard.rs b/board/src/bitboard/bitboard.rs index 7c86e09..7bcd3b8 100644 --- a/board/src/bitboard/bitboard.rs +++ b/board/src/bitboard/bitboard.rs @@ -9,6 +9,14 @@ use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not}; #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub(crate) struct BitBoard(pub(super) u64); +macro_rules! moves_getter { + ($getter_name:ident) => { + pub fn $getter_name(sq: Square) -> BitBoard { + library().$getter_name(sq) + } + }; +} + impl BitBoard { pub const fn empty() -> BitBoard { BitBoard(0) @@ -28,9 +36,11 @@ impl BitBoard { FILES[file] } - pub fn knight_moves(sq: Square) -> BitBoard { - library().knight_moves(sq) - } + moves_getter!(knight_moves); + moves_getter!(bishop_moves); + moves_getter!(rook_moves); + moves_getter!(queen_moves); + moves_getter!(king_moves); 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 421f59c..5e78c5a 100644 --- a/board/src/bitboard/library.rs +++ b/board/src/bitboard/library.rs @@ -30,83 +30,195 @@ static mut MOVE_LIBRARY: MoveLibrary = MoveLibrary::new(); static MOVE_LIBRARY_INIT: Once = Once::new(); pub(super) fn library() -> &'static MoveLibrary { + static MOVE_LIBRARY_INIT: Once = Once::new(); + static mut MOVE_LIBRARY: MoveLibrary = MoveLibrary::new(); + unsafe { MOVE_LIBRARY_INIT.call_once(|| { - MOVE_LIBRARY.initialize_if_needed(); + MOVE_LIBRARY.init(); }); &MOVE_LIBRARY } } +#[derive(Clone, Copy, Debug)] +struct OrthogonalRays { + positive_rank: BitBoard, + positive_file: BitBoard, + negative_rank: BitBoard, + negative_file: BitBoard, +} + +impl OrthogonalRays { + const fn new() -> OrthogonalRays { + OrthogonalRays { + positive_rank: BitBoard::empty(), + positive_file: BitBoard::empty(), + negative_rank: BitBoard::empty(), + negative_file: BitBoard::empty(), + } + } +} + +#[derive(Clone, Copy, Debug)] +struct DiagonalRays { + positive_diagonal: BitBoard, + positive_antidiagonal: BitBoard, + negative_diagonal: BitBoard, + negative_antidiagonal: BitBoard, +} + +impl DiagonalRays { + const fn new() -> DiagonalRays { + DiagonalRays { + positive_diagonal: BitBoard::empty(), + positive_antidiagonal: BitBoard::empty(), + negative_diagonal: BitBoard::empty(), + negative_antidiagonal: BitBoard::empty(), + } + } +} + +macro_rules! library_getter { + ($name:ident) => { + pub(super) fn $name(&self, sq: Square) -> BitBoard { + self.$name[sq.index() as usize] + } + }; +} + +#[derive(Debug)] pub(super) struct MoveLibrary { - is_initialized: bool, + // Rays + diagonal_rays: [DiagonalRays; 64], + orthogonal_rays: [OrthogonalRays; 64], // Piecewise move tables - king_moves: [BitBoard; 64], knight_moves: [BitBoard; 64], + bishop_moves: [BitBoard; 64], + rook_moves: [BitBoard; 64], + queen_moves: [BitBoard; 64], + king_moves: [BitBoard; 64], } impl MoveLibrary { const fn new() -> MoveLibrary { MoveLibrary { - is_initialized: false, - king_moves: [BitBoard::empty(); 64], + diagonal_rays: [DiagonalRays::new(); 64], + orthogonal_rays: [OrthogonalRays::new(); 64], knight_moves: [BitBoard::empty(); 64], + bishop_moves: [BitBoard::empty(); 64], + rook_moves: [BitBoard::empty(); 64], + queen_moves: [BitBoard::empty(); 64], + king_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) { + fn init(&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; + self.init_orthogonal_rays(i); + self.init_diagonal_rays(i); + self.init_knight_moves(i as usize); + self.init_bishop_moves(i as usize); + self.init_rook_moves(i as usize); + self.init_queen_moves(i as usize); + self.init_king_moves(i as usize); } } + fn init_orthogonal_rays(&mut self, idx: u8) { + let sq = Square::from_index_unsafe(idx); + let sq_bb = BitBoard::from_square(sq); + + let ortho_rays = &mut self.orthogonal_rays[idx as usize]; + ortho_rays.positive_file = Self::generate_ray(sq_bb, BitBoard::shift_north_one); + ortho_rays.negative_file = Self::generate_ray(sq_bb, BitBoard::shift_south_one); + ortho_rays.positive_rank = Self::generate_ray(sq_bb, BitBoard::shift_east_one); + ortho_rays.negative_rank = Self::generate_ray(sq_bb, BitBoard::shift_west_one); + } + + fn init_diagonal_rays(&mut self, idx: u8) { + let sq = Square::from_index_unsafe(idx); + let sq_bb = BitBoard::from_square(sq); + + let diag_rays = &mut self.diagonal_rays[idx as usize]; + diag_rays.positive_diagonal = Self::generate_ray(sq_bb, BitBoard::shift_north_east_one); + diag_rays.positive_antidiagonal = Self::generate_ray(sq_bb, BitBoard::shift_north_west_one); + diag_rays.negative_diagonal = Self::generate_ray(sq_bb, BitBoard::shift_south_west_one); + diag_rays.negative_antidiagonal = Self::generate_ray(sq_bb, BitBoard::shift_south_east_one); + } + + fn init_king_moves(&mut self, idx: usize) { + let king = BitBoard::new(1 << idx); + 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[idx] = 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); + fn init_knight_moves(&mut self, idx: usize) { + let knight = BitBoard::new(1 << idx); - let east = knight.shift_east_one(); - let west = knight.shift_west_one(); + 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 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(); + let east = east.shift_east_one(); + let west = west.shift_west_one(); - attacks |= (east | west).shift_north_one(); - attacks |= (east | west).shift_south_one(); + attacks |= (east | west).shift_north_one(); + attacks |= (east | west).shift_south_one(); - self.knight_moves[i] = attacks; + self.knight_moves[idx] = attacks; + } + + fn init_bishop_moves(&mut self, idx: usize) { + let diag_rays = self.diagonal_rays[idx]; + self.bishop_moves[idx] = diag_rays.positive_diagonal + | diag_rays.negative_diagonal + | diag_rays.positive_antidiagonal + | diag_rays.negative_antidiagonal; + } + + fn init_rook_moves(&mut self, idx: usize) { + let ortho_rays = self.orthogonal_rays[idx]; + self.rook_moves[idx] = ortho_rays.positive_rank + | ortho_rays.negative_rank + | ortho_rays.positive_file + | ortho_rays.negative_file; + } + + fn init_queen_moves(&mut self, idx: usize) { + let rook_moves = self.rook_moves[idx]; + let bishop_moves = self.bishop_moves[idx]; + self.queen_moves[idx] = rook_moves | bishop_moves; + } + + #[inline] + fn generate_ray(sq: BitBoard, shift: fn(&BitBoard) -> BitBoard) -> BitBoard { + let mut ray = BitBoard::empty(); + + let mut iter = shift(&sq); + while !iter.is_empty() { + ray |= iter; + iter = shift(&iter); } + + ray } - pub(super) fn knight_moves(&self, sq: Square) -> BitBoard { - self.knight_moves[sq.index() as usize] - } + library_getter!(knight_moves); + library_getter!(bishop_moves); + library_getter!(rook_moves); + library_getter!(queen_moves); + library_getter!(king_moves); } diff --git a/board/src/moves/king.rs b/board/src/moves/king.rs index 9abca90..b345e2e 100644 --- a/board/src/moves/king.rs +++ b/board/src/moves/king.rs @@ -87,13 +87,13 @@ impl<'a> KingMoveGenerator<'a> { } fn generate_bitboards(&mut self) { - let mut king = self + let king = self .position .bitboard_for_piece(Piece::king(self.color)) - .clone(); - let mut all_moves = king.shift_east_one() | king.shift_west_one(); - king |= all_moves; - all_moves |= king.shift_north_one() | king.shift_south_one(); + .occupied_squares() + .next() + .unwrap(); + let all_moves = BitBoard::king_moves(king); let empty_squares = self.position.empty_squares(); let opposing_pieces = self.position.bitboard_for_color(self.color.other());