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

View file

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