2023-12-19 11:13:06 -08:00
|
|
|
// Eryn Wells <eryn@erynwells.me>
|
|
|
|
|
2023-12-23 23:04:18 -07:00
|
|
|
mod bit_scanner;
|
|
|
|
|
|
|
|
use self::bit_scanner::BitScanner;
|
2023-12-21 08:17:06 -08:00
|
|
|
use crate::square::Square;
|
2023-12-23 16:09:58 -07:00
|
|
|
use std::fmt;
|
2023-12-23 09:16:32 -07:00
|
|
|
use std::ops::{BitAnd, BitOr, Not};
|
2023-12-21 08:17:06 -08:00
|
|
|
|
2023-12-26 11:19:13 -07:00
|
|
|
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
2023-12-23 23:04:18 -07:00
|
|
|
pub(crate) struct BitBoard(u64);
|
2023-12-21 08:17:06 -08:00
|
|
|
|
|
|
|
impl BitBoard {
|
2023-12-22 08:50:03 -08:00
|
|
|
pub fn empty() -> BitBoard {
|
|
|
|
BitBoard(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_bit_field(bits: u64) -> BitBoard {
|
|
|
|
BitBoard(bits)
|
|
|
|
}
|
|
|
|
|
2023-12-23 23:04:18 -07:00
|
|
|
pub fn is_empty(&self) -> bool {
|
2023-12-21 08:17:06 -08:00
|
|
|
self.0 == 0
|
|
|
|
}
|
|
|
|
|
2023-12-23 09:31:41 -07:00
|
|
|
pub fn has_piece_at(&self, sq: &Square) -> bool {
|
2023-12-21 08:17:06 -08:00
|
|
|
(self.0 & (1 << sq.index)) > 0
|
|
|
|
}
|
|
|
|
|
2023-12-23 09:31:41 -07:00
|
|
|
pub fn place_piece_at(&mut self, sq: &Square) {
|
2023-12-21 08:17:06 -08:00
|
|
|
self.0 |= 1 << sq.index
|
|
|
|
}
|
2023-12-21 08:17:17 -08:00
|
|
|
|
2023-12-21 08:30:48 -08:00
|
|
|
fn remove_piece_at(&mut self, sq: &Square) {
|
|
|
|
self.0 &= !(1 << sq.index)
|
|
|
|
}
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|
|
|
|
|
2023-12-23 16:09:58 -07:00
|
|
|
impl BitBoard {
|
|
|
|
const NOT_A_FILE: u64 = 0xfefefefefefefefe;
|
|
|
|
const NOT_H_FILE: u64 = 0x7f7f7f7f7f7f7f7f;
|
|
|
|
|
|
|
|
#[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_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_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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-23 23:04:18 -07:00
|
|
|
impl BitBoard {
|
2023-12-26 09:15:53 -07:00
|
|
|
pub(crate) fn occupied_squares(&self) -> impl Iterator<Item = Square> {
|
2023-12-23 23:04:18 -07:00
|
|
|
BitScanner::new(self.0)
|
|
|
|
.map(|x| u8::try_from(x))
|
|
|
|
.take_while(|x| x.is_ok())
|
|
|
|
.map(|x| Square::from_index_unsafe(x.unwrap()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-23 16:09:58 -07:00
|
|
|
impl fmt::Debug for BitBoard {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
|
|
|
let bits = self.0;
|
|
|
|
write!(f, "BitBoard({bits:064b})")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-22 08:50:03 -08:00
|
|
|
impl BitAnd for BitBoard {
|
|
|
|
type Output = BitBoard;
|
|
|
|
|
2023-12-23 09:17:07 -07:00
|
|
|
#[inline]
|
2023-12-22 08:50:03 -08:00
|
|
|
fn bitand(self, rhs: Self) -> Self {
|
|
|
|
BitBoard(self.0 & rhs.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BitAnd<&BitBoard> for BitBoard {
|
|
|
|
type Output = BitBoard;
|
|
|
|
|
2023-12-23 09:17:07 -07:00
|
|
|
#[inline]
|
2023-12-22 08:50:03 -08:00
|
|
|
fn bitand(self, rhs: &Self) -> Self {
|
|
|
|
BitBoard(self.0 & rhs.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-23 09:16:32 -07:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-22 08:50:03 -08:00
|
|
|
impl BitOr for BitBoard {
|
|
|
|
type Output = BitBoard;
|
|
|
|
|
2023-12-23 09:16:32 -07:00
|
|
|
#[inline]
|
2023-12-22 08:50:03 -08:00
|
|
|
fn bitor(self, rhs: Self) -> Self {
|
|
|
|
BitBoard(self.0 | rhs.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BitOr<&BitBoard> for BitBoard {
|
|
|
|
type Output = BitBoard;
|
|
|
|
|
2023-12-23 09:17:07 -07:00
|
|
|
#[inline]
|
2023-12-22 08:50:03 -08:00
|
|
|
fn bitor(self, rhs: &Self) -> Self {
|
|
|
|
BitBoard(self.0 | rhs.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-21 08:17:06 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::square::Square;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn is_empty() {
|
|
|
|
assert!(BitBoard(0).is_empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn has_piece_at() {
|
|
|
|
let bb = BitBoard(0b1001100);
|
|
|
|
|
2023-12-23 09:27:56 -07:00
|
|
|
let c1 = Square::from_algebraic_string("c1").expect("Unable to get square for 'c1'");
|
|
|
|
assert!(bb.has_piece_at(&c1));
|
|
|
|
|
|
|
|
let b1 = Square::from_algebraic_string("b1").expect("Unable to get square for 'b1'");
|
|
|
|
assert!(!bb.has_piece_at(&b1));
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn place_piece_at() {
|
2023-12-21 08:30:48 -08:00
|
|
|
let sq = Square::from_index(34).expect("Invalid square index");
|
|
|
|
|
|
|
|
let mut bb = BitBoard(0b1001100);
|
|
|
|
bb.place_piece_at(&sq);
|
|
|
|
assert!(bb.has_piece_at(&sq));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_piece_at() {
|
|
|
|
let sq = Square::from_index(2).expect("Invalid square index");
|
|
|
|
|
2023-12-21 08:17:06 -08:00
|
|
|
let mut bb = BitBoard(0b1001100);
|
2023-12-21 08:30:48 -08:00
|
|
|
bb.remove_piece_at(&sq);
|
|
|
|
assert!(!bb.has_piece_at(&sq));
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|
2023-12-21 08:17:17 -08:00
|
|
|
|
2023-12-23 16:09:58 -07:00
|
|
|
#[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
|
|
|
|
println!(" bb: {:?}", bb);
|
|
|
|
|
|
|
|
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"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-12-21 08:17:17 -08:00
|
|
|
#[test]
|
2023-12-23 23:04:18 -07:00
|
|
|
fn pieces() {
|
|
|
|
let bb = BitBoard(0x1001_1010); // e4
|
|
|
|
|
2023-12-26 09:15:53 -07:00
|
|
|
let mut occupied_squares = bb.occupied_squares();
|
|
|
|
assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(28)));
|
|
|
|
assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(16)));
|
|
|
|
assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(12)));
|
|
|
|
assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(4)));
|
|
|
|
assert_eq!(occupied_squares.next(), None);
|
2023-12-21 08:17:17 -08:00
|
|
|
}
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|