From 89e77fb977857f5108228088ba8f1ace6660fdbb Mon Sep 17 00:00:00 2001 From: Paul-Nicolas Madelaine Date: Thu, 13 Nov 2025 23:10:22 +0100 Subject: [PATCH] control flow --- src/moves.rs | 31 ++++---- src/position.rs | 194 +++++++++++++++++++++--------------------------- 2 files changed, 101 insertions(+), 124 deletions(-) diff --git a/src/moves.rs b/src/moves.rs index f1b2826..eff1936 100644 --- a/src/moves.rs +++ b/src/moves.rs @@ -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 MoveGen for MoveGenImpl { + impl MoveGen for MoveGenImpl { #[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(&mut self, iter: I) + fn extend(&mut self, iter: I) -> ControlFlow where I: Iterator + 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(m: &Move) -> SanInner { let mut moves = MoveGenImpl::::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, } -impl<'l> MoveGen for Moves<'l> { +impl<'l> MoveGen for Moves<'l> { #[inline] - fn is_check(&mut self) { + fn is_check(&mut self) -> ControlFlow { self.is_check = true; + ControlFlow::Continue(()) } #[inline] - fn en_passant_is_legal(&mut self) { + fn en_passant_is_legal(&mut self) -> ControlFlow { self.en_passant_is_legal = true; + ControlFlow::Continue(()) } #[inline] - fn extend(&mut self, iter: I) + fn extend(&mut self, iter: I) -> ControlFlow where I: Iterator + 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, diff --git a/src/position.rs b/src/position.rs index 22472b4..837c136 100644 --- a/src/position.rs +++ b/src/position.rs @@ -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(&mut self, iter: I) + impl MoveGen for MoveGenImpl { + fn extend(&mut self, iter: I) -> ControlFlow where I: Iterator + 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(&mut self, _iter: I) - where - I: Iterator + 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(&mut self, _iter: I) - where - I: Iterator + 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, } impl MoveGenImpl { #[inline] fn new(role: Role, from: Bitboard, to: Bitboard) -> Self { - Self { - role, - from, - to, - found: None, - } + Self { role, from, to } } } - impl MoveGen for MoveGenImpl { + impl MoveGen for MoveGenImpl { #[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(&mut self, iter: I) + fn extend(&mut self, iter: I) -> ControlFlow where I: Iterator + 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, InvalidUciMove> { let mut moves = MoveGenImpl::::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 MoveGen for MoveGenImpl { + impl MoveGen for MoveGenImpl { #[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(&mut self, iter: I) + fn extend(&mut self, iter: I) -> ControlFlow where I: Iterator + 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, InvalidSan> { let mut moves = MoveGenImpl::::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 { #[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(&mut self, iter: I) + #[inline] + fn is_check(&mut self) -> ControlFlow { + ControlFlow::Continue(()) + } + #[inline] + fn en_passant_is_legal(&mut self) -> ControlFlow { + ControlFlow::Continue(()) + } + fn extend(&mut self, _iter: I) -> ControlFlow where - I: Iterator + ExactSizeIterator; + I: Iterator + ExactSizeIterator, + { + ControlFlow::Continue(()) + } } impl Position { @@ -596,9 +568,9 @@ impl Position { Self(setup) } - pub(crate) fn generate_moves(&self, moves: &mut T) + pub(crate) fn generate_moves(&self, moves: &mut T) -> ControlFlow where - T: MoveGen, + T: MoveGen, { 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 for MateMoveGenImpl { #[inline] - fn is_check(&mut self) { + fn is_check(&mut self) -> ControlFlow { self.is_check = true; + ControlFlow::Continue(()) } #[inline] - fn en_passant_is_legal(&mut self) {} - #[inline] - fn extend(&mut self, iter: I) + fn extend(&mut self, iter: I) -> ControlFlow where I: Iterator + ExactSizeIterator, { self.is_mate &= iter.len() == 0; + ControlFlow::Continue(()) } }