2023-12-19 11:13:06 -08:00
|
|
|
// Eryn Wells <eryn@erynwells.me>
|
|
|
|
|
2024-01-01 09:25:31 -08:00
|
|
|
use super::library::{library, FILES, RANKS};
|
2024-01-06 19:41:26 -08:00
|
|
|
use super::{LeadingBitScanner, TrailingBitScanner};
|
|
|
|
use crate::{square::Direction, Square};
|
2023-12-23 16:09:58 -07:00
|
|
|
use std::fmt;
|
2023-12-29 16:34:11 -08:00
|
|
|
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, 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-28 12:14:30 -07:00
|
|
|
pub(crate) struct BitBoard(pub(super) u64);
|
2023-12-21 08:17:06 -08:00
|
|
|
|
2024-01-02 08:42:47 -08:00
|
|
|
macro_rules! moves_getter {
|
|
|
|
($getter_name:ident) => {
|
|
|
|
pub fn $getter_name(sq: Square) -> BitBoard {
|
|
|
|
library().$getter_name(sq)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-12-21 08:17:06 -08:00
|
|
|
impl BitBoard {
|
2024-01-01 09:25:31 -08:00
|
|
|
pub const fn empty() -> BitBoard {
|
2023-12-22 08:50:03 -08:00
|
|
|
BitBoard(0)
|
|
|
|
}
|
|
|
|
|
2023-12-28 21:42:18 -07:00
|
|
|
pub fn new(bits: u64) -> BitBoard {
|
2023-12-22 08:50:03 -08:00
|
|
|
BitBoard(bits)
|
|
|
|
}
|
|
|
|
|
2023-12-31 09:26:20 -08:00
|
|
|
pub fn rank(rank: usize) -> BitBoard {
|
2023-12-30 10:26:37 -08:00
|
|
|
assert!(rank < 8);
|
2023-12-31 09:26:20 -08:00
|
|
|
RANKS[rank]
|
2023-12-27 10:04:02 -07:00
|
|
|
}
|
|
|
|
|
2023-12-31 09:26:20 -08:00
|
|
|
pub fn file(file: usize) -> BitBoard {
|
2023-12-30 10:26:37 -08:00
|
|
|
assert!(file < 8);
|
2023-12-31 09:26:20 -08:00
|
|
|
FILES[file]
|
2023-12-30 10:26:37 -08:00
|
|
|
}
|
|
|
|
|
2024-01-02 08:42:47 -08:00
|
|
|
moves_getter!(knight_moves);
|
|
|
|
moves_getter!(bishop_moves);
|
|
|
|
moves_getter!(rook_moves);
|
|
|
|
moves_getter!(queen_moves);
|
|
|
|
moves_getter!(king_moves);
|
2024-01-01 09:25:31 -08:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-01-06 16:11:52 -08:00
|
|
|
pub fn has_piece_at(self, sq: Square) -> bool {
|
|
|
|
!(self & sq.into()).is_empty()
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|
|
|
|
|
2024-01-06 16:11:52 -08:00
|
|
|
pub fn place_piece_at(&mut self, sq: Square) {
|
|
|
|
let sq_bb: BitBoard = sq.into();
|
|
|
|
*self |= sq_bb
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|
2023-12-21 08:17:17 -08:00
|
|
|
|
2024-01-06 16:11:52 -08:00
|
|
|
fn remove_piece_at(&mut self, sq: Square) {
|
|
|
|
let sq_bb: BitBoard = sq.into();
|
|
|
|
*self &= !sq_bb
|
2023-12-21 08:30:48 -08:00
|
|
|
}
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|
|
|
|
|
2023-12-23 23:04:18 -07:00
|
|
|
impl BitBoard {
|
2024-01-06 19:41:26 -08:00
|
|
|
/// Return an Iterator over the occupied squares, starting from the leading
|
|
|
|
/// (most-significant bit) end of the field.
|
2023-12-26 09:15:53 -07:00
|
|
|
pub(crate) fn occupied_squares(&self) -> impl Iterator<Item = Square> {
|
2024-01-06 19:41:26 -08:00
|
|
|
LeadingBitScanner::new(self.0).map(Square::from_index)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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(Square::from_index)
|
2024-01-06 16:11:52 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Square> for BitBoard {
|
|
|
|
fn from(value: Square) -> Self {
|
|
|
|
BitBoard(1 << value as u64)
|
2023-12-23 23:04:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-29 15:37:38 -08:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-30 10:26:37 -08:00
|
|
|
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(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-23 16:09:58 -07:00
|
|
|
impl fmt::Debug for BitBoard {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
2023-12-29 15:37:38 -08:00
|
|
|
f.debug_tuple("BitBoard")
|
|
|
|
.field(&format_args!("{:064b}", self.0))
|
|
|
|
.finish()
|
2023-12-23 16:09:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-27 10:00:30 -07:00
|
|
|
macro_rules! infix_op {
|
2024-01-06 16:11:52 -08:00
|
|
|
($trait_type:ident, $func_name:ident, $left_type:ty, $right_type:ty) => {
|
2023-12-27 10:00:30 -07:00
|
|
|
impl $trait_type<$right_type> for $left_type {
|
|
|
|
type Output = BitBoard;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn $func_name(self, rhs: $right_type) -> Self::Output {
|
2024-01-06 16:11:52 -08:00
|
|
|
BitBoard($trait_type::$func_name(self.0, rhs.0))
|
2023-12-27 10:00:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2023-12-22 08:50:03 -08:00
|
|
|
}
|
|
|
|
|
2023-12-29 16:34:11 -08:00
|
|
|
macro_rules! assign_op {
|
2024-01-06 16:11:52 -08:00
|
|
|
($trait_type:ident, $func_name:ident, $left_type:ty) => {
|
2023-12-29 16:34:11 -08:00
|
|
|
impl $trait_type for $left_type {
|
|
|
|
#[inline]
|
|
|
|
fn $func_name(&mut self, rhs: Self) {
|
2024-01-06 16:11:52 -08:00
|
|
|
$trait_type::$func_name(&mut self.0, rhs.0)
|
2023-12-29 16:34:11 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-01-06 16:11:52 -08:00
|
|
|
infix_op!(BitAnd, bitand, BitBoard, BitBoard);
|
2023-12-29 16:34:11 -08:00
|
|
|
|
2024-01-06 16:11:52 -08:00
|
|
|
assign_op!(BitAndAssign, bitand_assign, BitBoard);
|
|
|
|
assign_op!(BitOrAssign, bitor_assign, BitBoard);
|
2023-12-22 08:50:03 -08:00
|
|
|
|
2024-01-06 16:11:52 -08:00
|
|
|
infix_op!(BitOr, bitor, BitBoard, BitBoard);
|
2023-12-22 08:50:03 -08:00
|
|
|
|
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-21 08:17:06 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2023-12-26 11:25:27 -07:00
|
|
|
use crate::Square;
|
2023-12-21 08:17:06 -08:00
|
|
|
|
2023-12-30 10:26:37 -08:00
|
|
|
#[test]
|
|
|
|
fn display_and_debug() {
|
|
|
|
let bb = BitBoard::file(0) | BitBoard::file(3) | BitBoard::rank(7) | BitBoard::rank(4);
|
|
|
|
println!("{}", &bb);
|
|
|
|
println!("{:?}", &bb);
|
|
|
|
}
|
|
|
|
|
2023-12-27 10:04:02 -07:00
|
|
|
#[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");
|
|
|
|
}
|
|
|
|
|
2023-12-21 08:17:06 -08:00
|
|
|
#[test]
|
|
|
|
fn is_empty() {
|
|
|
|
assert!(BitBoard(0).is_empty());
|
2023-12-27 10:28:44 -07:00
|
|
|
assert!(!BitBoard(0xFF).is_empty());
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn has_piece_at() {
|
|
|
|
let bb = BitBoard(0b1001100);
|
2024-01-06 16:11:52 -08:00
|
|
|
assert!(bb.has_piece_at(Square::C1));
|
|
|
|
assert!(!bb.has_piece_at(Square::B1));
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn place_piece_at() {
|
2024-01-06 16:11:52 -08:00
|
|
|
let sq = Square::E4;
|
2023-12-21 08:30:48 -08:00
|
|
|
let mut bb = BitBoard(0b1001100);
|
2024-01-06 16:11:52 -08:00
|
|
|
bb.place_piece_at(sq);
|
|
|
|
assert!(bb.has_piece_at(sq));
|
2023-12-21 08:30:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_piece_at() {
|
2024-01-06 16:11:52 -08:00
|
|
|
let sq = Square::A3;
|
2023-12-21 08:17:06 -08:00
|
|
|
let mut bb = BitBoard(0b1001100);
|
2024-01-06 16:11:52 -08:00
|
|
|
bb.remove_piece_at(sq);
|
|
|
|
assert!(!bb.has_piece_at(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);
|
|
|
|
}
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|
2023-12-21 08:17:17 -08:00
|
|
|
|
|
|
|
#[test]
|
2024-01-06 16:11:52 -08:00
|
|
|
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);
|
|
|
|
}
|
2023-12-21 08:17:17 -08:00
|
|
|
}
|
2023-12-21 08:17:06 -08:00
|
|
|
}
|