use crate::{File, Rank, Square};
use core::ops::*;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
pub struct BitBoard(
pub u128,
);
macro_rules! impl_math_ops {
($($trait:ident, $fn:ident;)*) => {$(
impl $trait for BitBoard {
type Output = Self;
#[inline(always)]
fn $fn(self, rhs: Self) -> Self::Output {
self.$fn(rhs)
}
}
)*};
}
impl_math_ops! {
BitAnd, bitand;
BitOr, bitor;
BitXor, bitxor;
}
macro_rules! impl_math_assign_ops {
($($trait:ident, $fn:ident;)*) => {$(
impl $trait for BitBoard {
#[inline(always)]
fn $fn(&mut self, rhs: Self) {
$trait::$fn(&mut self.0, rhs.0)
}
}
)*};
}
impl_math_assign_ops! {
BitAndAssign, bitand_assign;
BitOrAssign, bitor_assign;
BitXorAssign, bitxor_assign;
}
impl Sub for BitBoard {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
self & !rhs
}
}
impl SubAssign for BitBoard {
#[inline(always)]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Not for BitBoard {
type Output = Self;
#[inline(always)]
fn not(self) -> Self::Output {
self.not()
}
}
impl Shl<usize> for BitBoard {
type Output = BitBoard;
#[inline(always)]
fn shl(self, rhs: usize) -> BitBoard {
self.shl(rhs)
}
}
impl Shr<usize> for BitBoard {
type Output = BitBoard;
#[inline(always)]
fn shr(self, rhs: usize) -> BitBoard {
self.shr(rhs)
}
}
macro_rules! impl_convert {
($($type:ty),*) => {$(
impl From<$type> for BitBoard {
fn from(value: $type) -> Self {
value.bitboard()
}
}
)*};
}
impl_convert!(File, Rank, Square);
impl BitBoard {
#[inline(always)]
pub const fn not(self) -> Self {
Self(!self.0 & BitBoard::BOARD_MASK)
}
#[inline(always)]
pub const fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
#[inline(always)]
pub const fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
#[inline(always)]
pub const fn bitxor(self, rhs: Self) -> Self {
Self(self.0 ^ rhs.0)
}
#[inline(always)]
pub const fn dec(self) -> Self {
Self::new(self.0.wrapping_sub(1))
}
#[inline(always)]
pub const fn shl(self, rhs: usize) -> Self {
if rhs == 0 {
self
} else if rhs >= 9 {
BitBoard::EMPTY
} else {
BitBoard::new((self.0 << rhs) & Rank::SOUTH[rhs - 1].0)
}
}
#[inline(always)]
pub const fn shr(self, rhs: usize) -> Self {
if rhs == 0 {
self
} else if rhs >= 9 {
BitBoard::EMPTY
} else {
BitBoard::new((self.0 >> rhs) & Rank::NORTH[9 - rhs].0)
}
}
#[inline(always)]
pub const fn shift_north(self, dy: usize) -> Self {
self.shr(dy)
}
#[inline(always)]
pub const fn shift_south(self, dy: usize) -> Self {
self.shl(dy)
}
#[inline(always)]
pub const fn shift_along_file(self, dy: i32) -> Self {
if dy < -9 || dy > 9 {
panic!("Shift amount out of range");
}
if dy <= 0 {
self.shr(-dy as usize)
} else {
self.shl(dy as usize)
}
}
#[inline(always)]
pub const fn shift_along_rank(self, dx: i32) -> Self {
if dx < -9 || dx > 9 {
panic!("Shift amount out of range");
}
if dx <= 0 {
self.shift_east(-dx as usize)
} else {
self.shift_west(dx as usize)
}
}
#[inline(always)]
pub const fn shift_east(self, dx: usize) -> Self {
BitBoard(self.0 >> (9 * dx))
}
#[inline(always)]
pub const fn shift_west(self, dx: usize) -> Self {
BitBoard((self.0 << (9 * dx)) & BitBoard::BOARD_MASK)
}
pub const fn shift(self, from: Square, to: Square) -> Self {
let dx = to.file() as i32 - from.file() as i32;
let dy = to.rank() as i32 - from.rank() as i32;
self.shift_along_file(dy).shift_along_rank(dx)
}
}
impl BitBoard {
pub const BOARD_MASK: u128 = (1 << Square::NUM) - 1;
#[inline(always)]
pub const fn new(value: u128) -> Self {
Self(value & Self::BOARD_MASK)
}
pub const EMPTY: Self = Self(0);
pub const FULL: Self = Self::new(!0);
pub const EDGES: Self = Self::__EDGES;
const __EDGES: Self = bitboard! {
X X X X X X X X X
X . . . . . . . X
X . . . . . . . X
X . . . . . . . X
X . . . . . . . X
X . . . . . . . X
X . . . . . . . X
X . . . . . . . X
X X X X X X X X X
};
pub const INNER: Self = Self::__INNER;
const __INNER: Self = bitboard! {
. . . . . . . . .
. X X X X X X X .
. X X X X X X X .
. X X X X X X X .
. X X X X X X X .
. X X X X X X X .
. X X X X X X X .
. X X X X X X X .
. . . . . . . . .
};
pub const CORNERS: Self = Self::__CORNERS;
const __CORNERS: Self = bitboard! {
X . . . . . . . X
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
X . . . . . . . X
};
#[inline(always)]
pub const fn flip_files(self) -> Self {
const FILE_MASKS: [u128; 9] = [
0x1FF, 0x1FF << 9, 0x1FF << 18, 0x1FF << 27, 0x1FF << 36, 0x1FF << 45, 0x1FF << 54, 0x1FF << 63, 0x1FF << 72, ];
Self::new(
((self.0 & FILE_MASKS[0]) << 72)
| ((self.0 & FILE_MASKS[1]) << 54)
| ((self.0 & FILE_MASKS[2]) << 36)
| ((self.0 & FILE_MASKS[3]) << 18)
| (self.0 & FILE_MASKS[4])
| ((self.0 & FILE_MASKS[5]) >> 18)
| ((self.0 & FILE_MASKS[6]) >> 36)
| ((self.0 & FILE_MASKS[7]) >> 54)
| ((self.0 & FILE_MASKS[8]) >> 72),
)
}
#[inline(always)]
pub const fn flip_ranks(self) -> Self {
const RANK_ONE: u128 = 0x1008040201008040201;
const RANK_MASKS: [u128; 9] = [
RANK_ONE,
RANK_ONE << 1,
RANK_ONE << 2,
RANK_ONE << 3,
RANK_ONE << 4,
RANK_ONE << 5,
RANK_ONE << 6,
RANK_ONE << 7,
RANK_ONE << 8,
];
Self::new(
((self.0 & RANK_MASKS[0]) << 8)
| ((self.0 & RANK_MASKS[1]) << 6)
| ((self.0 & RANK_MASKS[2]) << 4)
| ((self.0 & RANK_MASKS[3]) << 2)
| (self.0 & RANK_MASKS[4])
| ((self.0 & RANK_MASKS[5]) >> 2)
| ((self.0 & RANK_MASKS[6]) >> 4)
| ((self.0 & RANK_MASKS[7]) >> 6)
| ((self.0 & RANK_MASKS[8]) >> 8),
)
}
#[inline(always)]
pub const fn rotate(self) -> Self {
Self(self.0.reverse_bits() >> (128 - Square::NUM))
}
#[inline(always)]
pub const fn rev(self) -> Self {
Self(self.0.reverse_bits())
}
#[inline(always)]
pub const fn len(self) -> u32 {
self.0.count_ones()
}
#[inline(always)]
pub const fn count_ones(self) -> u32 {
self.0.count_ones()
}
#[inline(always)]
pub const fn count_zeros(self) -> u32 {
self.0.count_zeros() - 47
}
#[inline(always)]
pub const fn has(self, square: Square) -> bool {
self.0 & (1u128 << square as usize) != 0
}
#[inline(always)]
pub const fn rm(self, square: Square) -> Self {
self.bitand(square.bitboard().not())
}
#[inline(always)]
pub const fn is_disjoint(self, other: BitBoard) -> bool {
self.0 & other.0 == Self::EMPTY.0
}
#[inline(always)]
pub const fn is_subset(self, other: BitBoard) -> bool {
other.0 & self.0 == self.0
}
#[inline(always)]
pub const fn is_superset(self, other: BitBoard) -> bool {
self.0 & other.0 == other.0
}
#[inline(always)]
pub const fn is_empty(self) -> bool {
self.0 == Self::EMPTY.0
}
#[inline(always)]
pub const fn next_square(self) -> Option<Square> {
if self.0 > 0 {
Some(Square::index_const(self.0.trailing_zeros() as usize))
} else {
None
}
}
#[inline(always)]
pub fn iter(self) -> BitBoardIter {
BitBoardIter(self)
}
#[inline(always)]
pub fn iter_subsets(self) -> BitBoardSubsetIter {
BitBoardSubsetIter {
set: self,
subset: Self::EMPTY,
finished: false,
}
}
}
impl IntoIterator for BitBoard {
type Item = Square;
type IntoIter = BitBoardIter;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl FromIterator<Square> for BitBoard {
fn from_iter<T: IntoIterator<Item = Square>>(iter: T) -> Self {
iter.into_iter()
.fold(Self::EMPTY, |bb, sq| bb | sq.bitboard())
}
}
pub struct BitBoardIter(BitBoard);
impl Iterator for BitBoardIter {
type Item = Square;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let square = self.0.next_square();
if let Some(square) = square {
self.0 ^= square.bitboard();
}
square
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
}
impl ExactSizeIterator for BitBoardIter {
#[inline(always)]
fn len(&self) -> usize {
self.0.len() as usize
}
}
pub struct BitBoardSubsetIter {
set: BitBoard,
subset: BitBoard,
finished: bool,
}
impl Iterator for BitBoardSubsetIter {
type Item = BitBoard;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
let current = self.subset;
self.subset.0 = self.subset.0.wrapping_sub(self.set.0) & self.set.0;
self.finished = self.subset.is_empty();
Some(current)
}
}
#[macro_export]
macro_rules! bitboard {
( $a9:tt $a8:tt $a7:tt $a6:tt $a5:tt $a4:tt $a3:tt $a2:tt $a1:tt
$b9:tt $b8:tt $b7:tt $b6:tt $b5:tt $b4:tt $b3:tt $b2:tt $b1:tt
$c9:tt $c8:tt $c7:tt $c6:tt $c5:tt $c4:tt $c3:tt $c2:tt $c1:tt
$d9:tt $d8:tt $d7:tt $d6:tt $d5:tt $d4:tt $d3:tt $d2:tt $d1:tt
$e9:tt $e8:tt $e7:tt $e6:tt $e5:tt $e4:tt $e3:tt $e2:tt $e1:tt
$f9:tt $f8:tt $f7:tt $f6:tt $f5:tt $f4:tt $f3:tt $f2:tt $f1:tt
$g9:tt $g8:tt $g7:tt $g6:tt $g5:tt $g4:tt $g3:tt $g2:tt $g1:tt
$h9:tt $h8:tt $h7:tt $h6:tt $h5:tt $h4:tt $h3:tt $h2:tt $h1:tt
$i9:tt $i8:tt $i7:tt $i6:tt $i5:tt $i4:tt $i3:tt $i2:tt $i1:tt
) => {
$crate::bitboard! { @__inner
$a1 $b1 $c1 $d1 $e1 $f1 $g1 $h1 $i1
$a2 $b2 $c2 $d2 $e2 $f2 $g2 $h2 $i2
$a3 $b3 $c3 $d3 $e3 $f3 $g3 $h3 $i3
$a4 $b4 $c4 $d4 $e4 $f4 $g4 $h4 $i4
$a5 $b5 $c5 $d5 $e5 $f5 $g5 $h5 $i5
$a6 $b6 $c6 $d6 $e6 $f6 $g6 $h6 $i6
$a7 $b7 $c7 $d7 $e7 $f7 $g7 $h7 $i7
$a8 $b8 $c8 $d8 $e8 $f8 $g8 $h8 $i8
$a9 $b9 $c9 $d9 $e9 $f9 $g9 $h9 $i9
}
};
(@__inner $($occupied:tt)*) => {{
const BITBOARD: $crate::BitBoard = {
let mut index = 0;
let mut bitboard = $crate::BitBoard::EMPTY;
$(
if $crate::bitboard!(@__square $occupied) {
bitboard.0 |= 1 << index;
}
index += 1;
)*
let _ = index;
bitboard
};
BITBOARD
}};
(@__square X) => { true };
(@__square .) => { false };
(@__square *) => { false };
(@__square $token:tt) => {
compile_error!(
concat!(
"Expected only `X` or `.` tokens, found `",
stringify!($token),
"`"
)
)
};
($($token:tt)*) => {
compile_error!("Expected 81 squares")
};
}
pub use bitboard;
impl core::fmt::Debug for BitBoard {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if f.alternate() {
write!(f, "bitboard! {{")?;
for &rank in Rank::ALL.iter() {
write!(f, "\n ")?;
for &file in File::ALL.iter().rev() {
if self.has(Square::new(file, rank)) {
write!(f, " X")?;
} else {
write!(f, " .")?;
}
}
}
write!(f, "\n}}")?;
Ok(())
} else {
write!(f, "BitBoard({:#018X})", self.0)
}
}
}