eschac 0.6.1

computing chess moves
Documentation
//! Sets of squares.

use crate::board::*;

use core::ops::ControlFlow;

/// A set of squares.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)]
pub struct Bitboard(pub u64);

impl Bitboard {
	/// Makes a new, empty `Bitboard`.
	#[inline]
	pub const fn new() -> Self {
		Self(0)
	}

	/// Returns `true` if the bitboard contains the given square.
	#[inline]
	pub const fn contains(&self, square: Square) -> bool {
		self.0 & (1 << square.index()) != 0
	}

	/// Returns the first square in the bitboard, if any.
	#[inline]
	pub const fn first(&self) -> Option<Square> {
		let mask = self.0;
		match mask {
			0 => None,
			_ => Some(unsafe { Square::new_unchecked(mask.trailing_zeros() as u8) }),
		}
	}

	/// Returns the last square in the bitboard, if any.
	#[inline]
	pub const fn last(&self) -> Option<Square> {
		let mask = self.0;
		match mask {
			0 => None,
			_ => Some(unsafe { Square::new_unchecked(63 ^ mask.leading_zeros() as u8) }),
		}
	}

	/// Removes the first square from the bitboard and returns it, if any.
	#[inline]
	pub const fn pop_first(&mut self) -> Option<Square> {
		let mask = &mut self.0;
		let square = match mask {
			0 => None,
			_ => Some(unsafe { Square::new_unchecked(mask.trailing_zeros() as u8) }),
		};
		*mask &= mask.wrapping_sub(1);
		square
	}

	/// Adds a square to the bitboard.
	#[inline]
	pub const fn insert(&mut self, square: Square) {
		self.0 |= 1 << square.index();
	}

	/// Removes a square from the bitboard.
	#[inline]
	pub const fn remove(&mut self, square: Square) {
		self.0 &= !(1 << square.index());
	}

	/// Returns the number of squares in the bitboard.
	#[inline]
	pub const fn len(&self) -> usize {
		self.0.count_ones() as usize
	}

	/// Returns `true` if the bitboard contains no squares.
	#[inline]
	pub const fn is_empty(&self) -> bool {
		self.0 == 0
	}

	/// Returns the mirror image of the bitboard. This is the set of the mirror images of all the
	/// squares in the bitboard.
	#[inline]
	pub const fn mirror(self) -> Self {
		let [a, b, c, d, e, f, g, h] = self.0.to_le_bytes();
		Self(u64::from_le_bytes([h, g, f, e, d, c, b, a]))
	}

	#[inline]
	pub(crate) const fn trans(&self, direction: Direction) -> Self {
		let a = File::A.bitboard().0;
		let h = File::H.bitboard().0;
		Self(match direction {
			Direction::North => self.0 << 8,
			Direction::NorthEast => (self.0 << 9) & !a,
			Direction::East => (self.0 << 1) & !a,
			Direction::SouthEast => (self.0 >> 7) & !a,
			Direction::South => self.0 >> 8,
			Direction::SouthWest => (self.0 >> 9) & !h,
			Direction::West => (self.0 >> 1) & !h,
			Direction::NorthWest => (self.0 << 7) & !h,
		})
	}
}

impl core::ops::BitOr for Bitboard {
	type Output = Self;
	#[inline]
	fn bitor(self, rhs: Self) -> Self::Output {
		Self(self.0 | rhs.0)
	}
}
impl core::ops::BitAnd for Bitboard {
	type Output = Self;
	#[inline]
	fn bitand(self, rhs: Self) -> Self::Output {
		Self(self.0 & rhs.0)
	}
}
impl core::ops::BitXor for Bitboard {
	type Output = Self;
	#[inline]
	fn bitxor(self, rhs: Self) -> Self::Output {
		Self(self.0 ^ rhs.0)
	}
}
impl core::ops::BitOrAssign for Bitboard {
	#[inline]
	fn bitor_assign(&mut self, rhs: Self) {
		self.0 |= rhs.0;
	}
}
impl core::ops::BitAndAssign for Bitboard {
	#[inline]
	fn bitand_assign(&mut self, rhs: Self) {
		self.0 &= rhs.0;
	}
}
impl core::ops::BitXorAssign for Bitboard {
	#[inline]
	fn bitxor_assign(&mut self, rhs: Self) {
		self.0 ^= rhs.0;
	}
}
impl core::ops::Not for Bitboard {
	type Output = Self;
	#[inline]
	fn not(self) -> Self::Output {
		Self(!self.0)
	}
}

impl Iterator for Bitboard {
	type Item = Square;
	#[inline]
	fn next(&mut self) -> Option<Square> {
		self.pop_first()
	}
	#[inline]
	fn size_hint(&self) -> (usize, Option<usize>) {
		let len = self.len();
		(len, Some(len))
	}
	#[inline]
	fn fold<B, F>(self, init: B, mut f: F) -> B
	where
		Self: Sized,
		F: FnMut(B, Square) -> B,
	{
		let mut mask = self.0;
		let mut acc = init;
		while mask != 0 {
			acc = f(acc, unsafe {
				Square::new_unchecked(mask.trailing_zeros() as u8)
			});
			mask &= mask.wrapping_sub(1);
		}
		acc
	}
	#[inline]
	fn for_each<F>(self, mut f: F)
	where
		Self: Sized,
		F: FnMut(Square),
	{
		self.fold((), |(), x| f(x))
	}
	#[inline]
	fn count(self) -> usize
	where
		Self: Sized,
	{
		self.len()
	}
}
impl core::iter::FusedIterator for Bitboard {}
impl core::iter::ExactSizeIterator for Bitboard {
	#[inline]
	fn len(&self) -> usize {
		self.len()
	}
}

impl Bitboard {
	#[inline]
	pub(crate) fn control_for_each<F, B>(&mut self, mut f: F) -> ControlFlow<B>
	where
		F: FnMut(Square) -> ControlFlow<B>,
	{
		let mut mask = self.0;
		while mask != 0 {
			f(unsafe { Square::new_unchecked(mask.trailing_zeros() as u8) })?;
			mask &= mask.wrapping_sub(1);
		}
		ControlFlow::Continue(())
	}
}

impl core::fmt::Debug for Bitboard {
	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
		write!(f, "Bitboard(0x{:016X})", self.0)
	}
}