[bitboard] Move everything in board::bitboard to the bitboard crate
This commit is contained in:
parent
32100b9553
commit
625bfb2446
7 changed files with 25 additions and 25 deletions
122
bitboard/src/bit_scanner.rs
Normal file
122
bitboard/src/bit_scanner.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
macro_rules! bit_scanner {
|
||||
($name:ident) => {
|
||||
pub(crate) struct $name {
|
||||
bits: u64,
|
||||
shift: usize,
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub(crate) fn new(bits: u64) -> Self {
|
||||
Self { bits, shift: 0 }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bit_scanner!(LeadingBitScanner);
|
||||
bit_scanner!(TrailingBitScanner);
|
||||
|
||||
impl Iterator for LeadingBitScanner {
|
||||
type Item = usize;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let u64bits = u64::BITS as usize;
|
||||
|
||||
if self.shift == u64bits {
|
||||
return None;
|
||||
}
|
||||
|
||||
let shifted_bits = self.bits << self.shift;
|
||||
let leading_zeros = shifted_bits.leading_zeros() as usize;
|
||||
|
||||
if leading_zeros == u64bits {
|
||||
self.shift = leading_zeros;
|
||||
return None;
|
||||
}
|
||||
|
||||
let position = u64bits - (self.shift + leading_zeros + 1);
|
||||
// Shift 1 additional place to account for the 1 that `leading_zeros` found.
|
||||
self.shift += leading_zeros + 1;
|
||||
|
||||
Some(position)
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for TrailingBitScanner {
|
||||
type Item = usize;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let u64bits = u64::BITS as usize;
|
||||
|
||||
if self.shift == u64bits {
|
||||
return None;
|
||||
}
|
||||
|
||||
let shifted_bits = self.bits >> self.shift;
|
||||
let trailing_zeros = shifted_bits.trailing_zeros() as usize;
|
||||
|
||||
if trailing_zeros == u64bits {
|
||||
self.shift = trailing_zeros;
|
||||
return None;
|
||||
}
|
||||
|
||||
let position = self.shift + trailing_zeros;
|
||||
// Shift 1 additional place to account for the 1 that `leading_zeros` found.
|
||||
self.shift += trailing_zeros + 1;
|
||||
|
||||
Some(position)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn leading_zero() {
|
||||
let mut scanner = LeadingBitScanner::new(0);
|
||||
assert_eq!(scanner.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_one() {
|
||||
let mut scanner = LeadingBitScanner::new(1);
|
||||
assert_eq!(scanner.next(), Some(0));
|
||||
assert_eq!(scanner.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_complex() {
|
||||
let mut scanner = LeadingBitScanner::new(0b11000101);
|
||||
assert_eq!(scanner.next(), Some(7));
|
||||
assert_eq!(scanner.next(), Some(6));
|
||||
assert_eq!(scanner.next(), Some(2));
|
||||
assert_eq!(scanner.next(), Some(0));
|
||||
assert_eq!(scanner.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_zero() {
|
||||
let mut scanner = TrailingBitScanner::new(0);
|
||||
assert_eq!(scanner.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_one() {
|
||||
let mut scanner = TrailingBitScanner::new(1);
|
||||
assert_eq!(scanner.next(), Some(0));
|
||||
assert_eq!(scanner.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_complex() {
|
||||
let mut scanner = TrailingBitScanner::new(0b11000101);
|
||||
assert_eq!(scanner.next(), Some(0));
|
||||
assert_eq!(scanner.next(), Some(2));
|
||||
assert_eq!(scanner.next(), Some(6));
|
||||
assert_eq!(scanner.next(), Some(7));
|
||||
assert_eq!(scanner.next(), None);
|
||||
}
|
||||
}
|
352
bitboard/src/bitboard.rs
Normal file
352
bitboard/src/bitboard.rs
Normal file
|
@ -0,0 +1,352 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::library::{library, FILES, RANKS};
|
||||
use crate::LeadingBitScanner;
|
||||
use chess_core::{Direction, Square};
|
||||
use std::fmt;
|
||||
use std::ops::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)
|
||||
}
|
||||
|
||||
pub fn new(bits: u64) -> BitBoard {
|
||||
BitBoard(bits)
|
||||
}
|
||||
|
||||
pub fn rank(rank: usize) -> BitBoard {
|
||||
assert!(rank < 8);
|
||||
RANKS[rank]
|
||||
}
|
||||
|
||||
pub fn file(file: usize) -> BitBoard {
|
||||
assert!(file < 8);
|
||||
FILES[file]
|
||||
}
|
||||
|
||||
pub fn ray(sq: Square, dir: Direction) -> BitBoard {
|
||||
library().ray(sq, dir)
|
||||
}
|
||||
|
||||
moves_getter!(knight_moves);
|
||||
moves_getter!(bishop_moves);
|
||||
moves_getter!(rook_moves);
|
||||
moves_getter!(queen_moves);
|
||||
moves_getter!(king_moves);
|
||||
}
|
||||
|
||||
impl BitBoard {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
pub fn is_set(self, sq: Square) -> bool {
|
||||
let square_bitboard: BitBoard = sq.into();
|
||||
!(self & square_bitboard).is_empty()
|
||||
}
|
||||
|
||||
pub fn set_square(&mut self, sq: Square) {
|
||||
let sq_bb: BitBoard = sq.into();
|
||||
*self |= sq_bb
|
||||
}
|
||||
|
||||
pub fn clear_square(&mut self, sq: Square) {
|
||||
let sq_bb: BitBoard = sq.into();
|
||||
*self &= !sq_bb
|
||||
}
|
||||
}
|
||||
|
||||
impl BitBoard {
|
||||
/// Return an Iterator over the occupied squares, starting from the leading
|
||||
/// (most-significant bit) end of the field.
|
||||
pub(crate) fn occupied_squares(&self) -> impl Iterator<Item = Square> {
|
||||
LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) })
|
||||
}
|
||||
|
||||
/// Return an Iterator over the occupied squares, starting from the trailing
|
||||
/// (least-significant bit) end of the field.
|
||||
pub(crate) fn occupied_squares_trailing(&self) -> impl Iterator<Item = Square> {
|
||||
LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BitBoard {
|
||||
fn default() -> Self {
|
||||
BitBoard::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Square> for BitBoard {
|
||||
fn from(value: Square) -> Self {
|
||||
BitBoard(1 << value as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Square> for BitBoard {
|
||||
fn from_iter<T: IntoIterator<Item = Square>>(iter: T) -> Self {
|
||||
let mut builder = BitBoardBuilder::empty();
|
||||
|
||||
for sq in iter {
|
||||
builder = builder.square(sq)
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Binary for BitBoard {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Delegate to u64's implementation of Binary.
|
||||
fmt::Binary::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::LowerHex for BitBoard {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Delegate to u64's implementation of LowerHex.
|
||||
fmt::LowerHex::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::UpperHex for BitBoard {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Delegate to u64's implementation of UpperHex.
|
||||
fmt::UpperHex::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BitBoard {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let binary_ranks = format!("{:064b}", self.0)
|
||||
.chars()
|
||||
.rev()
|
||||
.map(|c| String::from(c))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let mut ranks_written = 0;
|
||||
for rank in binary_ranks.chunks(8).rev() {
|
||||
let joined_rank = rank.join(" ");
|
||||
write!(f, "{}", joined_rank)?;
|
||||
|
||||
ranks_written += 1;
|
||||
if ranks_written < 8 {
|
||||
write!(f, "\n")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BitBoard {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "BitBoard({:064b})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! infix_op {
|
||||
($trait_type:ident, $func_name:ident, $type:ty) => {
|
||||
infix_op!($trait_type, $func_name, $type, $type);
|
||||
infix_op!($trait_type, $func_name, $type, &$type);
|
||||
infix_op!($trait_type, $func_name, &$type, $type);
|
||||
infix_op!($trait_type, $func_name, &$type, &$type);
|
||||
};
|
||||
($trait_type:ident, $func_name:ident, $left_type:ty, $right_type:ty) => {
|
||||
impl std::ops::$trait_type<$right_type> for $left_type {
|
||||
type Output = BitBoard;
|
||||
|
||||
#[inline]
|
||||
fn $func_name(self, rhs: $right_type) -> Self::Output {
|
||||
BitBoard(std::ops::$trait_type::$func_name(self.0, rhs.0))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assign_op {
|
||||
($trait_type:ident, $func_name:ident, $type:ty) => {
|
||||
impl std::ops::$trait_type for $type {
|
||||
#[inline]
|
||||
fn $func_name(&mut self, rhs: $type) {
|
||||
std::ops::$trait_type::$func_name(&mut self.0, rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::$trait_type<&$type> for $type {
|
||||
#[inline]
|
||||
fn $func_name(&mut self, rhs: &$type) {
|
||||
std::ops::$trait_type::$func_name(&mut self.0, rhs.0)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
infix_op!(BitAnd, bitand, BitBoard);
|
||||
infix_op!(BitOr, bitor, BitBoard);
|
||||
infix_op!(BitXor, bitxor, BitBoard);
|
||||
|
||||
assign_op!(BitAndAssign, bitand_assign, BitBoard);
|
||||
assign_op!(BitOrAssign, bitor_assign, BitBoard);
|
||||
assign_op!(BitXorAssign, bitxor_assign, BitBoard);
|
||||
|
||||
impl Not for BitBoard {
|
||||
type Output = BitBoard;
|
||||
|
||||
#[inline]
|
||||
fn not(self) -> Self::Output {
|
||||
BitBoard(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for &BitBoard {
|
||||
type Output = BitBoard;
|
||||
|
||||
#[inline]
|
||||
fn not(self) -> Self::Output {
|
||||
BitBoard(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BitBoardBuilder(BitBoard);
|
||||
|
||||
impl BitBoardBuilder {
|
||||
pub const fn empty() -> BitBoardBuilder {
|
||||
BitBoardBuilder(BitBoard::empty())
|
||||
}
|
||||
|
||||
pub fn new(bits: u64) -> BitBoardBuilder {
|
||||
BitBoardBuilder(BitBoard::new(bits))
|
||||
}
|
||||
|
||||
pub fn square(mut self, square: Square) -> BitBoardBuilder {
|
||||
self.0.set_square(square);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> BitBoard {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::bitboard;
|
||||
use chess_core::Square;
|
||||
|
||||
#[test]
|
||||
fn display_and_debug() {
|
||||
let bb = BitBoard::file(0) | BitBoard::file(3) | BitBoard::rank(7) | BitBoard::rank(4);
|
||||
println!("{}", &bb);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rank() {
|
||||
assert_eq!(BitBoard::rank(0).0, 0xFF, "Rank 1");
|
||||
assert_eq!(BitBoard::rank(1).0, 0xFF00, "Rank 2");
|
||||
assert_eq!(BitBoard::rank(2).0, 0xFF0000, "Rank 3");
|
||||
assert_eq!(BitBoard::rank(3).0, 0xFF000000, "Rank 4");
|
||||
assert_eq!(BitBoard::rank(4).0, 0xFF00000000, "Rank 5");
|
||||
assert_eq!(BitBoard::rank(5).0, 0xFF0000000000, "Rank 6");
|
||||
assert_eq!(BitBoard::rank(6).0, 0xFF000000000000, "Rank 7");
|
||||
assert_eq!(BitBoard::rank(7).0, 0xFF00000000000000, "Rank 8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_empty() {
|
||||
assert!(BitBoard(0).is_empty());
|
||||
assert!(!BitBoard(0xFF).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_piece_at() {
|
||||
let bb = BitBoard(0b1001100);
|
||||
assert!(bb.is_set(Square::C1));
|
||||
assert!(!bb.is_set(Square::B1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_square() {
|
||||
let sq = Square::E4;
|
||||
let mut bb = BitBoard(0b1001100);
|
||||
bb.set_square(sq);
|
||||
assert!(bb.is_set(sq));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear_square() {
|
||||
let sq = Square::A3;
|
||||
let mut bb = BitBoard(0b1001100);
|
||||
bb.clear_square(sq);
|
||||
assert!(!bb.is_set(sq));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_rank_occupancy() {
|
||||
let bb = BitBoard(0b01010100);
|
||||
let expected_squares = [Square::G1, Square::E1, Square::C1];
|
||||
for (a, b) in bb.occupied_squares().zip(expected_squares.iter().cloned()) {
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn occupancy_spot_check() {
|
||||
let bb =
|
||||
BitBoard(0b10000000_00000000_00100000_00000100_00000000_00000000_00010000_00001000);
|
||||
|
||||
let expected_squares = [Square::H8, Square::F6, Square::C5, Square::E2, Square::D1];
|
||||
|
||||
for (a, b) in bb.occupied_squares().zip(expected_squares.iter().cloned()) {
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xor() {
|
||||
let a = bitboard![C5, G7];
|
||||
let b = bitboard![B5, G7, H3];
|
||||
|
||||
assert_eq!(a ^ b, bitboard![B5, C5, H3]);
|
||||
assert_eq!(a ^ BitBoard::empty(), a);
|
||||
assert_eq!(BitBoard::empty() ^ BitBoard::empty(), BitBoard::empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitand_assign() {
|
||||
let mut a = bitboard![C5, G7];
|
||||
let b = bitboard![B5, G7, H3];
|
||||
|
||||
a &= b;
|
||||
|
||||
assert_eq!(a, bitboard![G7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitor_assign() {
|
||||
let mut a = bitboard![C5, G7];
|
||||
let b = bitboard![B5, G7, H3];
|
||||
|
||||
a |= b;
|
||||
|
||||
assert_eq!(a, bitboard![B5, C5, G7, H3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_square() {
|
||||
assert_eq!(BitBoard::from(Square::A1), BitBoard(0b1));
|
||||
assert_eq!(BitBoard::from(Square::H8), BitBoard(1 << 63));
|
||||
}
|
||||
}
|
|
@ -1 +1,18 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
mod bit_scanner;
|
||||
mod bitboard;
|
||||
mod library;
|
||||
mod shifts;
|
||||
|
||||
pub(crate) use bit_scanner::{LeadingBitScanner, TrailingBitScanner};
|
||||
pub(crate) use bitboard::{BitBoard, BitBoardBuilder};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bitboard {
|
||||
($($sq:ident),* $(,)?) => {
|
||||
$crate::bitboard::BitBoardBuilder::empty()
|
||||
$(.square(chess_core::Square::$sq))*
|
||||
.build()
|
||||
};
|
||||
}
|
||||
|
|
189
bitboard/src/library.rs
Normal file
189
bitboard/src/library.rs
Normal file
|
@ -0,0 +1,189 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use super::BitBoard;
|
||||
use chess_core::{Direction, Square};
|
||||
use std::sync::Once;
|
||||
|
||||
pub(super) const RANKS: [BitBoard; 8] = [
|
||||
BitBoard(0xFF << 0 * 8),
|
||||
BitBoard(0xFF << 1 * 8),
|
||||
BitBoard(0xFF << 2 * 8),
|
||||
BitBoard(0xFF << 3 * 8),
|
||||
BitBoard(0xFF << 4 * 8),
|
||||
BitBoard(0xFF << 5 * 8),
|
||||
BitBoard(0xFF << 6 * 8),
|
||||
BitBoard(0xFF << 7 * 8),
|
||||
];
|
||||
|
||||
pub(super) const FILES: [BitBoard; 8] = [
|
||||
BitBoard(0x0101010101010101 << 0),
|
||||
BitBoard(0x0101010101010101 << 1),
|
||||
BitBoard(0x0101010101010101 << 2),
|
||||
BitBoard(0x0101010101010101 << 3),
|
||||
BitBoard(0x0101010101010101 << 4),
|
||||
BitBoard(0x0101010101010101 << 5),
|
||||
BitBoard(0x0101010101010101 << 6),
|
||||
BitBoard(0x0101010101010101 << 7),
|
||||
];
|
||||
|
||||
pub(super) const LIGHT_SQUARES: BitBoard =
|
||||
BitBoard(0x5555 | 0x5555 << 16 | 0x5555 << 32 | 0x5555 << 48);
|
||||
pub(super) const DARK_SQUARES: BitBoard = BitBoard(!LIGHT_SQUARES.0);
|
||||
|
||||
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.init();
|
||||
});
|
||||
|
||||
&MOVE_LIBRARY
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! library_getter {
|
||||
($name:ident) => {
|
||||
pub(super) fn $name(&self, sq: Square) -> BitBoard {
|
||||
self.$name[sq as usize]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct MoveLibrary {
|
||||
// Rays
|
||||
rays: [[BitBoard; 8]; Square::NUM],
|
||||
|
||||
// Piecewise move tables
|
||||
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 {
|
||||
rays: [[BitBoard::empty(); 8]; Square::NUM],
|
||||
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 init(&mut self) {
|
||||
for sq in Square::ALL {
|
||||
self.init_orthogonal_rays(sq);
|
||||
self.init_diagonal_rays(sq);
|
||||
self.init_knight_moves(sq as usize);
|
||||
self.init_bishop_moves(sq);
|
||||
self.init_rook_moves(sq);
|
||||
self.init_queen_moves(sq);
|
||||
self.init_king_moves(sq as usize);
|
||||
}
|
||||
}
|
||||
|
||||
fn init_orthogonal_rays(&mut self, sq: Square) {
|
||||
let sq_bb: BitBoard = sq.into();
|
||||
let rays = &mut self.rays[sq as usize];
|
||||
rays[Direction::North as usize] = Self::generate_ray(sq_bb, BitBoard::shift_north_one);
|
||||
rays[Direction::South as usize] = Self::generate_ray(sq_bb, BitBoard::shift_south_one);
|
||||
rays[Direction::East as usize] = Self::generate_ray(sq_bb, BitBoard::shift_east_one);
|
||||
rays[Direction::West as usize] = Self::generate_ray(sq_bb, BitBoard::shift_west_one);
|
||||
}
|
||||
|
||||
fn init_diagonal_rays(&mut self, sq: Square) {
|
||||
let sq_bb: BitBoard = sq.into();
|
||||
let rays = &mut self.rays[sq as usize];
|
||||
rays[Direction::NorthEast as usize] =
|
||||
Self::generate_ray(sq_bb, BitBoard::shift_north_east_one);
|
||||
rays[Direction::NorthWest as usize] =
|
||||
Self::generate_ray(sq_bb, BitBoard::shift_north_west_one);
|
||||
rays[Direction::SouthWest as usize] =
|
||||
Self::generate_ray(sq_bb, BitBoard::shift_south_west_one);
|
||||
rays[Direction::SouthEast as usize] =
|
||||
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 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 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[idx] = attacks;
|
||||
}
|
||||
|
||||
fn init_bishop_moves(&mut self, sq: Square) {
|
||||
let rays = self.rays[sq as usize];
|
||||
self.bishop_moves[sq as usize] = rays[Direction::NorthWest as usize]
|
||||
| rays[Direction::NorthEast as usize]
|
||||
| rays[Direction::SouthEast as usize]
|
||||
| rays[Direction::SouthWest as usize];
|
||||
}
|
||||
|
||||
fn init_rook_moves(&mut self, sq: Square) {
|
||||
let rays = self.rays[sq as usize];
|
||||
self.rook_moves[sq as usize] = rays[Direction::North as usize]
|
||||
| rays[Direction::East as usize]
|
||||
| rays[Direction::South as usize]
|
||||
| rays[Direction::West as usize];
|
||||
}
|
||||
|
||||
fn init_queen_moves(&mut self, sq: Square) {
|
||||
let rook_moves = self.rook_moves[sq as usize];
|
||||
let bishop_moves = self.bishop_moves[sq as usize];
|
||||
self.queen_moves[sq as usize] = 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 ray(&self, sq: Square, dir: Direction) -> BitBoard {
|
||||
self.rays[sq as usize][dir as usize]
|
||||
}
|
||||
|
||||
library_getter!(knight_moves);
|
||||
library_getter!(bishop_moves);
|
||||
library_getter!(rook_moves);
|
||||
library_getter!(queen_moves);
|
||||
library_getter!(king_moves);
|
||||
}
|
123
bitboard/src/shifts.rs
Normal file
123
bitboard/src/shifts.rs
Normal file
|
@ -0,0 +1,123 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use super::BitBoard;
|
||||
|
||||
impl BitBoard {
|
||||
const NOT_A_FILE: u64 = 0xfefefefefefefefe;
|
||||
const NOT_H_FILE: u64 = 0x7f7f7f7f7f7f7f7f;
|
||||
|
||||
#[inline]
|
||||
pub fn shift_north(&self, n: u8) -> BitBoard {
|
||||
BitBoard(self.0 << (8 * n))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_north_one(&self) -> BitBoard {
|
||||
BitBoard(self.0 << 8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_north_east_one(&self) -> BitBoard {
|
||||
BitBoard(self.0 << 9 & BitBoard::NOT_A_FILE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_east(&self, n: u8) -> BitBoard {
|
||||
// TODO: Implement a bounds check here.
|
||||
BitBoard(self.0 << n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_east_one(&self) -> BitBoard {
|
||||
BitBoard(self.0 << 1 & BitBoard::NOT_A_FILE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_south_east_one(&self) -> BitBoard {
|
||||
BitBoard(self.0 >> 7 & BitBoard::NOT_A_FILE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_south(&self, n: u8) -> BitBoard {
|
||||
BitBoard(self.0 >> (8 * n))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_south_one(&self) -> BitBoard {
|
||||
BitBoard(self.0 >> 8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_south_west_one(&self) -> BitBoard {
|
||||
BitBoard(self.0 >> 9 & BitBoard::NOT_H_FILE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_west_one(&self) -> BitBoard {
|
||||
BitBoard(self.0 >> 1 & BitBoard::NOT_H_FILE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shift_north_west_one(&self) -> BitBoard {
|
||||
BitBoard(self.0 << 7 & BitBoard::NOT_H_FILE)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cardinal_direction_shifts() {
|
||||
let bb = BitBoard(0x1000_0000); // e4
|
||||
|
||||
assert_eq!(bb.shift_north_one(), BitBoard(0x0010_0000_0000), "North");
|
||||
assert_eq!(bb.shift_east_one(), BitBoard(0x2000_0000), "East");
|
||||
assert_eq!(bb.shift_south_one(), BitBoard(0x0010_0000), "South");
|
||||
assert_eq!(bb.shift_west_one(), BitBoard(0x0800_0000), "West");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intercardinal_direction_shifts() {
|
||||
let bb = BitBoard(0x1000_0000); // e4
|
||||
|
||||
assert_eq!(
|
||||
bb.shift_north_east_one(),
|
||||
BitBoard(0x0020_0000_0000),
|
||||
"North East"
|
||||
);
|
||||
assert_eq!(
|
||||
bb.shift_south_east_one(),
|
||||
BitBoard(0x0020_0000),
|
||||
"South East"
|
||||
);
|
||||
assert_eq!(
|
||||
bb.shift_south_west_one(),
|
||||
BitBoard(0x0008_0000),
|
||||
"South West"
|
||||
);
|
||||
assert_eq!(
|
||||
bb.shift_north_west_one(),
|
||||
BitBoard(0x0008_0000_0000),
|
||||
"North West"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shift_n() {
|
||||
assert_eq!(
|
||||
BitBoard(0x0008_0000_0000).shift_north(2),
|
||||
BitBoard(0x0008_0000_0000_0000)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
BitBoard(0x0008_0000_0000).shift_east(2),
|
||||
BitBoard(0x0020_0000_0000)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
BitBoard(0x0008_0000_0000).shift_south(2),
|
||||
BitBoard(0x0008_0000)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue