1
0
Fork 0

control flow

This commit is contained in:
Paul-Nicolas Madelaine 2025-11-13 23:10:22 +01:00
parent 34df546eaa
commit 89e77fb977
2 changed files with 101 additions and 124 deletions

View file

@ -7,8 +7,9 @@ use crate::position::*;
use crate::san::*;
use crate::uci::*;
use std::iter::ExactSizeIterator;
use std::iter::FusedIterator;
use std::convert::Infallible;
use std::iter::{ExactSizeIterator, FusedIterator};
use std::ops::ControlFlow;
/// A legal move.
#[must_use]
@ -101,7 +102,7 @@ impl<'l> Move<'l> {
}
}
}
impl<const ROLE: u8> MoveGen for MoveGenImpl<ROLE> {
impl<const ROLE: u8> MoveGen<Infallible> for MoveGenImpl<ROLE> {
#[inline]
fn roles(&self, role: Role) -> bool {
role as u8 == ROLE
@ -111,11 +112,7 @@ impl<'l> Move<'l> {
self.to
}
#[inline]
fn is_check(&mut self) {}
#[inline]
fn en_passant_is_legal(&mut self) {}
#[inline]
fn extend<I>(&mut self, iter: I)
fn extend<I>(&mut self, iter: I) -> ControlFlow<Infallible>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
@ -124,6 +121,7 @@ impl<'l> Move<'l> {
debug_assert!(self.to.contains(raw.to()));
self.candidates.insert(raw.from);
});
ControlFlow::Continue(())
}
}
San {
@ -153,7 +151,7 @@ impl<'l> Move<'l> {
_ => {
fn aux<const ROLE: u8>(m: &Move) -> SanInner {
let mut moves = MoveGenImpl::<ROLE>::new(m.to());
m.position().generate_moves(&mut moves);
let ControlFlow::Continue(()) = m.position().generate_moves(&mut moves);
let candidates = moves.candidates;
let (file, rank) = if candidates == m.from().bitboard() {
(None, None)
@ -186,7 +184,7 @@ impl<'l> Move<'l> {
suffix: {
let pos = self.make();
let mut moves = MateMoveGenImpl::new();
pos.generate_moves(&mut moves);
let ControlFlow::Continue(()) = pos.generate_moves(&mut moves);
let MateMoveGenImpl { is_check, is_mate } = moves;
match (is_check, is_mate) {
(false, _) => None,
@ -210,21 +208,24 @@ pub struct Moves<'l> {
array: ArrayVec<RawMove, MAX_LEGAL_MOVES>,
}
impl<'l> MoveGen for Moves<'l> {
impl<'l> MoveGen<Infallible> for Moves<'l> {
#[inline]
fn is_check(&mut self) {
fn is_check(&mut self) -> ControlFlow<Infallible> {
self.is_check = true;
ControlFlow::Continue(())
}
#[inline]
fn en_passant_is_legal(&mut self) {
fn en_passant_is_legal(&mut self) -> ControlFlow<Infallible> {
self.en_passant_is_legal = true;
ControlFlow::Continue(())
}
#[inline]
fn extend<I>(&mut self, iter: I)
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) });
ControlFlow::Continue(())
}
}
@ -232,7 +233,7 @@ impl<'l> Moves<'l> {
#[inline]
pub(crate) fn compute(position: &'l Position) -> Moves<'l> {
fn aux(position: &Position, moves: &mut Moves) {
position.generate_moves(moves);
let ControlFlow::Continue(()) = position.generate_moves(moves);
}
let mut moves = Moves {
position,

View file

@ -8,8 +8,9 @@ use crate::san::*;
use crate::setup::*;
use crate::uci::*;
use std::iter::ExactSizeIterator;
use std::iter::FusedIterator;
use std::convert::Infallible;
use std::iter::{ExactSizeIterator, FusedIterator};
use std::ops::ControlFlow;
/// **A chess position.**
///
@ -115,31 +116,25 @@ impl Position {
Self { len: 0 }
}
}
impl MoveGen for MoveGenImpl {
#[inline]
fn is_check(&mut self) {}
#[inline]
fn en_passant_is_legal(&mut self) {}
#[inline]
fn extend<I>(&mut self, iter: I)
impl MoveGen<Infallible> for MoveGenImpl {
fn extend<I>(&mut self, iter: I) -> ControlFlow<Infallible>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
self.len += iter.len();
ControlFlow::Continue(())
}
}
let mut moves = MoveGenImpl::new();
self.generate_moves(&mut moves);
let ControlFlow::Continue(()) = self.generate_moves(&mut moves);
moves.len
}
/// Returns `true` if the king is in check.
#[must_use]
pub fn is_check(&self) -> bool {
struct MoveGenImpl {
is_check: bool,
}
impl MoveGen for MoveGenImpl {
struct MoveGenImpl;
impl MoveGen<()> for MoveGenImpl {
#[inline]
fn roles(&self, _role: Role) -> bool {
false
@ -153,21 +148,11 @@ impl Position {
Bitboard::new()
}
#[inline]
fn is_check(&mut self) {
self.is_check = true;
}
#[inline]
fn en_passant_is_legal(&mut self) {}
#[inline]
fn extend<I>(&mut self, _iter: I)
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
fn is_check(&mut self) -> ControlFlow<()> {
ControlFlow::Break(())
}
}
let mut moves = MoveGenImpl { is_check: false };
self.generate_moves(&mut moves);
moves.is_check
self.generate_moves(&mut MoveGenImpl).is_break()
}
/// Returns `true` if there is no legal move on the position.
@ -184,9 +169,8 @@ impl Position {
}
struct MoveGenImpl {
to: Bitboard,
en_passant_is_legal: bool,
}
impl MoveGen for MoveGenImpl {
impl MoveGen<()> for MoveGenImpl {
#[inline]
fn roles(&self, role: Role) -> bool {
match role {
@ -203,24 +187,14 @@ impl Position {
self.to
}
#[inline]
fn is_check(&mut self) {}
#[inline]
fn en_passant_is_legal(&mut self) {
self.en_passant_is_legal = true;
}
#[inline]
fn extend<I>(&mut self, _iter: I)
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
fn en_passant_is_legal(&mut self) -> ControlFlow<()> {
ControlFlow::Break(())
}
}
let mut moves = MoveGenImpl {
to: self.as_setup().en_passant.bitboard(),
en_passant_is_legal: false,
};
self.generate_moves(&mut moves);
moves.en_passant_is_legal
self.generate_moves(&mut moves).is_break()
}
/// Discards the en passant target square.
@ -315,20 +289,14 @@ impl Position {
role: Role,
from: Bitboard,
to: Bitboard,
found: Option<RawMove>,
}
impl<const ROLE: u8> MoveGenImpl<ROLE> {
#[inline]
fn new(role: Role, from: Bitboard, to: Bitboard) -> Self {
Self {
role,
from,
to,
found: None,
Self { role, from, to }
}
}
}
impl<const ROLE: u8> MoveGen for MoveGenImpl<ROLE> {
impl<const ROLE: u8> MoveGen<RawMove> for MoveGenImpl<ROLE> {
#[inline]
fn roles(&self, role: Role) -> bool {
role as u8 == ROLE
@ -342,23 +310,19 @@ impl Position {
self.to
}
#[inline]
fn is_check(&mut self) {}
#[inline]
fn en_passant_is_legal(&mut self) {}
#[inline]
fn extend<I>(&mut self, iter: I)
fn extend<I>(&mut self, iter: I) -> ControlFlow<RawMove>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
iter.for_each(|raw| {
for raw in iter {
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 {
debug_assert!(self.found.is_none());
self.found = Some(raw);
return ControlFlow::Break(raw);
}
});
}
ControlFlow::Continue(())
}
}
let UciMove {
@ -375,8 +339,10 @@ impl Position {
to: Square,
) -> Result<Move<'l>, InvalidUciMove> {
let mut moves = MoveGenImpl::<ROLE>::new(role, from.bitboard(), to.bitboard());
position.generate_moves(&mut moves);
let raw = moves.found.ok_or(InvalidUciMove::Illegal)?;
let raw = position
.generate_moves(&mut moves)
.break_value()
.ok_or(InvalidUciMove::Illegal)?;
Ok(unsafe { Move::new_unchecked(position, raw) })
}
let promotion = if role == Role::Pawn {
@ -409,7 +375,7 @@ impl Position {
}
}
}
impl<const ROLE: u8> MoveGen for MoveGenImpl<ROLE> {
impl<const ROLE: u8> MoveGen<Infallible> for MoveGenImpl<ROLE> {
#[inline]
fn roles(&self, role: Role) -> bool {
role as u8 == ROLE
@ -423,11 +389,7 @@ impl Position {
self.to
}
#[inline]
fn is_check(&mut self) {}
#[inline]
fn en_passant_is_legal(&mut self) {}
#[inline]
fn extend<I>(&mut self, iter: I)
fn extend<I>(&mut self, iter: I) -> ControlFlow<Infallible>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
@ -442,6 +404,7 @@ impl Position {
}
}
});
ControlFlow::Continue(())
}
}
let (role, promotion, from, to) = match san.inner {
@ -486,7 +449,7 @@ impl Position {
to: Bitboard,
) -> Result<Move<'l>, InvalidSan> {
let mut moves = MoveGenImpl::<ROLE>::new(role, from, to);
position.generate_moves(&mut moves);
let ControlFlow::Continue(()) = position.generate_moves(&mut moves);
match moves.found {
None => Err(InvalidSan::Illegal),
Some(raw) => match moves.found_other {
@ -569,7 +532,7 @@ impl RawMove {
}
}
pub(crate) trait MoveGen {
pub(crate) trait MoveGen<S> {
#[inline]
fn roles(&self, _role: Role) -> bool {
true
@ -583,11 +546,20 @@ pub(crate) trait MoveGen {
!Bitboard::new()
}
fn is_check(&mut self);
fn en_passant_is_legal(&mut self);
fn extend<I>(&mut self, iter: I)
#[inline]
fn is_check(&mut self) -> ControlFlow<S> {
ControlFlow::Continue(())
}
#[inline]
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;
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
ControlFlow::Continue(())
}
}
impl Position {
@ -596,9 +568,9 @@ impl Position {
Self(setup)
}
pub(crate) fn generate_moves<T>(&self, moves: &mut T)
pub(crate) fn generate_moves<S, T>(&self, moves: &mut T) -> ControlFlow<S>
where
T: MoveGen,
T: MoveGen<S>,
{
let global_mask_from = moves.from();
let global_mask_to = moves.to();
@ -673,7 +645,7 @@ impl Position {
to,
role: Role::King,
}),
);
)?;
// castling
if castling_rights.get(turn, CastlingSide::Short) {
let (x, y) = match turn {
@ -692,7 +664,7 @@ impl Position {
from,
to,
role: Role::King,
}))
}))?;
}
}
}
@ -713,15 +685,15 @@ impl Position {
from,
to,
role: Role::King,
}))
}))?;
}
}
}
}
if checkers.len() > 1 {
moves.is_check();
return;
moves.is_check()?;
return ControlFlow::Continue(());
}
let blockers_x_ray = blockers & !(x | y);
@ -762,7 +734,7 @@ impl Position {
from: unsafe { to.trans_unchecked(!forward) },
to,
role: Role::Pawn,
}));
}))?;
moves.extend(MoveAndPromote::new((targets & promotion).map(|to| {
RawMove {
kind: MoveType::PawnAdvancePromotion,
@ -770,7 +742,7 @@ impl Position {
to,
role: Role::Pawn,
}
})));
})))?;
}
// pawn attacks kingside
{
@ -784,7 +756,7 @@ impl Position {
from: unsafe { to.trans_unchecked(!kside) },
to,
role: Role::Pawn,
}));
}))?;
moves.extend(MoveAndPromote::new((targets & promotion).map(|to| {
RawMove {
kind: MoveType::PawnAttackPromotion,
@ -792,7 +764,7 @@ impl Position {
to,
role: Role::Pawn,
}
})));
})))?;
}
// pawn attacks queenside
{
@ -806,7 +778,7 @@ impl Position {
from: unsafe { to.trans_unchecked(!qside) },
to,
role: Role::Pawn,
}));
}))?;
moves.extend(MoveAndPromote::new((targets & promotion).map(|to| {
RawMove {
kind: MoveType::PawnAttackPromotion,
@ -814,7 +786,7 @@ impl Position {
to,
role: Role::Pawn,
}
})));
})))?;
}
// pawn double advances
moves.extend(
@ -826,7 +798,7 @@ impl Position {
role: Role::Pawn,
},
),
);
)?;
// en passant
if let Some(to) = en_passant.try_into_square() {
if global_mask_to.contains(to) {
@ -846,18 +818,18 @@ impl Position {
) & theirs.rook())
.map(|sq| lookup::segment(king_square, sq))
.reduce_or();
(global_mask_from
for from in global_mask_from
& candidates
& (!pinned | lookup::segment(king_square, to)))
.for_each(|from| {
moves.en_passant_is_legal();
& (!pinned | lookup::segment(king_square, to))
{
moves.en_passant_is_legal()?;
moves.extend(std::iter::once(RawMove {
kind: MoveType::EnPassant,
from,
to,
role: Role::Pawn,
}))
})
}))?;
}
}
}
}
@ -876,26 +848,27 @@ impl Position {
role,
}
}),
)
)?;
}
ControlFlow::Continue(())
};
if moves.roles(Role::Knight) {
aux(moves, Role::Knight)
aux(moves, Role::Knight)?;
}
if moves.roles(Role::Bishop) {
aux(moves, Role::Bishop)
aux(moves, Role::Bishop)?;
}
if moves.roles(Role::Rook) {
aux(moves, Role::Rook)
aux(moves, Role::Rook)?;
}
if moves.roles(Role::Queen) {
aux(moves, Role::Queen)
aux(moves, Role::Queen)?;
}
}
if checker.is_some() {
moves.is_check();
return;
moves.is_check()?;
return ControlFlow::Continue(());
}
// pinned pieces
@ -911,19 +884,22 @@ impl Position {
role,
},
),
)
)?;
}
ControlFlow::Continue(())
};
if moves.roles(Role::Bishop) {
aux(moves, Role::Bishop, lookup::bishop_lines(king_square));
aux(moves, Role::Bishop, lookup::bishop_lines(king_square))?;
}
if moves.roles(Role::Rook) {
aux(moves, Role::Rook, lookup::rook_lines(king_square));
aux(moves, Role::Rook, lookup::rook_lines(king_square))?;
}
if moves.roles(Role::Queen) {
aux(moves, Role::Queen, !Bitboard::new());
aux(moves, Role::Queen, !Bitboard::new())?;
}
}
ControlFlow::Continue(())
}
#[inline]
@ -1094,19 +1070,19 @@ impl MateMoveGenImpl {
}
}
}
impl MoveGen for MateMoveGenImpl {
impl MoveGen<Infallible> for MateMoveGenImpl {
#[inline]
fn is_check(&mut self) {
fn is_check(&mut self) -> ControlFlow<Infallible> {
self.is_check = true;
ControlFlow::Continue(())
}
#[inline]
fn en_passant_is_legal(&mut self) {}
#[inline]
fn extend<I>(&mut self, iter: I)
fn extend<I>(&mut self, iter: I) -> ControlFlow<Infallible>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
self.is_mate &= iter.len() == 0;
ControlFlow::Continue(())
}
}