diff --git a/src/moves.rs b/src/moves.rs index aac0161..335b734 100644 --- a/src/moves.rs +++ b/src/moves.rs @@ -120,11 +120,8 @@ impl<'l> Move<'l> { self.to } #[inline] - fn extend(&mut self, iter: I) -> ControlFlow - where - I: Iterator + ExactSizeIterator, - { - iter.for_each(|raw| { + fn extend(&mut self, s: MoveSet) -> ControlFlow { + 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 for Moves<'l> { ControlFlow::Continue(()) } #[inline] - fn extend(&mut self, iter: I) -> ControlFlow - where - I: Iterator + ExactSizeIterator, - { - iter.for_each(|raw| unsafe { self.array.push_unchecked(raw) }); + fn extend(&mut self, s: MoveSet) -> ControlFlow { + s.for_each(|raw| unsafe { self.array.push_unchecked(raw) }); ControlFlow::Continue(()) } } diff --git a/src/position.rs b/src/position.rs index 4e8bd5c..f6a3f64 100644 --- a/src/position.rs +++ b/src/position.rs @@ -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 for MoveGenImpl { - fn extend(&mut self, iter: I) -> ControlFlow - where - I: Iterator + ExactSizeIterator, - { - self.len += iter.len(); + fn extend(&mut self, s: MoveSet) -> ControlFlow { + self.len += s.len(); ControlFlow::Continue(()) } } @@ -322,19 +318,17 @@ impl Position { self.to } #[inline] - fn extend(&mut self, iter: I) -> ControlFlow - where - I: Iterator + ExactSizeIterator, - { - for raw in iter { + fn extend(&mut self, s: MoveSet) -> ControlFlow { + 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(&mut self, iter: I) -> ControlFlow - where - I: Iterator + ExactSizeIterator, - { - iter.for_each(|raw| { + fn extend(&mut self, s: MoveSet) -> ControlFlow { + 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(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 { #[inline] fn roles(&self, _role: Role) -> bool { @@ -589,10 +768,8 @@ pub(crate) trait MoveGen { fn en_passant_is_legal(&mut self) -> ControlFlow { ControlFlow::Continue(()) } - fn extend(&mut self, _iter: I) -> ControlFlow - where - I: Iterator + ExactSizeIterator, - { + #[inline] + fn extend(&mut self, _s: MoveSet) -> ControlFlow { 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, - from: king_square, - to, - role: Role::King, - }), - )?; + moves.extend(MoveSet::KingMove { + from: king_square, + 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))) - .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, - } - })))?; - } + moves.extend(MoveSet::PawnAttack { + from: !kside, + to: (global_mask_from & ours.pawn() & (!pinned | lookup::ray(king_square, kside))) + .trans(kside) + & them + & target_mask, + })?; // pawn attacks queenside - { - let targets = - (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, - } - })))?; - } + moves.extend(MoveSet::PawnAttack { + from: !qside, + to: (global_mask_from & ours.pawn() & (!pinned | lookup::ray(king_square, qside))) + .trans(qside) + & them + & 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, - role, - } - }), - )?; + 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, - role, - }, - ), - )?; + 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 for MateMoveGenImpl { ControlFlow::Continue(()) } #[inline] - fn extend(&mut self, iter: I) -> ControlFlow - where - I: Iterator + ExactSizeIterator, - { - self.is_mate &= iter.len() == 0; + fn extend(&mut self, s: MoveSet) -> ControlFlow { + self.is_mate &= s.is_empty(); ControlFlow::Continue(()) } } - -struct MoveAndPromote + ExactSizeIterator + FusedIterator> { - role: u8, - inner: I, - cur: core::mem::MaybeUninit, -} -impl MoveAndPromote -where - I: Iterator + ExactSizeIterator + FusedIterator, -{ - #[inline] - fn new(inner: I) -> Self { - Self { - role: 1, - inner, - cur: core::mem::MaybeUninit::uninit(), - } - } -} -impl Iterator for MoveAndPromote -where - I: Iterator + ExactSizeIterator + FusedIterator, -{ - type Item = RawMove; - #[inline] - fn next(&mut self) -> Option { - 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) { - let len = self.len(); - (len, Some(len)) - } -} -impl FusedIterator for MoveAndPromote where - I: Iterator + ExactSizeIterator + FusedIterator -{ -} -impl ExactSizeIterator for MoveAndPromote -where - I: Iterator + ExactSizeIterator + FusedIterator, -{ - #[inline] - fn len(&self) -> usize { - self.inner.len() * 4 + self.role as usize - 1 - } -}