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

View file

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