1
0
Fork 0

wip: introduce MoveSet

This commit is contained in:
Paul-Nicolas Madelaine 2025-12-17 23:03:20 +01:00
parent 42ccddc12b
commit a8b7b9ca63
2 changed files with 245 additions and 209 deletions

View file

@ -120,11 +120,8 @@ impl<'l> Move<'l> {
self.to
}
#[inline]
fn extend<I>(&mut self, iter: I) -> ControlFlow<Infallible>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
iter.for_each(|raw| {
fn extend(&mut self, s: MoveSet) -> ControlFlow<Infallible> {
s.for_each(|raw| {
debug_assert!(raw.role() as u8 == ROLE);
debug_assert!(self.to.contains(raw.to()));
self.candidates.insert(raw.from);
@ -236,11 +233,8 @@ impl<'l> MoveGen<Infallible> for Moves<'l> {
ControlFlow::Continue(())
}
#[inline]
fn extend<I>(&mut self, iter: I) -> ControlFlow<Infallible>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
iter.for_each(|raw| unsafe { self.array.push_unchecked(raw) });
fn extend(&mut self, s: MoveSet) -> ControlFlow<Infallible> {
s.for_each(|raw| unsafe { self.array.push_unchecked(raw) });
ControlFlow::Continue(())
}
}

View file

@ -9,7 +9,6 @@ use crate::setup::*;
use crate::uci::*;
use core::convert::Infallible;
use core::iter::{ExactSizeIterator, FusedIterator};
use core::ops::ControlFlow;
/// **A chess position.**
@ -118,11 +117,8 @@ impl Position {
}
}
impl MoveGen<Infallible> for MoveGenImpl {
fn extend<I>(&mut self, iter: I) -> ControlFlow<Infallible>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
self.len += iter.len();
fn extend(&mut self, s: MoveSet) -> ControlFlow<Infallible> {
self.len += s.len();
ControlFlow::Continue(())
}
}
@ -322,19 +318,17 @@ impl Position {
self.to
}
#[inline]
fn extend<I>(&mut self, iter: I) -> ControlFlow<RawMove>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
for raw in iter {
fn extend(&mut self, s: MoveSet) -> ControlFlow<RawMove> {
let mut res = ControlFlow::Continue(());
s.for_each(|raw| {
debug_assert!(raw.role() as u8 == ROLE);
debug_assert!(self.from.contains(raw.from()));
debug_assert!(self.to.contains(raw.to()));
if raw.role == self.role {
return ControlFlow::Break(raw);
res = ControlFlow::Break(raw);
}
}
ControlFlow::Continue(())
});
res
}
}
let UciMove {
@ -393,11 +387,8 @@ impl Position {
self.to
}
#[inline]
fn extend<I>(&mut self, iter: I) -> ControlFlow<Infallible>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
iter.for_each(|raw| {
fn extend(&mut self, s: MoveSet) -> ControlFlow<Infallible> {
s.for_each(|raw| {
debug_assert!(raw.role() as u8 == ROLE);
debug_assert!(self.from.contains(raw.from()));
debug_assert!(self.to.contains(raw.to()));
@ -567,6 +558,194 @@ impl RawMove {
}
}
pub(crate) enum MoveSet {
CastleShort {
from: Square,
to: Square,
},
CastleLong {
from: Square,
to: Square,
},
KingMove {
from: Square,
to: Bitboard,
},
PieceMove {
role: Role,
from: Square,
to: Bitboard,
},
PawnAttack {
from: Direction,
to: Bitboard,
},
PawnAdvance {
from: Direction,
to: Bitboard,
},
PawnDoubleAdvance {
from: Direction,
to: Bitboard,
},
EnPassant {
from: Square,
to: Square,
},
}
impl MoveSet {
#[inline]
pub fn len(&self) -> usize {
match self {
Self::CastleShort { .. } | Self::CastleLong { .. } | Self::EnPassant { .. } => 1,
Self::KingMove { to, .. }
| Self::PieceMove { to, .. }
| Self::PawnDoubleAdvance { to, .. } => to.len(),
Self::PawnAdvance { to, .. } | Self::PawnAttack { to, .. } => {
let mask = Rank::First.bitboard() | Rank::Eighth.bitboard();
to.len() + (*to & mask).len() * 3
}
}
}
#[inline]
pub fn is_empty(&self) -> bool {
match self {
Self::CastleShort { .. } | Self::CastleLong { .. } | Self::EnPassant { .. } => false,
Self::KingMove { to, .. }
| Self::PieceMove { to, .. }
| Self::PawnDoubleAdvance { to, .. }
| Self::PawnAdvance { to, .. }
| Self::PawnAttack { to, .. } => to.is_empty(),
}
}
#[inline]
pub fn for_each<F: FnMut(RawMove)>(self, mut f: F) {
match self {
Self::CastleShort { from, to } => f(RawMove {
kind: MoveType::CastleShort,
role: Role::King,
from,
to,
}),
Self::CastleLong { from, to } => f(RawMove {
kind: MoveType::CastleLong,
role: Role::King,
from,
to,
}),
Self::EnPassant { from, to } => f(RawMove {
kind: MoveType::EnPassant,
role: Role::Pawn,
from,
to,
}),
Self::KingMove { from, to } => to.for_each(|to| {
f(RawMove {
kind: MoveType::KingMove,
role: Role::King,
from,
to,
})
}),
Self::PieceMove { role, from, to } => to.for_each(|to| {
f(RawMove {
kind: MoveType::PieceMove,
role,
from,
to,
})
}),
Self::PawnDoubleAdvance { from, to } => to.for_each(|to| {
f(RawMove {
kind: MoveType::PawnDoubleAdvance,
role: Role::Pawn,
from: unsafe { to.trans_unchecked(from).trans_unchecked(from) },
to,
})
}),
Self::PawnAdvance { from, to } => {
let mask = Rank::First.bitboard() | Rank::Eighth.bitboard();
(to & !mask).for_each(|to| {
f(RawMove {
kind: MoveType::PawnAdvance,
role: Role::Pawn,
from: unsafe { to.trans_unchecked(from) },
to,
})
});
(to & mask).for_each(|to| {
let kind = MoveType::PawnAdvancePromotion;
let from = unsafe { to.trans_unchecked(from) };
f(RawMove {
kind,
role: Role::Queen,
from,
to,
});
f(RawMove {
kind,
role: Role::Rook,
from,
to,
});
f(RawMove {
kind,
role: Role::Bishop,
from,
to,
});
f(RawMove {
kind,
role: Role::Knight,
from,
to,
});
});
}
Self::PawnAttack { from, to } => {
let mask = Rank::First.bitboard() | Rank::Eighth.bitboard();
(to & !mask).for_each(|to| {
f(RawMove {
kind: MoveType::PawnAttack,
role: Role::Pawn,
from: unsafe { to.trans_unchecked(from) },
to,
})
});
(to & mask).for_each(|to| {
let kind = MoveType::PawnAttackPromotion;
let from = unsafe { to.trans_unchecked(from) };
f(RawMove {
kind,
role: Role::Queen,
from,
to,
});
f(RawMove {
kind,
role: Role::Rook,
from,
to,
});
f(RawMove {
kind,
role: Role::Bishop,
from,
to,
});
f(RawMove {
kind,
role: Role::Knight,
from,
to,
});
});
}
}
}
}
pub(crate) trait MoveGen<S> {
#[inline]
fn roles(&self, _role: Role) -> bool {
@ -589,10 +768,8 @@ pub(crate) trait MoveGen<S> {
fn en_passant_is_legal(&mut self) -> ControlFlow<S> {
ControlFlow::Continue(())
}
fn extend<I>(&mut self, _iter: I) -> ControlFlow<S>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
#[inline]
fn extend(&mut self, _s: MoveSet) -> ControlFlow<S> {
ControlFlow::Continue(())
}
}
@ -670,14 +847,10 @@ impl Position {
.reduce_or()
};
// king moves
moves.extend(
(global_mask_to & lookup::king(king_square) & !us & !attacked).map(|to| RawMove {
kind: MoveType::KingMove,
moves.extend(MoveSet::KingMove {
from: king_square,
to,
role: Role::King,
}),
)?;
to: global_mask_to & lookup::king(king_square) & !us & !attacked,
})?;
// castling
if castling_rights.get(turn, CastlingSide::Short) {
let (x, y) = match turn {
@ -691,12 +864,7 @@ impl Position {
.trans_unchecked(Direction::East)
};
if global_mask_to.contains(to) {
moves.extend(core::iter::once(RawMove {
kind: MoveType::CastleShort,
from,
to,
role: Role::King,
}))?;
moves.extend(MoveSet::CastleShort { from, to })?;
}
}
}
@ -712,12 +880,7 @@ impl Position {
.trans_unchecked(Direction::West)
};
if global_mask_to.contains(to) {
moves.extend(core::iter::once(RawMove {
kind: MoveType::CastleLong,
from,
to,
role: Role::King,
}))?;
moves.extend(MoveSet::CastleLong { from, to })?;
}
}
}
@ -757,80 +920,34 @@ impl Position {
let adv = (global_mask_from & ours.pawn() & (!pinned | king_square.file().bitboard()))
.trans(forward)
& !blockers;
let promotion = turn.promotion_rank().bitboard();
// pawn advances
{
let targets = adv & target_mask;
moves.extend((targets & !promotion).map(|to| RawMove {
kind: MoveType::PawnAdvance,
from: unsafe { to.trans_unchecked(!forward) },
to,
role: Role::Pawn,
}))?;
moves.extend(MoveAndPromote::new((targets & promotion).map(|to| {
RawMove {
kind: MoveType::PawnAdvancePromotion,
from: unsafe { to.trans_unchecked(!forward) },
to,
role: Role::Pawn,
}
})))?;
moves.extend(MoveSet::PawnAdvance {
from: !forward,
to: adv & target_mask,
})?;
}
// pawn attacks kingside
{
let targets =
(global_mask_from & ours.pawn() & (!pinned | lookup::ray(king_square, kside)))
moves.extend(MoveSet::PawnAttack {
from: !kside,
to: (global_mask_from & ours.pawn() & (!pinned | lookup::ray(king_square, kside)))
.trans(kside)
& them
& target_mask;
moves.extend((targets & !promotion).map(|to| RawMove {
kind: MoveType::PawnAttack,
from: unsafe { to.trans_unchecked(!kside) },
to,
role: Role::Pawn,
}))?;
moves.extend(MoveAndPromote::new((targets & promotion).map(|to| {
RawMove {
kind: MoveType::PawnAttackPromotion,
from: unsafe { to.trans_unchecked(!kside) },
to,
role: Role::Pawn,
}
})))?;
}
& target_mask,
})?;
// pawn attacks queenside
{
let targets =
(global_mask_from & ours.pawn() & (!pinned | lookup::ray(king_square, qside)))
moves.extend(MoveSet::PawnAttack {
from: !qside,
to: (global_mask_from & ours.pawn() & (!pinned | lookup::ray(king_square, qside)))
.trans(qside)
& them
& target_mask;
moves.extend((targets & !promotion).map(|to| RawMove {
kind: MoveType::PawnAttack,
from: unsafe { to.trans_unchecked(!qside) },
to,
role: Role::Pawn,
}))?;
moves.extend(MoveAndPromote::new((targets & promotion).map(|to| {
RawMove {
kind: MoveType::PawnAttackPromotion,
from: unsafe { to.trans_unchecked(!qside) },
to,
role: Role::Pawn,
}
})))?;
}
& target_mask,
})?;
// pawn double advances
moves.extend(
((adv & third_rank.bitboard()).trans(forward) & !blockers & target_mask).map(
|to| RawMove {
kind: MoveType::PawnDoubleAdvance,
from: unsafe { to.trans_unchecked(!forward).trans_unchecked(!forward) },
to,
role: Role::Pawn,
},
),
)?;
moves.extend(MoveSet::PawnDoubleAdvance {
from: !forward,
to: (adv & third_rank.bitboard()).trans(forward) & !blockers & target_mask,
})?;
// en passant
if let Some(to) = en_passant {
if global_mask_to.contains(to) {
@ -855,12 +972,7 @@ impl Position {
& (!pinned | lookup::segment(king_square, to))
{
moves.en_passant_is_legal()?;
moves.extend(core::iter::once(RawMove {
kind: MoveType::EnPassant,
from,
to,
role: Role::Pawn,
}))?;
moves.extend(MoveSet::EnPassant { from, to })?;
}
}
}
@ -871,16 +983,11 @@ impl Position {
{
let aux = |moves: &mut T, role| {
for from in global_mask_from & *ours.get(role) & !pinned {
moves.extend(
(lookup::targets(role, from, blockers) & !us & target_mask).map(|to| {
RawMove {
kind: MoveType::PieceMove,
from,
to,
moves.extend(MoveSet::PieceMove {
role,
}
}),
)?;
from,
to: lookup::targets(role, from, blockers) & !us & target_mask,
})?;
}
ControlFlow::Continue(())
};
@ -907,16 +1014,11 @@ impl Position {
{
let aux = |moves: &mut T, role, role_mask| {
for from in global_mask_from & pinned & role_mask & *ours.get(role) {
moves.extend(
(global_mask_to & !us & pinned & lookup::line(king_square, from)).map(
|to| RawMove {
kind: MoveType::PieceMove,
from,
to,
moves.extend(MoveSet::PieceMove {
role,
},
),
)?;
from,
to: global_mask_to & !us & pinned & lookup::line(king_square, from),
})?;
}
ControlFlow::Continue(())
};
@ -1136,68 +1238,8 @@ impl MoveGen<Infallible> for MateMoveGenImpl {
ControlFlow::Continue(())
}
#[inline]
fn extend<I>(&mut self, iter: I) -> ControlFlow<Infallible>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
self.is_mate &= iter.len() == 0;
fn extend(&mut self, s: MoveSet) -> ControlFlow<Infallible> {
self.is_mate &= s.is_empty();
ControlFlow::Continue(())
}
}
struct MoveAndPromote<I: Iterator<Item = RawMove> + ExactSizeIterator + FusedIterator> {
role: u8,
inner: I,
cur: core::mem::MaybeUninit<RawMove>,
}
impl<I> MoveAndPromote<I>
where
I: Iterator<Item = RawMove> + ExactSizeIterator + FusedIterator,
{
#[inline]
fn new(inner: I) -> Self {
Self {
role: 1,
inner,
cur: core::mem::MaybeUninit::uninit(),
}
}
}
impl<I> Iterator for MoveAndPromote<I>
where
I: Iterator<Item = RawMove> + ExactSizeIterator + FusedIterator,
{
type Item = RawMove;
#[inline]
fn next(&mut self) -> Option<RawMove> {
if self.role == 1 {
self.cur.write(self.inner.next()?);
self.role = 5;
}
let raw = unsafe { self.cur.assume_init() };
let res = RawMove {
role: unsafe { Role::transmute(self.role) },
..raw
};
self.role = unsafe { self.role.unchecked_sub(1) };
Some(res)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<I> FusedIterator for MoveAndPromote<I> where
I: Iterator<Item = RawMove> + ExactSizeIterator + FusedIterator
{
}
impl<I> ExactSizeIterator for MoveAndPromote<I>
where
I: Iterator<Item = RawMove> + ExactSizeIterator + FusedIterator,
{
#[inline]
fn len(&self) -> usize {
self.inner.len() * 4 + self.role as usize - 1
}
}