Add 'Bitboard::iter_powerset'
This commit is contained in:
parent
2b9b637ab5
commit
fa68be533a
|
@ -3,6 +3,8 @@ use crate::utils::static_assert;
|
||||||
|
|
||||||
mod iterator;
|
mod iterator;
|
||||||
use iterator::*;
|
use iterator::*;
|
||||||
|
mod superset;
|
||||||
|
use superset::*;
|
||||||
|
|
||||||
/// Use a 64-bit number to represent a chessboard. Each bit is mapped from to a specific square, so
|
/// Use a 64-bit number to represent a chessboard. Each bit is mapped from to a specific square, so
|
||||||
/// that index 0 -> A1, 1 -> A2, ..., 63 -> H8.
|
/// that index 0 -> A1, 1 -> A2, ..., 63 -> H8.
|
||||||
|
@ -63,6 +65,15 @@ impl Bitboard {
|
||||||
pub fn is_empty(self) -> bool {
|
pub fn is_empty(self) -> bool {
|
||||||
self == Self::EMPTY
|
self == Self::EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterate over the power-set of a given [Bitboard], yielding each possible sub-set of
|
||||||
|
/// [Square] that belong to the [Bitboard]. In other words, generate all set of [Square] that
|
||||||
|
/// contain all, some, or none of the [Square] that are in the given [Bitboard].
|
||||||
|
/// If given an empty [Bitboard], yields the empty [Bitboard] back.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn iter_power_set(self) -> impl Iterator<Item = Self> {
|
||||||
|
BitboardPowerSetIterator::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure zero-cost (at least size-wise) wrapping.
|
// Ensure zero-cost (at least size-wise) wrapping.
|
||||||
|
@ -196,8 +207,10 @@ impl std::ops::Sub<Square> for Bitboard {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::board::square::*;
|
use crate::board::{square::*, File, Rank};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count() {
|
fn count() {
|
||||||
|
@ -280,4 +293,79 @@ mod test {
|
||||||
assert_eq!(Bitboard::FILES[0] - Bitboard::RANKS[0], Bitboard(0xff - 1));
|
assert_eq!(Bitboard::FILES[0] - Bitboard::RANKS[0], Bitboard(0xff - 1));
|
||||||
assert_eq!(Bitboard::FILES[0] - Square::A1, Bitboard(0xff - 1));
|
assert_eq!(Bitboard::FILES[0] - Square::A1, Bitboard(0xff - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iter_power_set_empty() {
|
||||||
|
assert_eq!(
|
||||||
|
Bitboard::EMPTY.iter_power_set().collect::<Vec<_>>(),
|
||||||
|
vec![Bitboard::EMPTY]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iter_power_set_one_square() {
|
||||||
|
for square in Square::iter() {
|
||||||
|
assert_eq!(
|
||||||
|
square
|
||||||
|
.into_bitboard()
|
||||||
|
.iter_power_set()
|
||||||
|
.collect::<HashSet<_>>(),
|
||||||
|
[Bitboard::EMPTY, square.into_bitboard()]
|
||||||
|
.into_iter()
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iter_power_set_two_squares() {
|
||||||
|
assert_eq!(
|
||||||
|
(Square::A1 | Square::H8)
|
||||||
|
.iter_power_set()
|
||||||
|
.collect::<HashSet<_>>(),
|
||||||
|
[
|
||||||
|
Bitboard::EMPTY,
|
||||||
|
Square::A1.into_bitboard(),
|
||||||
|
Square::H8.into_bitboard(),
|
||||||
|
Square::A1 | Square::H8
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iter_power_set_six_squares_exhaustive() {
|
||||||
|
let mask = (0..6)
|
||||||
|
.into_iter()
|
||||||
|
.map(Square::from_index)
|
||||||
|
.fold(Bitboard::EMPTY, |lhs, rhs| lhs | rhs);
|
||||||
|
assert_eq!(
|
||||||
|
mask.iter_power_set().collect::<HashSet<_>>(),
|
||||||
|
(0..(1 << 6))
|
||||||
|
.into_iter()
|
||||||
|
.map(Bitboard)
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iter_power_set_eight_squares_length() {
|
||||||
|
assert_eq!(
|
||||||
|
File::A
|
||||||
|
.into_bitboard()
|
||||||
|
.iter_power_set()
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.len(),
|
||||||
|
1 << 8
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Rank::First
|
||||||
|
.into_bitboard()
|
||||||
|
.iter_power_set()
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.len(),
|
||||||
|
1 << 8
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
51
src/board/bitboard/superset.rs
Normal file
51
src/board/bitboard/superset.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use super::Bitboard;
|
||||||
|
|
||||||
|
/// Iterator over a [Bitboard] mask, which yields all potential subsets of the given board.
|
||||||
|
/// In other words, for each square that belongs to the mask, this will yield all sets that do
|
||||||
|
/// contain the square, and all sets that do not.
|
||||||
|
pub struct BitboardPowerSetIterator {
|
||||||
|
/// The mask.
|
||||||
|
mask: Bitboard,
|
||||||
|
/// The "index" of the next blocker set that should be generated.
|
||||||
|
current: usize,
|
||||||
|
/// The number of blocker sets that should be generated by [BlockerIterator], i.e: 2^n with n
|
||||||
|
/// the number of squares belonging to `mask`.
|
||||||
|
total: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitboardPowerSetIterator {
|
||||||
|
pub fn new(mask: Bitboard) -> Self {
|
||||||
|
Self {
|
||||||
|
mask,
|
||||||
|
current: 0,
|
||||||
|
total: 1 << mask.count(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for BitboardPowerSetIterator {
|
||||||
|
type Item = Bitboard;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.current >= self.total {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let blockers = (0..)
|
||||||
|
.into_iter()
|
||||||
|
.zip(self.mask.into_iter())
|
||||||
|
.filter(|(index, _)| self.current & (1 << index) != 0)
|
||||||
|
.map(|(_, board)| board)
|
||||||
|
.fold(Bitboard::EMPTY, |lhs, rhs| lhs | rhs);
|
||||||
|
self.current += 1;
|
||||||
|
Some(blockers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
(self.total, Some(self.total))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for BitboardPowerSetIterator {}
|
||||||
|
|
||||||
|
impl std::iter::FusedIterator for BitboardPowerSetIterator {}
|
Loading…
Reference in a new issue