BitBoard::file returns a BitBoard representing the 0-indexed file. fmt::Display prints a grid of bits in the standard orientation (white on bottom, left to right) Add asserts to the rank and file constructors to catch out of bounds arguments.
233 lines
6.2 KiB
Rust
233 lines
6.2 KiB
Rust
// Eryn Wells <eryn@erynwells.me>
|
|
|
|
use super::BitScanner;
|
|
use crate::Square;
|
|
use std::fmt;
|
|
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
|
|
|
|
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
|
pub(crate) struct BitBoard(pub(super) u64);
|
|
|
|
impl BitBoard {
|
|
pub fn empty() -> BitBoard {
|
|
BitBoard(0)
|
|
}
|
|
|
|
pub fn new(bits: u64) -> BitBoard {
|
|
BitBoard(bits)
|
|
}
|
|
|
|
pub fn rank(rank: u8) -> BitBoard {
|
|
assert!(rank < 8);
|
|
BitBoard(0xFF).shift_north(rank)
|
|
}
|
|
|
|
pub fn file(file: u8) -> BitBoard {
|
|
assert!(file < 8);
|
|
BitBoard(0x0101010101010101).shift_east(file)
|
|
}
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
self.0 == 0
|
|
}
|
|
|
|
pub fn has_piece_at(&self, sq: &Square) -> bool {
|
|
(self.0 & (1 << sq.index())) > 0
|
|
}
|
|
|
|
pub fn place_piece_at(&mut self, sq: &Square) {
|
|
self.0 |= 1 << sq.index()
|
|
}
|
|
|
|
fn remove_piece_at(&mut self, sq: &Square) {
|
|
self.0 &= !(1 << sq.index())
|
|
}
|
|
}
|
|
|
|
impl BitBoard {
|
|
pub(crate) fn occupied_squares(&self) -> impl Iterator<Item = Square> {
|
|
BitScanner::new(self.0)
|
|
.map(|x| u8::try_from(x))
|
|
.take_while(|x| x.is_ok())
|
|
.map(|x| Square::from_index_unsafe(x.unwrap()))
|
|
}
|
|
}
|
|
|
|
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> {
|
|
f.debug_tuple("BitBoard")
|
|
.field(&format_args!("{:064b}", self.0))
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
macro_rules! infix_op {
|
|
($trait_type:ident, $func_name:ident, $left_type:ty, $right_type:ty, $op:tt) => {
|
|
impl $trait_type<$right_type> for $left_type {
|
|
type Output = BitBoard;
|
|
|
|
#[inline]
|
|
fn $func_name(self, rhs: $right_type) -> Self::Output {
|
|
BitBoard(self.0 $op rhs.0)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! assign_op {
|
|
($trait_type:ident, $func_name:ident, $left_type:ty, $op:tt) => {
|
|
impl $trait_type for $left_type {
|
|
#[inline]
|
|
fn $func_name(&mut self, rhs: Self) {
|
|
self.0 $op rhs.0
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
infix_op!(BitAnd, bitand, BitBoard, BitBoard, &);
|
|
infix_op!(BitAnd, bitand, &BitBoard, BitBoard, &);
|
|
infix_op!(BitAnd, bitand, BitBoard, &BitBoard, &);
|
|
infix_op!(BitAnd, bitand, &BitBoard, &BitBoard, &);
|
|
|
|
assign_op!(BitAndAssign, bitand_assign, BitBoard, &=);
|
|
assign_op!(BitOrAssign, bitor_assign, BitBoard, |=);
|
|
|
|
infix_op!(BitOr, bitor, BitBoard, BitBoard, |);
|
|
infix_op!(BitOr, bitor, &BitBoard, BitBoard, |);
|
|
infix_op!(BitOr, bitor, BitBoard, &BitBoard, |);
|
|
infix_op!(BitOr, bitor, &BitBoard, &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)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::Square;
|
|
|
|
#[test]
|
|
fn display_and_debug() {
|
|
let bb = BitBoard::file(0) | BitBoard::file(3) | BitBoard::rank(7) | BitBoard::rank(4);
|
|
println!("{}", &bb);
|
|
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);
|
|
|
|
let c1 = Square::from_algebraic_str("c1").expect("Unable to get square for 'c1'");
|
|
assert!(bb.has_piece_at(&c1));
|
|
|
|
let b1 = Square::from_algebraic_str("b1").expect("Unable to get square for 'b1'");
|
|
assert!(!bb.has_piece_at(&b1));
|
|
}
|
|
|
|
#[test]
|
|
fn place_piece_at() {
|
|
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");
|
|
|
|
let mut bb = BitBoard(0b1001100);
|
|
bb.remove_piece_at(&sq);
|
|
assert!(!bb.has_piece_at(&sq));
|
|
}
|
|
|
|
#[test]
|
|
fn pieces() {
|
|
let bb = BitBoard(0x1001_1010); // e4
|
|
|
|
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);
|
|
}
|
|
}
|