shakmaty/
position.rs

1use core::{
2    error, fmt,
3    hash::{Hash, Hasher},
4    num::NonZeroU32,
5    str::FromStr,
6};
7
8use bitflags::bitflags;
9
10use crate::{
11    Bitboard, Board, ByColor, ByRole, Castles, CastlingMode, CastlingSide, Color,
12    Color::{Black, White},
13    EnPassantMode, Move, MoveList, Piece, Rank, RemainingChecks, Role, Setup, Square, attacks,
14    bitboard::Direction,
15    setup::EnPassant,
16    zobrist::ZobristValue,
17};
18
19/// A definitive outcome of a game.
20///
21/// See also [`Outcome`].
22#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
23#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
24pub enum KnownOutcome {
25    Decisive { winner: Color },
26    Draw,
27}
28
29impl KnownOutcome {
30    pub const fn from_winner(winner: Option<Color>) -> KnownOutcome {
31        match winner {
32            Some(winner) => KnownOutcome::Decisive { winner },
33            None => KnownOutcome::Draw,
34        }
35    }
36
37    pub const fn winner(self) -> Option<Color> {
38        match self {
39            KnownOutcome::Decisive { winner } => Some(winner),
40            KnownOutcome::Draw => None,
41        }
42    }
43
44    pub const fn from_ascii(bytes: &[u8]) -> Result<KnownOutcome, ParseOutcomeError> {
45        Ok(match bytes {
46            b"1-0" => KnownOutcome::Decisive { winner: White },
47            b"0-1" => KnownOutcome::Decisive { winner: Black },
48            b"1/2-1/2" => KnownOutcome::Draw,
49            _ => return Err(ParseOutcomeError),
50        })
51    }
52
53    pub const fn as_str(self) -> &'static str {
54        match self {
55            KnownOutcome::Decisive { winner: White } => "1-0",
56            KnownOutcome::Decisive { winner: Black } => "0-1",
57            KnownOutcome::Draw => "1/2-1/2",
58        }
59    }
60}
61
62impl fmt::Display for KnownOutcome {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        f.write_str(self.as_str())
65    }
66}
67
68impl FromStr for KnownOutcome {
69    type Err = ParseOutcomeError;
70
71    fn from_str(s: &str) -> Result<KnownOutcome, ParseOutcomeError> {
72        KnownOutcome::from_ascii(s.as_bytes())
73    }
74}
75
76#[cfg(feature = "bincode")]
77impl bincode::Encode for KnownOutcome {
78    fn encode<E: bincode::enc::Encoder>(
79        &self,
80        encoder: &mut E,
81    ) -> Result<(), bincode::error::EncodeError> {
82        Outcome::Known(*self).encode(encoder)
83    }
84}
85
86#[cfg(feature = "bincode")]
87impl<Config> bincode::Decode<Config> for KnownOutcome {
88    fn decode<D: bincode::de::Decoder>(
89        decoder: &mut D,
90    ) -> Result<Self, bincode::error::DecodeError> {
91        Outcome::decode(decoder).and_then(|outcome| {
92            outcome
93                .known()
94                .ok_or(bincode::error::DecodeError::Other("invalid KnownOutcome"))
95        })
96    }
97}
98
99#[cfg(feature = "bincode")]
100bincode::impl_borrow_decode!(KnownOutcome);
101
102/// Outcome of a game, if any.
103#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
104#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
105pub enum Outcome {
106    /// `1-0`, `0-1`, or `1/2-1/2`.
107    Known(KnownOutcome),
108    /// `*` - Game in progress, game abandoned, or result otherwise unknown.
109    Unknown,
110}
111
112impl Outcome {
113    pub const fn from_known(outcome: Option<KnownOutcome>) -> Outcome {
114        match outcome {
115            Some(outcome) => Self::Known(outcome),
116            None => Self::Unknown,
117        }
118    }
119
120    pub const fn known(self) -> Option<KnownOutcome> {
121        match self {
122            Self::Known(outcome) => Some(outcome),
123            Self::Unknown => None,
124        }
125    }
126
127    pub const fn is_known(self) -> bool {
128        matches!(self, Self::Known(_))
129    }
130
131    pub const fn is_unknown(self) -> bool {
132        matches!(self, Self::Unknown)
133    }
134
135    pub const fn winner(self) -> Option<Color> {
136        match self {
137            Self::Known(outcome) => outcome.winner(),
138            Self::Unknown => None,
139        }
140    }
141
142    pub fn from_ascii(bytes: &[u8]) -> Result<Outcome, ParseOutcomeError> {
143        if bytes == b"*" {
144            Ok(Self::Unknown)
145        } else {
146            KnownOutcome::from_ascii(bytes).map(Self::Known)
147        }
148    }
149
150    pub const fn as_str(self) -> &'static str {
151        match self {
152            Self::Known(outcome) => outcome.as_str(),
153            Self::Unknown => "*",
154        }
155    }
156}
157
158impl From<KnownOutcome> for Outcome {
159    fn from(outcome: KnownOutcome) -> Self {
160        Self::Known(outcome)
161    }
162}
163
164impl FromStr for Outcome {
165    type Err = ParseOutcomeError;
166
167    fn from_str(s: &str) -> Result<Self, Self::Err> {
168        Self::from_ascii(s.as_bytes())
169    }
170}
171
172impl fmt::Display for Outcome {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        f.write_str(self.as_str())
175    }
176}
177
178/// Error when parsing an [`Outcome`] or [`KnownOutcome`].
179#[derive(Clone, Debug)]
180pub struct ParseOutcomeError;
181
182impl fmt::Display for ParseOutcomeError {
183    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184        f.write_str("invalid outcome")
185    }
186}
187
188impl error::Error for ParseOutcomeError {}
189
190#[cfg(feature = "bincode")]
191impl bincode::Encode for Outcome {
192    fn encode<E: bincode::enc::Encoder>(
193        &self,
194        encoder: &mut E,
195    ) -> Result<(), bincode::error::EncodeError> {
196        u8::encode(
197            &match self {
198                Outcome::Unknown => 0,
199                Outcome::Known(KnownOutcome::Decisive {
200                    winner: Color::White,
201                }) => 1,
202                Outcome::Known(KnownOutcome::Decisive {
203                    winner: Color::Black,
204                }) => 2,
205                Outcome::Known(KnownOutcome::Draw) => 3,
206            },
207            encoder,
208        )
209    }
210}
211
212#[cfg(feature = "bincode")]
213impl<Config> bincode::Decode<Config> for Outcome {
214    fn decode<D: bincode::de::Decoder>(
215        decoder: &mut D,
216    ) -> Result<Self, bincode::error::DecodeError> {
217        Ok(match u8::decode(decoder)? {
218            0 => Outcome::Unknown,
219            1 => Outcome::Known(KnownOutcome::Decisive {
220                winner: Color::White,
221            }),
222            2 => Outcome::Known(KnownOutcome::Decisive {
223                winner: Color::Black,
224            }),
225            3 => Outcome::Known(KnownOutcome::Draw),
226            _ => return Err(bincode::error::DecodeError::Other("invalid Outcome")),
227        })
228    }
229}
230
231#[cfg(feature = "bincode")]
232bincode::impl_borrow_decode!(Outcome);
233
234/// Error when trying to play an illegal move.
235#[derive(Debug)]
236pub struct PlayError<P> {
237    /// The move that was not played.
238    pub m: Move,
239    /// The unchanged position.
240    pub position: P,
241}
242
243impl<P: fmt::Debug> fmt::Display for PlayError<P> {
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        write!(f, "illegal move {:?} in {:?}", self.m, self.position)
246    }
247}
248
249impl<P: fmt::Debug> error::Error for PlayError<P> {}
250
251bitflags! {
252    /// Reasons for a [`Setup`] not being a legal [`Position`].
253    ///
254    /// A position is legal if it can be reached with a sequence of legal moves
255    /// from the starting position. All legal positions are accepted.
256    /// However, it is not feasible (or even always desirable) to reject all
257    /// illegal positions.
258    ///
259    /// Instead, the validity requirements here are chosen based on
260    /// practical considerations: Are shakmaty, as well as common
261    /// chess software (in particular Stockfish and Lichess) able to correctly
262    /// handle the position, and are they likely to continue to do so in future
263    /// versions?
264    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
265    pub struct PositionErrorKinds: u32 {
266        /// There are no pieces on the board.
267        const EMPTY_BOARD = 1 << 0;
268
269        /// A king is required but missing.
270        const MISSING_KING = 1 << 1;
271
272        /// A player has too many kings.
273        const TOO_MANY_KINGS = 1 << 2;
274
275        /// There are pawns on the backrank. Only [`Horde`](variant::Horde) allows players to
276        /// have pawns on their own backrank.
277        const PAWNS_ON_BACKRANK = 1 << 3;
278
279        /// Some castling rights are invalid for the selected [`CastlingMode`].
280        ///
281        /// Can be recovered by discarding the invalid castling rights using
282        /// [`PositionError::ignore_invalid_castling_rights()`].
283        const INVALID_CASTLING_RIGHTS = 1 << 4;
284
285        /// The en passant square is on the wrong rank, not empty, or the
286        /// allegedly pushed pawn is not present.
287        ///
288        /// Can be recovered by discarding the invalid en passant square using
289        /// [`PositionError::ignore_invalid_ep_square()`].
290        const INVALID_EP_SQUARE = 1 << 5;
291
292        /// The player not to move is in check.
293        const OPPOSITE_CHECK = 1 << 6;
294
295        /// There are impossibly many checkers, two stepping checkers,
296        /// two sliding checkers are aligned, or check is not possible because
297        /// the en passant square implies that the last move was a double
298        /// pawn push.
299        ///
300        /// Unlike [`PositionErrorKinds::OPPOSITE_CHECK`], this can be ignored
301        /// using [`PositionError::ignore_impossible_check()`], but note that
302        /// other software may not work correctly in such situations.
303        const IMPOSSIBLE_CHECK = 1 << 7;
304
305        /// The material configuration cannot be reached with any sequence of
306        /// legal moves, because there is *too much* material.
307        ///
308        /// Distinguishes light-squared and dark-squared bishops,
309        /// pockets and promoted pieces in Crazyhouse, and pieces on the board.
310        /// Does not consider their positions. Does not consider missing pieces
311        /// in Crazyhouse.
312        ///
313        /// This can be ignored using
314        /// [`PositionError::ignore_too_much_material()`], but note that
315        /// other software may not work correctly with too much material.
316        const TOO_MUCH_MATERIAL = 1 << 8;
317
318        /// A variant specific rule is violated.
319        const VARIANT = 1 << 9;
320    }
321}
322
323/// Error when trying to create a [`Position`] from an illegal [`Setup`].
324///
325/// See [`PositionErrorKinds`] for possible reasons.
326#[derive(Clone)]
327pub struct PositionError<P> {
328    pub(crate) pos: P,
329    pub(crate) errors: PositionErrorKinds,
330}
331
332impl<P> PositionError<P> {
333    fn ignore(mut self, ignore: PositionErrorKinds) -> Result<P, Self> {
334        self.errors -= ignore;
335        match self {
336            PositionError { pos, errors } if errors.is_empty() => Ok(pos),
337            _ => Err(self),
338        }
339    }
340
341    fn strict(self) -> Result<P, Self> {
342        self.ignore(PositionErrorKinds::empty())
343    }
344
345    /// Discards invalid castling rights to recover from
346    /// [`PositionErrorKinds::INVALID_CASTLING_RIGHTS`].
347    pub fn ignore_invalid_castling_rights(self) -> Result<P, Self> {
348        self.ignore(PositionErrorKinds::INVALID_CASTLING_RIGHTS)
349    }
350
351    /// Discards invalid en passant squares to recover from
352    /// [`PositionErrorKinds::INVALID_EP_SQUARE`].
353    pub fn ignore_invalid_ep_square(self) -> Result<P, Self> {
354        self.ignore(PositionErrorKinds::INVALID_EP_SQUARE)
355    }
356
357    /// Get the position despite [`PositionErrorKinds::TOO_MUCH_MATERIAL`].
358    ///
359    /// Note that other programs may not work with too much material.
360    pub fn ignore_too_much_material(self) -> Result<P, Self> {
361        self.ignore(PositionErrorKinds::TOO_MUCH_MATERIAL)
362    }
363
364    /// Get the position despite [`PositionErrorKinds::IMPOSSIBLE_CHECK`]
365    /// (not be be confused with [`PositionErrorKinds::OPPOSITE_CHECK`]).
366    ///
367    /// Note that other programs may not work in such a situation.
368    pub fn ignore_impossible_check(self) -> Result<P, Self> {
369        self.ignore(PositionErrorKinds::IMPOSSIBLE_CHECK)
370    }
371
372    /// Returns the reasons for this error.
373    pub fn kinds(&self) -> PositionErrorKinds {
374        self.errors
375    }
376}
377
378impl<P> fmt::Debug for PositionError<P> {
379    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380        f.debug_struct("PositionError")
381            .field("errors", &self.errors)
382            .finish_non_exhaustive()
383    }
384}
385
386impl<P> fmt::Display for PositionError<P> {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        f.write_str("illegal position: ")?;
389
390        let mut first = true;
391        let mut reason = |kind: PositionErrorKinds, display: &str| -> fmt::Result {
392            if self.errors.contains(kind) {
393                if !first {
394                    f.write_str(", ")?;
395                }
396                f.write_str(display)?;
397                first = false;
398            }
399            Ok(())
400        };
401
402        reason(PositionErrorKinds::EMPTY_BOARD, "empty board")?;
403        reason(PositionErrorKinds::MISSING_KING, "missing king")?;
404        reason(PositionErrorKinds::TOO_MANY_KINGS, "too many kings")?;
405        reason(PositionErrorKinds::PAWNS_ON_BACKRANK, "pawns on backrank")?;
406        reason(
407            PositionErrorKinds::INVALID_CASTLING_RIGHTS,
408            "invalid castling rights",
409        )?;
410        reason(PositionErrorKinds::INVALID_EP_SQUARE, "invalid ep square")?;
411        reason(PositionErrorKinds::OPPOSITE_CHECK, "opposite check")?;
412        reason(PositionErrorKinds::IMPOSSIBLE_CHECK, "impossible check")?;
413        reason(PositionErrorKinds::TOO_MUCH_MATERIAL, "too much material")?;
414        reason(PositionErrorKinds::VARIANT, "variant rule violated")?;
415        if first {
416            f.write_str("unknown reason")?;
417        }
418
419        Ok(())
420    }
421}
422
423impl<P> error::Error for PositionError<P> {}
424
425/// Validate and set up a playable [`Position`]. All provided chess variants
426/// support this.
427pub trait FromSetup: Sized {
428    /// Set up a playable [`Position`].
429    ///
430    /// # Errors
431    ///
432    /// Returns [`PositionError`] if the [`Setup`] does not
433    /// meet [basic validity requirements](PositionErrorKinds).
434    ///
435    /// Meeting the requirements does not imply that the position
436    /// is actually reachable with a series of legal moves from the starting
437    /// position.
438    ///
439    /// # Examples
440    ///
441    /// ```
442    /// use shakmaty::{CastlingMode, Chess, FromSetup, Setup, PositionError};
443    ///
444    /// let setup = Setup::default();
445    ///
446    /// let pos = Chess::from_setup(setup, CastlingMode::Standard)
447    ///     .or_else(PositionError::ignore_too_much_material)
448    ///     .or_else(PositionError::ignore_impossible_check)?;
449    ///
450    /// # Ok::<_, PositionError<_>>(())
451    /// ```
452    fn from_setup(setup: Setup, mode: CastlingMode) -> Result<Self, PositionError<Self>>;
453}
454
455/// A playable chess or chess variant position. See [`Chess`] for a concrete
456/// implementation.
457///
458/// # Equality
459///
460/// All provided variants implement [`Hash`],
461/// [`PartialEq`], and [`Eq`] according
462/// to FIDE rules for repeated positions. That is, considering
463///
464/// * piece positions
465/// * promoted pieces only in Crazyhouse
466/// * pockets only in Crazyhouse
467/// * turn
468/// * current castling rights
469/// * currently available [legal](`EnPassantMode::Legal`) en passant moves
470/// * remaining checks only in Three-Check
471///
472/// but specifically *ignoring* halfmove and fullmove counters.
473///
474/// Use [`Setup`] for structural equality.
475///
476/// # Arbitrary
477///
478/// All provided variants optionally implement
479/// [`arbitrary::Arbitrary`](https://docs.rs/arbitrary/1/arbitrary/trait.Arbitrary.html),
480/// such that all standard and Chess960 positions that meet all basic validity
481/// requirements of [`FromSetup`] can be generated.
482pub trait Position {
483    /// Piece positions on the board.
484    fn board(&self) -> &Board;
485    /// Positions of tracked promoted pieces. Used only for Crazyhouse.
486    fn promoted(&self) -> Bitboard;
487    /// Pockets in Crazyhouse.
488    fn pockets(&self) -> Option<&ByColor<ByRole<u8>>>;
489    /// Side to move.
490    fn turn(&self) -> Color;
491    /// Castling paths and unmoved rooks.
492    fn castles(&self) -> &Castles;
493    /// [Unconditionally](`EnPassantMode::Always`) gets the en passant target
494    /// square after a double pawn push, even if no en passant capture is
495    /// actually possible.
496    ///
497    /// Also see [`Position::pseudo_legal_ep_square()`] and
498    /// [`Position::legal_ep_square()`].
499    fn maybe_ep_square(&self) -> Option<Square>;
500    /// Remaining checks in Three-Check.
501    fn remaining_checks(&self) -> Option<&ByColor<RemainingChecks>>;
502    /// Number of half-moves since the last
503    /// [capture or pawn move](super::Move::is_zeroing()).
504    fn halfmoves(&self) -> u32;
505    /// Move number. Starts at 1 and is increased after every black move.
506    fn fullmoves(&self) -> NonZeroU32;
507
508    /// Converts the position to the current [`Setup`].
509    fn to_setup(&self, mode: EnPassantMode) -> Setup /* FINAL */ {
510        Setup {
511            board: self.board().clone(),
512            promoted: self.promoted(),
513            pockets: self.pockets().copied(),
514            turn: self.turn(),
515            castling_rights: self.castles().castling_rights(),
516            ep_square: self.ep_square(mode),
517            remaining_checks: self.remaining_checks().copied(),
518            halfmoves: self.halfmoves(),
519            fullmoves: self.fullmoves(),
520        }
521    }
522
523    /// Generates all legal moves.
524    fn legal_moves(&self) -> MoveList;
525
526    /// Generates a subset of legal moves: All piece moves and drops of type
527    /// `role` to the square `to`, excluding castling moves.
528    fn san_candidates(&self, role: Role, to: Square) -> MoveList {
529        let mut moves = self.legal_moves();
530        filter_san_candidates(role, to, &mut moves);
531        moves
532    }
533
534    /// Generates legal castling moves.
535    fn castling_moves(&self, side: CastlingSide) -> MoveList {
536        let mut moves = self.legal_moves();
537        moves.retain(|m| m.castling_side().is_some_and(|s| side == s));
538        moves
539    }
540
541    /// Generates en passant moves.
542    fn en_passant_moves(&self) -> MoveList {
543        let mut moves = self.legal_moves();
544        moves.retain(|m| m.is_en_passant());
545        moves
546    }
547
548    /// Generates capture moves.
549    fn capture_moves(&self) -> MoveList {
550        let mut moves = self.legal_moves();
551        moves.retain(|m| m.is_capture());
552        moves
553    }
554
555    /// Generate promotion moves.
556    fn promotion_moves(&self) -> MoveList {
557        let mut moves = self.legal_moves();
558        moves.retain(|m| m.is_promotion());
559        moves
560    }
561
562    /// Tests if a move is irreversible.
563    ///
564    /// In standard chess, pawn moves, captures, moves that destroy castling
565    /// rights, and moves that cede en-passant are irreversible.
566    ///
567    /// The implementation has false-negatives, because it does not consider
568    /// forced lines. For example, a checking move that will force the king
569    /// to lose castling rights is not considered irreversible, only the
570    /// actual king move is.
571    fn is_irreversible(&self, m: Move) -> bool {
572        (match m {
573            Move::Normal {
574                role: Role::Pawn, ..
575            }
576            | Move::Normal {
577                capture: Some(_), ..
578            }
579            | Move::Castle { .. }
580            | Move::EnPassant { .. }
581            | Move::Put { .. } => true,
582            Move::Normal { role, from, to, .. } => {
583                self.castles().castling_rights().contains(from)
584                    || self.castles().castling_rights().contains(to)
585                    || (role == Role::King && self.castles().has_color(self.turn()))
586            }
587        }) || self.legal_ep_square().is_some()
588    }
589
590    /// Attacks that a king on `square` would have to deal with.
591    fn king_attackers(&self, square: Square, attacker: Color, occupied: Bitboard) -> Bitboard {
592        self.board().attacks_to(square, attacker, occupied)
593    }
594
595    /// Checks if the game is over due to a special variant end condition.
596    ///
597    /// Note that for example stalemate is not considered a variant-specific
598    /// end condition (`is_variant_end()` will return `false`), but it can have
599    /// a special [`variant_outcome()`](Position::variant_outcome) in suicide
600    /// chess.
601    fn is_variant_end(&self) -> bool;
602
603    /// Tests if a side has insufficient winning material.
604    ///
605    /// Returns `false` if there is any series of legal moves that allows
606    /// `color` to win the game.
607    ///
608    /// The converse is not necessarily true for this static check.
609    /// The position might be locked up such that `color` can never win the
610    /// game (even if `!color` cooperates), or insufficient material might only
611    /// become apparent after a forced sequence of moves.
612    ///
613    /// The minimum guarantee is: Looking only at the material configuration,
614    /// taking into account color complexes of bishops and knights, but not
615    /// concrete piece positions, is there a position with the same material
616    /// configuration where `color` can win with a series of legal moves?
617    /// If not, then `color` has insufficient winning material.
618    ///
619    /// For a complete dynamic unwinnability solver see
620    /// <https://chasolver.org/>.
621    fn has_insufficient_material(&self, color: Color) -> bool;
622
623    /// Tests special variant winning, losing and drawing conditions.
624    fn variant_outcome(&self) -> Outcome;
625
626    /// Plays a move. It is the callers responsibility to ensure the move is
627    /// legal.
628    ///
629    /// # Panics
630    ///
631    /// Illegal moves can corrupt the state of the position and may
632    /// (or may not) panic or cause panics on future calls. Consider using
633    /// [`Position::play()`] if you cannot guarantee legality.
634    fn play_unchecked(&mut self, m: Move);
635
636    // Implementation note: Trait methods above this comment should be made
637    // available for VariantPosition. The provided methods below this comment
638    // are never overwritten in implementations, but for simplicity of use
639    // (especially around dyn) they are not moved to an extension trait.
640
641    /// Squares occupied by the side to move.
642    fn us(&self) -> Bitboard /* FINAL */ {
643        self.board().by_color(self.turn())
644    }
645
646    /// Squares occupied with the given piece type by the side to move.
647    fn our(&self, role: Role) -> Bitboard /* FINAL */ {
648        self.board().by_piece(role.of(self.turn()))
649    }
650
651    /// Squares occupied by the opponent of the side to move.
652    fn them(&self) -> Bitboard /* FINAL */ {
653        self.board().by_color(!self.turn())
654    }
655
656    /// Squares occupied with the given piece type by the opponent of the side
657    /// to move.
658    fn their(&self, role: Role) -> Bitboard /* FINAL */ {
659        self.board().by_piece(role.of(!self.turn()))
660    }
661
662    /// Tests a move for legality.
663    fn is_legal(&self, m: Move) -> bool /* FINAL */ {
664        let moves = match m {
665            Move::Normal { role, to, .. } | Move::Put { role, to } => self.san_candidates(role, to),
666            Move::EnPassant { to, .. } => self.san_candidates(Role::Pawn, to),
667            Move::Castle { king, rook } if king.file() < rook.file() => {
668                self.castling_moves(CastlingSide::KingSide)
669            }
670            Move::Castle { .. } => self.castling_moves(CastlingSide::QueenSide),
671        };
672        moves.contains(&m)
673    }
674
675    /// The en passant square, if it is the target of a
676    /// [pseudo-legal](`EnPassantMode::PseudoLegal`) en passant move.
677    fn pseudo_legal_ep_square(&self) -> Option<Square> /* FINAL */ {
678        self.maybe_ep_square().filter(|ep_square| {
679            (attacks::pawn_attacks(!self.turn(), *ep_square) & self.our(Role::Pawn)).any()
680        })
681    }
682
683    /// The en passant square, if it really is the target of a
684    /// [legal](`EnPassantMode::Legal`) en passant
685    /// move.
686    fn legal_ep_square(&self) -> Option<Square> /* FINAL */ {
687        self.pseudo_legal_ep_square()
688            .filter(|_| !self.en_passant_moves().is_empty())
689    }
690
691    /// The en passant square.
692    fn ep_square(&self, mode: EnPassantMode) -> Option<Square> /* FINAL */ {
693        match mode {
694            EnPassantMode::Always => self.maybe_ep_square(),
695            EnPassantMode::PseudoLegal => self.pseudo_legal_ep_square(),
696            EnPassantMode::Legal => self.legal_ep_square(),
697        }
698    }
699
700    /// Bitboard of pieces giving check.
701    fn checkers(&self) -> Bitboard /* FINAL */ {
702        self.our(Role::King).first().map_or(Bitboard(0), |king| {
703            self.king_attackers(king, !self.turn(), self.board().occupied())
704        })
705    }
706
707    /// Tests if the king is in check.
708    fn is_check(&self) -> bool /* FINAL */ {
709        self.checkers().any()
710    }
711
712    /// Tests for checkmate.
713    fn is_checkmate(&self) -> bool /* FINAL */ {
714        !self.checkers().is_empty() && self.legal_moves().is_empty()
715    }
716
717    /// Tests for stalemate.
718    fn is_stalemate(&self) -> bool /* FINAL */ {
719        self.checkers().is_empty() && !self.is_variant_end() && self.legal_moves().is_empty()
720    }
721
722    /// Tests if both sides
723    /// [have insufficient winning material](Position::has_insufficient_material).
724    fn is_insufficient_material(&self) -> bool /* FINAL */ {
725        self.has_insufficient_material(White) && self.has_insufficient_material(Black)
726    }
727
728    /// Tests if the game is over due to [checkmate](Position::is_checkmate()),
729    /// [stalemate](Position::is_stalemate()),
730    /// [insufficient material](Position::is_insufficient_material) or
731    /// [variant end](Position::is_variant_end).
732    fn is_game_over(&self) -> bool /* FINAL */ {
733        self.is_variant_end() || self.legal_moves().is_empty() || self.is_insufficient_material()
734    }
735
736    /// The outcome of the game, or [`Outcome::Unknown`] if the game is not over.
737    fn outcome(&self) -> Outcome /* FINAL */ {
738        let variant_outcome = self.variant_outcome();
739
740        if variant_outcome.is_known() {
741            return variant_outcome;
742        }
743
744        if self.legal_moves().is_empty() {
745            if self.is_check() {
746                Outcome::Known(KnownOutcome::Decisive {
747                    winner: !self.turn(),
748                })
749            } else {
750                Outcome::Known(KnownOutcome::Draw) // Stalemate
751            }
752        } else if self.is_insufficient_material() {
753            Outcome::Known(KnownOutcome::Draw)
754        } else {
755            Outcome::Unknown
756        }
757    }
758
759    /// Plays a move.
760    ///
761    ///
762    /// # Errors
763    ///
764    /// Returns a [`PlayError`] if the move is not legal. You can use
765    /// [`Position::play_unchecked()`] if you can guarantee legality.
766    fn play(mut self, m: Move) -> Result<Self, PlayError<Self>>
767    where
768        Self: Sized,
769    {
770        if self.is_legal(m) {
771            self.play_unchecked(m);
772            Ok(self)
773        } else {
774            Err(PlayError { m, position: self })
775        }
776    }
777
778    /// Swap turns and discard en passant rights. This is sometimes called
779    /// "playing a null move".
780    ///
781    /// # Errors
782    ///
783    /// Returns [`PositionError`] if swapping turns is not possible (usually
784    /// due to a check that has to be averted).
785    fn swap_turn(self) -> Result<Self, PositionError<Self>>
786    where
787        Self: Sized + FromSetup,
788    {
789        let mode = self.castles().mode();
790        let mut setup = self.to_setup(EnPassantMode::Always);
791        setup.swap_turn();
792        Self::from_setup(setup, mode)
793    }
794
795    /// Computes the Zobrist hash of the position from scratch. The hash
796    /// includes the position, except halfmove clock and fullmove number.
797    fn zobrist_hash<V: ZobristValue>(&self, mode: EnPassantMode) -> V
798    where
799        Self: Sized, /* FINAL */
800    {
801        let mut zobrist = self.board().board_zobrist_hash();
802
803        for sq in self.promoted() {
804            zobrist ^= V::zobrist_for_promoted(sq);
805        }
806
807        if let Some(pockets) = self.pockets() {
808            for (color, pocket) in pockets.zip_color() {
809                for (role, pieces) in pocket.zip_role() {
810                    zobrist ^= V::zobrist_for_pocket(color, role, pieces);
811                }
812            }
813        }
814
815        if self.turn() == Color::White {
816            zobrist ^= V::zobrist_for_white_turn();
817        }
818
819        let castles = self.castles();
820        for color in Color::ALL {
821            for side in CastlingSide::ALL {
822                if castles.has(color, side) {
823                    zobrist ^= V::zobrist_for_castling_right(color, side);
824                }
825            }
826        }
827
828        if let Some(sq) = self.ep_square(mode) {
829            zobrist ^= V::zobrist_for_en_passant_file(sq.file());
830        }
831
832        if let Some(remaining_checks) = self.remaining_checks() {
833            for (color, remaining) in remaining_checks.zip_color() {
834                zobrist ^= V::zobrist_for_remaining_checks(color, remaining);
835            }
836        }
837
838        zobrist
839    }
840
841    /// Incrementally computes the Zobrist hash of the position after playing
842    /// the legal move `m`, assuming that the current position has the Zobrist
843    /// hash `current`.
844    ///
845    /// May return `None` if an efficient incremental update is not implemented
846    /// for the given situation.
847    ///
848    /// # Panics
849    ///
850    /// May or may not panic if the move is not legal.
851    fn update_zobrist_hash<V: ZobristValue>(
852        &self,
853        current: V,
854        m: Move,
855        mode: EnPassantMode,
856    ) -> Option<V>
857    where
858        Self: Sized,
859    {
860        let _current = current;
861        let _m = m;
862        let _mode = mode;
863        None
864    }
865}
866
867#[cfg(feature = "arbitrary")]
868#[derive(arbitrary::Arbitrary)]
869enum PositionMutation {
870    LegalMove { idx: u8 },
871    DiscardPieceAt { sq: Square },
872    SetPieceAt { sq: Square, piece: Piece },
873    ToggleCastles { rooks: Bitboard, mode: CastlingMode },
874}
875
876#[cfg(feature = "arbitrary")]
877fn arbitrary_position<P>(
878    mutations: impl IntoIterator<Item = arbitrary::Result<PositionMutation>>,
879) -> arbitrary::Result<P>
880where
881    P: Position + FromSetup + Default,
882{
883    let mut pos = P::default();
884    for mutation in mutations {
885        let mutation = mutation?;
886        match mutation {
887            PositionMutation::LegalMove { idx } => {
888                let moves = pos.legal_moves();
889                if let Some(idx) = usize::from(idx).checked_rem(moves.len()) {
890                    pos.play_unchecked(moves[idx]);
891                }
892            }
893            PositionMutation::DiscardPieceAt { sq } => {
894                let mut setup = pos.to_setup(EnPassantMode::PseudoLegal);
895                setup.board.discard_piece_at(sq);
896                if let Ok(updated_pos) = setup
897                    .position(pos.castles().mode())
898                    .or_else(PositionError::ignore_invalid_castling_rights)
899                    .or_else(PositionError::ignore_invalid_ep_square)
900                {
901                    pos = updated_pos;
902                }
903            }
904            PositionMutation::SetPieceAt { sq, piece } => {
905                let mut setup = pos.to_setup(EnPassantMode::PseudoLegal);
906                setup.board.set_piece_at(sq, piece);
907                if let Ok(updated_pos) = setup
908                    .position(pos.castles().mode())
909                    .or_else(PositionError::ignore_invalid_castling_rights)
910                    .or_else(PositionError::ignore_invalid_ep_square)
911                {
912                    pos = updated_pos;
913                }
914            }
915            PositionMutation::ToggleCastles { rooks, mode } => {
916                let mut setup = pos.to_setup(EnPassantMode::PseudoLegal);
917                setup.castling_rights.toggle(rooks);
918                if let Ok(updated_pos) = setup
919                    .position(mode)
920                    .or_else(PositionError::ignore_invalid_castling_rights)
921                    .or_else(PositionError::ignore_invalid_ep_square)
922                {
923                    pos = updated_pos;
924                }
925            }
926        }
927    }
928    Ok(pos)
929}
930
931/// A standard Chess position.
932///
933/// # Equality
934///
935/// [`Hash`], [`PartialEq`],
936/// and [`Eq`] are implemented according to FIDE rules for
937/// repeated positions. See [`Position`](trait.Position.html#equality).
938#[derive(Clone, Debug)]
939pub struct Chess {
940    board: Board,
941    turn: Color,
942    castles: Castles,
943    ep_square: Option<EnPassant>,
944    halfmoves: u32,
945    fullmoves: NonZeroU32,
946}
947
948impl Chess {
949    #[cfg(feature = "variant")]
950    fn gives_check(&self, m: Move) -> bool {
951        let mut pos = self.clone();
952        pos.play_unchecked(m);
953        pos.is_check()
954    }
955
956    #[allow(clippy::type_complexity)]
957    fn from_setup_unchecked(
958        setup: Setup,
959        mode: CastlingMode,
960    ) -> (
961        Chess,
962        Option<ByColor<ByRole<u8>>>,
963        Option<ByColor<RemainingChecks>>,
964        PositionErrorKinds,
965    ) {
966        let mut errors = PositionErrorKinds::empty();
967
968        let castles = match Castles::from_setup(&setup, mode) {
969            Ok(castles) => castles,
970            Err(castles) => {
971                errors |= PositionErrorKinds::INVALID_CASTLING_RIGHTS;
972                castles
973            }
974        };
975
976        let ep_square = match EnPassant::from_setup(&setup) {
977            Ok(ep_square) => ep_square,
978            Err(()) => {
979                errors |= PositionErrorKinds::INVALID_EP_SQUARE;
980                None
981            }
982        };
983
984        let pos = Chess {
985            board: setup.board,
986            turn: setup.turn,
987            castles,
988            ep_square,
989            halfmoves: setup.halfmoves,
990            fullmoves: setup.fullmoves,
991        };
992
993        errors |= validate(&pos, ep_square);
994
995        (pos, setup.pockets, setup.remaining_checks, errors)
996    }
997
998    /// Initial position of any regular chess game.
999    pub const fn new() -> Chess {
1000        Chess {
1001            board: Board::new(),
1002            turn: White,
1003            castles: Castles::new(),
1004            ep_square: None,
1005            halfmoves: 0,
1006            fullmoves: NonZeroU32::MIN,
1007        }
1008    }
1009}
1010
1011impl Default for Chess {
1012    fn default() -> Chess {
1013        Chess::new()
1014    }
1015}
1016
1017impl Hash for Chess {
1018    fn hash<H: Hasher>(&self, state: &mut H) {
1019        self.board.hash(state);
1020        self.turn.hash(state);
1021        self.castles.castling_rights().hash(state);
1022        // Optimization: Not hashing legal_ep_square(), but still considered
1023        // for equality.
1024    }
1025}
1026
1027impl PartialEq for Chess {
1028    fn eq(&self, other: &Chess) -> bool {
1029        self.board == other.board
1030            && self.turn == other.turn
1031            && self.castles.castling_rights() == other.castles.castling_rights()
1032            && self.legal_ep_square() == other.legal_ep_square()
1033    }
1034}
1035
1036/// Equivalent to comparing `Position::to_setup(EnPassantMode::Legal)`.
1037///
1038/// Positions with different [`CastlingMode`] may be equivalent. En passant
1039/// squares are considered only if there is a legal en passant capture.
1040///
1041/// # Example
1042///
1043/// ```
1044/// use shakmaty::{CastlingMode, Chess, fen::Fen};
1045///
1046/// let fen: Fen = "r1bqkbnr/ppp2Qpp/2np4/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 4".parse()?;
1047/// let position: Chess = fen.clone().into_position(CastlingMode::Standard)?;
1048/// let position_960: Chess = fen.into_position(CastlingMode::Chess960)?;
1049/// assert_eq!(position, position_960);
1050///
1051/// # use shakmaty::{fen::ParseFenError, PositionError};
1052/// # #[derive(Debug)] struct CommonError;
1053/// # impl From<ParseFenError> for CommonError { fn from(_: ParseFenError) -> Self { Self } }
1054/// # impl<P> From<PositionError<P>> for CommonError { fn from(_: PositionError<P>) -> Self { Self } }
1055/// # Ok::<_, CommonError>(())
1056/// ```
1057impl Eq for Chess {}
1058
1059impl FromSetup for Chess {
1060    fn from_setup(setup: Setup, mode: CastlingMode) -> Result<Chess, PositionError<Chess>> {
1061        let (pos, _, _, errors) = Chess::from_setup_unchecked(setup, mode);
1062        PositionError { pos, errors }.strict()
1063    }
1064}
1065
1066#[cfg(feature = "arbitrary")]
1067impl arbitrary::Arbitrary<'_> for Chess {
1068    fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
1069        arbitrary_position(u.arbitrary_iter()?)
1070    }
1071
1072    fn arbitrary_take_rest(u: arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1073        arbitrary_position(u.arbitrary_take_rest_iter()?)
1074    }
1075}
1076
1077impl Position for Chess {
1078    fn board(&self) -> &Board {
1079        &self.board
1080    }
1081
1082    fn promoted(&self) -> Bitboard {
1083        Bitboard::EMPTY
1084    }
1085
1086    fn pockets(&self) -> Option<&ByColor<ByRole<u8>>> {
1087        None
1088    }
1089
1090    fn turn(&self) -> Color {
1091        self.turn
1092    }
1093
1094    fn castles(&self) -> &Castles {
1095        &self.castles
1096    }
1097
1098    fn maybe_ep_square(&self) -> Option<Square> {
1099        self.ep_square.map(EnPassant::square)
1100    }
1101
1102    fn remaining_checks(&self) -> Option<&ByColor<RemainingChecks>> {
1103        None
1104    }
1105
1106    fn halfmoves(&self) -> u32 {
1107        self.halfmoves
1108    }
1109
1110    fn fullmoves(&self) -> NonZeroU32 {
1111        self.fullmoves
1112    }
1113
1114    fn play_unchecked(&mut self, m: Move) {
1115        do_move(
1116            &mut self.board,
1117            &mut Bitboard(0),
1118            &mut self.turn,
1119            &mut self.castles,
1120            &mut self.ep_square,
1121            &mut self.halfmoves,
1122            &mut self.fullmoves,
1123            m,
1124        );
1125    }
1126
1127    fn legal_moves(&self) -> MoveList {
1128        let mut moves = MoveList::new();
1129
1130        let king = self
1131            .board()
1132            .king_of(self.turn())
1133            .expect("king in standard chess");
1134
1135        let has_ep = gen_en_passant(self.board(), self.turn(), self.ep_square, &mut moves);
1136
1137        let checkers = self.checkers();
1138        if checkers.is_empty() {
1139            let target = !self.us();
1140            gen_non_king(self, target, &mut moves);
1141            gen_safe_king(self, king, target, &mut moves);
1142            gen_castling_moves(
1143                self,
1144                &self.castles,
1145                king,
1146                CastlingSide::KingSide,
1147                &mut moves,
1148            );
1149            gen_castling_moves(
1150                self,
1151                &self.castles,
1152                king,
1153                CastlingSide::QueenSide,
1154                &mut moves,
1155            );
1156        } else {
1157            evasions(self, king, checkers, &mut moves);
1158        }
1159
1160        let blockers = slider_blockers(self.board(), self.them(), king);
1161        if blockers.any() || has_ep {
1162            moves.retain(|m| is_safe(self, king, *m, blockers));
1163        }
1164
1165        moves
1166    }
1167
1168    fn castling_moves(&self, side: CastlingSide) -> MoveList {
1169        let mut moves = MoveList::new();
1170        let king = self
1171            .board()
1172            .king_of(self.turn())
1173            .expect("king in standard chess");
1174        gen_castling_moves(self, &self.castles, king, side, &mut moves);
1175        moves
1176    }
1177
1178    fn en_passant_moves(&self) -> MoveList {
1179        let mut moves = MoveList::new();
1180
1181        if gen_en_passant(self.board(), self.turn(), self.ep_square, &mut moves) {
1182            let king = self
1183                .board()
1184                .king_of(self.turn())
1185                .expect("king in standard chess");
1186            let blockers = slider_blockers(self.board(), self.them(), king);
1187            moves.retain(|m| is_safe(self, king, *m, blockers));
1188        }
1189
1190        moves
1191    }
1192
1193    fn promotion_moves(&self) -> MoveList {
1194        let mut moves = MoveList::new();
1195
1196        let king = self
1197            .board()
1198            .king_of(self.turn())
1199            .expect("king in standard chess");
1200        let checkers = self.checkers();
1201
1202        if checkers.is_empty() {
1203            gen_pawn_moves(self, Bitboard::BACKRANKS, &mut moves);
1204        } else {
1205            evasions(self, king, checkers, &mut moves);
1206            moves.retain(|m| m.is_promotion());
1207        }
1208
1209        let blockers = slider_blockers(self.board(), self.them(), king);
1210        if blockers.any() {
1211            moves.retain(|m| is_safe(self, king, *m, blockers));
1212        }
1213
1214        moves
1215    }
1216
1217    fn san_candidates(&self, role: Role, to: Square) -> MoveList {
1218        let mut moves = MoveList::new();
1219
1220        let king = self
1221            .board()
1222            .king_of(self.turn())
1223            .expect("king in standard chess");
1224        let checkers = self.checkers();
1225
1226        if checkers.is_empty() {
1227            let piece_from = match role {
1228                Role::Pawn | Role::King => Bitboard(0),
1229                Role::Knight => attacks::knight_attacks(to),
1230                Role::Bishop => attacks::bishop_attacks(to, self.board().occupied()),
1231                Role::Rook => attacks::rook_attacks(to, self.board().occupied()),
1232                Role::Queen => attacks::queen_attacks(to, self.board().occupied()),
1233            };
1234
1235            if !self.us().contains(to) {
1236                match role {
1237                    Role::Pawn => gen_pawn_moves(self, Bitboard::from_square(to), &mut moves),
1238                    Role::King => gen_safe_king(self, king, Bitboard::from_square(to), &mut moves),
1239                    _ => {}
1240                }
1241
1242                (piece_from & self.our(role)).for_each(|from| {
1243                    moves.push(Move::Normal {
1244                        role,
1245                        from,
1246                        capture: self.board().role_at(to),
1247                        to,
1248                        promotion: None,
1249                    });
1250                });
1251            }
1252        } else {
1253            evasions(self, king, checkers, &mut moves);
1254            filter_san_candidates(role, to, &mut moves);
1255        }
1256
1257        let has_ep = role == Role::Pawn
1258            && self.ep_square.map(Square::from) == Some(to)
1259            && gen_en_passant(self.board(), self.turn(), self.ep_square, &mut moves);
1260
1261        let blockers = slider_blockers(self.board(), self.them(), king);
1262        if blockers.any() || has_ep {
1263            moves.retain(|m| is_safe(self, king, *m, blockers));
1264        }
1265
1266        moves
1267    }
1268
1269    fn has_insufficient_material(&self, color: Color) -> bool {
1270        // Pawns, rooks and queens are never insufficient material.
1271        if (self.board.by_color(color) & (self.board.pawns() | self.board.rooks_and_queens())).any()
1272        {
1273            return false;
1274        }
1275
1276        // Knights are only insufficient material if:
1277        // (1) We do not have any other pieces, including more than one knight.
1278        // (2) The opponent does not have pawns, knights, bishops or rooks.
1279        //     These would allow self mate.
1280        if (self.board.by_color(color) & self.board.knights()).any() {
1281            return self.board.by_color(color).count() <= 2
1282                && (self.board.by_color(!color) & !self.board.kings() & !self.board().queens())
1283                    .is_empty();
1284        }
1285
1286        // Bishops are only insufficient material if:
1287        // (1) We do not have any other pieces, including bishops on the
1288        //     opposite color.
1289        // (2) The opponent does not have bishops on the opposite color,
1290        //      pawns or knights. These would allow self mate.
1291        if (self.board.by_color(color) & self.board.bishops()).any() {
1292            let same_color = (self.board().bishops() & Bitboard::DARK_SQUARES).is_empty()
1293                || (self.board().bishops() & Bitboard::LIGHT_SQUARES).is_empty();
1294            return same_color
1295                && self.board().knights().is_empty()
1296                && self.board().pawns().is_empty();
1297        }
1298
1299        true
1300    }
1301
1302    /// Always returns `false`.
1303    fn is_variant_end(&self) -> bool {
1304        false
1305    }
1306
1307    /// Always returns [`Outcome::Unknown`].
1308    fn variant_outcome(&self) -> Outcome {
1309        Outcome::Unknown
1310    }
1311
1312    fn update_zobrist_hash<V: ZobristValue>(
1313        &self,
1314        current: V,
1315        m: Move,
1316        _mode: EnPassantMode,
1317    ) -> Option<V> {
1318        do_update_zobrist_hash(current, m, self.turn, &self.castles, self.ep_square)
1319    }
1320}
1321
1322#[cfg(feature = "variant")]
1323pub(crate) mod variant {
1324    use core::{cmp::min, ops::Not};
1325
1326    use super::*;
1327
1328    enum KingTag {}
1329
1330    impl Stepper for KingTag {
1331        const ROLE: Role = Role::King;
1332        fn attacks(from: Square) -> Bitboard {
1333            attacks::king_attacks(from)
1334        }
1335    }
1336
1337    /// An Atomic Chess position.
1338    #[derive(Clone, Debug)]
1339    pub struct Atomic {
1340        board: Board,
1341        turn: Color,
1342        castles: Castles,
1343        ep_square: Option<EnPassant>,
1344        halfmoves: u32,
1345        fullmoves: NonZeroU32,
1346    }
1347
1348    impl Atomic {
1349        pub const fn new() -> Atomic {
1350            Atomic {
1351                board: Board::new(),
1352                turn: White,
1353                castles: Castles::new(),
1354                ep_square: None,
1355                halfmoves: 0,
1356                fullmoves: NonZeroU32::MIN,
1357            }
1358        }
1359    }
1360
1361    impl Default for Atomic {
1362        fn default() -> Atomic {
1363            Atomic::new()
1364        }
1365    }
1366
1367    impl Hash for Atomic {
1368        fn hash<H: Hasher>(&self, state: &mut H) {
1369            self.board.hash(state);
1370            self.turn.hash(state);
1371            self.castles.castling_rights().hash(state);
1372        }
1373    }
1374
1375    impl PartialEq for Atomic {
1376        fn eq(&self, other: &Self) -> bool {
1377            self.board == other.board
1378                && self.turn == other.turn
1379                && self.castles.castling_rights() == other.castles.castling_rights()
1380                && self.legal_ep_square() == other.legal_ep_square()
1381        }
1382    }
1383
1384    impl Eq for Atomic {}
1385
1386    impl FromSetup for Atomic {
1387        fn from_setup(setup: Setup, mode: CastlingMode) -> Result<Atomic, PositionError<Atomic>> {
1388            let mut errors = PositionErrorKinds::empty();
1389
1390            let castles = match Castles::from_setup(&setup, mode) {
1391                Ok(castles) => castles,
1392                Err(castles) => {
1393                    errors |= PositionErrorKinds::INVALID_CASTLING_RIGHTS;
1394                    castles
1395                }
1396            };
1397
1398            let ep_square = match EnPassant::from_setup(&setup) {
1399                Ok(ep_square) => ep_square,
1400                Err(()) => {
1401                    errors |= PositionErrorKinds::INVALID_EP_SQUARE;
1402                    None
1403                }
1404            };
1405
1406            let pos = Atomic {
1407                board: setup.board,
1408                turn: setup.turn,
1409                castles,
1410                ep_square,
1411                halfmoves: setup.halfmoves,
1412                fullmoves: setup.fullmoves,
1413            };
1414
1415            errors |= validate(&pos, ep_square);
1416
1417            if ep_square.is_none() {
1418                // Other king moving away can cause many checks to be given
1419                // at the same time. We do not check the details, or even that
1420                // the other king really is close.
1421                errors.remove(PositionErrorKinds::IMPOSSIBLE_CHECK);
1422            }
1423
1424            if (pos.them() & pos.board().kings()).any() {
1425                // Our king just exploded. Game over, but valid position.
1426                errors.remove(PositionErrorKinds::MISSING_KING);
1427            }
1428
1429            PositionError { pos, errors }.strict()
1430        }
1431    }
1432
1433    #[cfg(feature = "arbitrary")]
1434    impl arbitrary::Arbitrary<'_> for Atomic {
1435        fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
1436            arbitrary_position(u.arbitrary_iter()?)
1437        }
1438
1439        fn arbitrary_take_rest(u: arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1440            arbitrary_position(u.arbitrary_take_rest_iter()?)
1441        }
1442    }
1443
1444    impl Position for Atomic {
1445        fn board(&self) -> &Board {
1446            &self.board
1447        }
1448
1449        fn promoted(&self) -> Bitboard {
1450            Bitboard::EMPTY
1451        }
1452
1453        fn pockets(&self) -> Option<&ByColor<ByRole<u8>>> {
1454            None
1455        }
1456
1457        fn turn(&self) -> Color {
1458            self.turn
1459        }
1460
1461        fn castles(&self) -> &Castles {
1462            &self.castles
1463        }
1464
1465        fn maybe_ep_square(&self) -> Option<Square> {
1466            self.ep_square.map(EnPassant::square)
1467        }
1468
1469        fn remaining_checks(&self) -> Option<&ByColor<RemainingChecks>> {
1470            None
1471        }
1472
1473        fn halfmoves(&self) -> u32 {
1474            self.halfmoves
1475        }
1476
1477        fn fullmoves(&self) -> NonZeroU32 {
1478            self.fullmoves
1479        }
1480
1481        fn play_unchecked(&mut self, m: Move) {
1482            do_move(
1483                &mut self.board,
1484                &mut Bitboard(0),
1485                &mut self.turn,
1486                &mut self.castles,
1487                &mut self.ep_square,
1488                &mut self.halfmoves,
1489                &mut self.fullmoves,
1490                m,
1491            );
1492
1493            match m {
1494                Move::Normal {
1495                    capture: Some(_),
1496                    to,
1497                    ..
1498                }
1499                | Move::EnPassant { to, .. } => {
1500                    self.board.discard_piece_at(to);
1501
1502                    let explosion_radius =
1503                        attacks::king_attacks(to) & self.board().occupied() & !self.board.pawns();
1504
1505                    if (explosion_radius & self.board().kings() & self.us()).any() {
1506                        self.castles.discard_color(self.turn());
1507                    }
1508
1509                    for explosion in explosion_radius {
1510                        self.board.discard_piece_at(explosion);
1511                        self.castles.discard_rook(explosion);
1512                    }
1513                }
1514                _ => (),
1515            }
1516        }
1517
1518        fn legal_moves(&self) -> MoveList {
1519            let mut moves = MoveList::new();
1520
1521            gen_en_passant(self.board(), self.turn(), self.ep_square, &mut moves);
1522            gen_non_king(self, !self.us(), &mut moves);
1523            KingTag::gen_moves(self, !self.board().occupied(), &mut moves);
1524            if let Some(king) = self.board().king_of(self.turn()) {
1525                gen_castling_moves(
1526                    self,
1527                    &self.castles,
1528                    king,
1529                    CastlingSide::KingSide,
1530                    &mut moves,
1531                );
1532                gen_castling_moves(
1533                    self,
1534                    &self.castles,
1535                    king,
1536                    CastlingSide::QueenSide,
1537                    &mut moves,
1538                );
1539            }
1540
1541            // Atomic move generation could be implemented more efficiently.
1542            // For simplicity we filter all pseudo legal moves.
1543            moves.retain(|m| {
1544                let mut after = self.clone();
1545                after.play_unchecked(*m);
1546                if let Some(our_king) = after.board().king_of(self.turn()) {
1547                    (after.board.kings() & after.board().by_color(!self.turn())).is_empty()
1548                        || after
1549                            .king_attackers(our_king, !self.turn(), after.board.occupied())
1550                            .is_empty()
1551                } else {
1552                    false
1553                }
1554            });
1555
1556            moves
1557        }
1558
1559        fn king_attackers(&self, square: Square, attacker: Color, occupied: Bitboard) -> Bitboard {
1560            let attacker_kings = self.board().kings() & self.board().by_color(attacker);
1561            if attacker_kings.is_empty() || (attacks::king_attacks(square) & attacker_kings).any() {
1562                Bitboard(0)
1563            } else {
1564                self.board().attacks_to(square, attacker, occupied)
1565            }
1566        }
1567
1568        fn is_variant_end(&self) -> bool {
1569            self.variant_outcome().is_known()
1570        }
1571
1572        fn has_insufficient_material(&self, color: Color) -> bool {
1573            // Remaining material does not matter if the opponents king is already
1574            // exploded.
1575            if (self.board.by_color(!color) & self.board.kings()).is_empty() {
1576                return false;
1577            }
1578
1579            // Bare king can not mate.
1580            if (self.board.by_color(color) & !self.board.kings()).is_empty() {
1581                return true;
1582            }
1583
1584            // As long as the opponent king is not alone there is always a chance
1585            // their own piece explodes next to it.
1586            if (self.board.by_color(!color) & !self.board.kings()).any() {
1587                // Unless there are only bishops that cannot explode each other.
1588                if self.board().occupied() == self.board().kings() | self.board().bishops() {
1589                    if (self.board().bishops() & self.board().white() & Bitboard::DARK_SQUARES)
1590                        .is_empty()
1591                    {
1592                        return (self.board().bishops()
1593                            & self.board().black()
1594                            & Bitboard::LIGHT_SQUARES)
1595                            .is_empty();
1596                    }
1597                    if (self.board().bishops() & self.board().white() & Bitboard::LIGHT_SQUARES)
1598                        .is_empty()
1599                    {
1600                        return (self.board().bishops()
1601                            & self.board().black()
1602                            & Bitboard::DARK_SQUARES)
1603                            .is_empty();
1604                    }
1605                }
1606
1607                return false;
1608            }
1609
1610            // Queen or pawn (future queen) can give mate against bare king.
1611            if self.board().queens().any() || self.board.pawns().any() {
1612                return false;
1613            }
1614
1615            // Single knight, bishop or rook can not mate against bare king.
1616            if (self.board().knights() | self.board().bishops() | self.board().rooks()).count() == 1
1617            {
1618                return true;
1619            }
1620
1621            // Two knights can not mate against bare king.
1622            if self.board().occupied() == self.board().kings() | self.board().knights() {
1623                return self.board().knights().count() <= 2;
1624            }
1625
1626            false
1627        }
1628
1629        fn variant_outcome(&self) -> Outcome {
1630            for color in Color::ALL {
1631                if (self.board().by_color(color) & self.board().kings()).is_empty() {
1632                    return Outcome::Known(KnownOutcome::Decisive { winner: !color });
1633                }
1634            }
1635            Outcome::Unknown
1636        }
1637
1638        fn update_zobrist_hash<V: ZobristValue>(
1639            &self,
1640            mut current: V,
1641            m: Move,
1642            _mode: EnPassantMode,
1643        ) -> Option<V> {
1644            if self.ep_square.is_some() {
1645                return None;
1646            }
1647
1648            match m {
1649                Move::Normal {
1650                    role,
1651                    from,
1652                    capture: None, // Do not try to resolve explosions
1653                    to,
1654                    promotion,
1655                } if (role != Role::Pawn || Square::abs_diff(from, to) != 16)
1656                    && role != Role::King
1657                    && !self.castles.castling_rights().contains(from)
1658                    && !self.castles.castling_rights().contains(to) =>
1659                {
1660                    current ^= V::zobrist_for_white_turn();
1661                    current ^= V::zobrist_for_piece(from, role.of(self.turn));
1662                    current ^= V::zobrist_for_piece(to, promotion.unwrap_or(role).of(self.turn));
1663                    Some(current)
1664                }
1665                _ => None,
1666            }
1667        }
1668    }
1669
1670    /// An Antichess position. Antichess is also known as Giveaway, but players
1671    /// start without castling rights.
1672    #[derive(Clone, Debug)]
1673    pub struct Antichess {
1674        board: Board,
1675        turn: Color,
1676        castles: Castles,
1677        ep_square: Option<EnPassant>,
1678        halfmoves: u32,
1679        fullmoves: NonZeroU32,
1680    }
1681
1682    impl Antichess {
1683        pub const fn new() -> Antichess {
1684            Antichess {
1685                board: Board::new(),
1686                turn: White,
1687                castles: Castles::empty(CastlingMode::Standard),
1688                ep_square: None,
1689                halfmoves: 0,
1690                fullmoves: NonZeroU32::MIN,
1691            }
1692        }
1693    }
1694
1695    impl Default for Antichess {
1696        fn default() -> Antichess {
1697            Antichess::new()
1698        }
1699    }
1700
1701    impl Hash for Antichess {
1702        fn hash<H: Hasher>(&self, state: &mut H) {
1703            self.board.hash(state);
1704            self.turn.hash(state);
1705            self.castles.castling_rights().hash(state);
1706        }
1707    }
1708
1709    impl PartialEq for Antichess {
1710        fn eq(&self, other: &Self) -> bool {
1711            self.board == other.board
1712                && self.turn == other.turn
1713                && self.castles.castling_rights() == other.castles.castling_rights()
1714                && self.legal_ep_square() == other.legal_ep_square()
1715        }
1716    }
1717
1718    impl Eq for Antichess {}
1719
1720    impl FromSetup for Antichess {
1721        fn from_setup(
1722            setup: Setup,
1723            mode: CastlingMode,
1724        ) -> Result<Antichess, PositionError<Antichess>> {
1725            let mut errors = PositionErrorKinds::empty();
1726
1727            let ep_square = match EnPassant::from_setup(&setup) {
1728                Ok(ep_square) => ep_square,
1729                Err(()) => {
1730                    errors |= PositionErrorKinds::INVALID_EP_SQUARE;
1731                    None
1732                }
1733            };
1734
1735            let pos = Antichess {
1736                board: setup.board,
1737                turn: setup.turn,
1738                castles: Castles::empty(mode),
1739                ep_square,
1740                halfmoves: setup.halfmoves,
1741                fullmoves: setup.fullmoves,
1742            };
1743
1744            if setup.castling_rights.any() {
1745                errors |= PositionErrorKinds::INVALID_CASTLING_RIGHTS;
1746            }
1747
1748            errors |= validate(&pos, ep_square)
1749                - PositionErrorKinds::MISSING_KING
1750                - PositionErrorKinds::TOO_MANY_KINGS
1751                - PositionErrorKinds::OPPOSITE_CHECK
1752                - PositionErrorKinds::IMPOSSIBLE_CHECK;
1753
1754            PositionError { pos, errors }.strict()
1755        }
1756    }
1757
1758    #[cfg(feature = "arbitrary")]
1759    impl arbitrary::Arbitrary<'_> for Antichess {
1760        fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
1761            arbitrary_position(u.arbitrary_iter()?)
1762        }
1763
1764        fn arbitrary_take_rest(u: arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1765            arbitrary_position(u.arbitrary_take_rest_iter()?)
1766        }
1767    }
1768
1769    impl Position for Antichess {
1770        fn board(&self) -> &Board {
1771            &self.board
1772        }
1773
1774        fn promoted(&self) -> Bitboard {
1775            Bitboard::EMPTY
1776        }
1777
1778        fn pockets(&self) -> Option<&ByColor<ByRole<u8>>> {
1779            None
1780        }
1781
1782        fn turn(&self) -> Color {
1783            self.turn
1784        }
1785
1786        fn castles(&self) -> &Castles {
1787            &self.castles
1788        }
1789
1790        fn maybe_ep_square(&self) -> Option<Square> {
1791            self.ep_square.map(EnPassant::square)
1792        }
1793
1794        fn remaining_checks(&self) -> Option<&ByColor<RemainingChecks>> {
1795            None
1796        }
1797
1798        fn halfmoves(&self) -> u32 {
1799            self.halfmoves
1800        }
1801
1802        fn fullmoves(&self) -> NonZeroU32 {
1803            self.fullmoves
1804        }
1805
1806        fn play_unchecked(&mut self, m: Move) {
1807            do_move(
1808                &mut self.board,
1809                &mut Bitboard(0),
1810                &mut self.turn,
1811                &mut self.castles,
1812                &mut self.ep_square,
1813                &mut self.halfmoves,
1814                &mut self.fullmoves,
1815                m,
1816            );
1817        }
1818
1819        fn en_passant_moves(&self) -> MoveList {
1820            let mut moves = MoveList::new();
1821            gen_en_passant(self.board(), self.turn, self.ep_square, &mut moves);
1822            moves
1823        }
1824
1825        fn capture_moves(&self) -> MoveList {
1826            let mut moves = self.en_passant_moves();
1827            let them = self.them();
1828            gen_non_king(self, them, &mut moves);
1829            add_king_promotions(&mut moves);
1830            KingTag::gen_moves(self, them, &mut moves);
1831            moves
1832        }
1833
1834        fn legal_moves(&self) -> MoveList {
1835            let mut moves = self.capture_moves();
1836
1837            if moves.is_empty() {
1838                // No compulsory captures. Generate everything else.
1839                gen_non_king(self, !self.board().occupied(), &mut moves);
1840                add_king_promotions(&mut moves);
1841                KingTag::gen_moves(self, !self.board().occupied(), &mut moves);
1842            }
1843
1844            moves
1845        }
1846
1847        fn king_attackers(
1848            &self,
1849            _square: Square,
1850            _attacker: Color,
1851            _occupied: Bitboard,
1852        ) -> Bitboard {
1853            Bitboard(0)
1854        }
1855
1856        fn is_variant_end(&self) -> bool {
1857            self.board().white().is_empty() || self.board().black().is_empty()
1858        }
1859
1860        fn has_insufficient_material(&self, color: Color) -> bool {
1861            if self.board.by_color(color).is_empty() {
1862                false
1863            } else if self.board.by_color(!color).is_empty() {
1864                true
1865            } else if self.board.occupied() == self.board.bishops() {
1866                // In a position with only bishops, check if all our bishops
1867                // can be captured.
1868                let we_some_on_light = (self.board.by_color(color) & Bitboard::LIGHT_SQUARES).any();
1869                let we_some_on_dark = (self.board.by_color(color) & Bitboard::DARK_SQUARES).any();
1870                let they_all_on_dark =
1871                    (self.board.by_color(!color) & Bitboard::LIGHT_SQUARES).is_empty();
1872                let they_all_on_light =
1873                    (self.board.by_color(!color) & Bitboard::DARK_SQUARES).is_empty();
1874                (we_some_on_light && they_all_on_dark) || (we_some_on_dark && they_all_on_light)
1875            } else if self.board.occupied() == self.board.knights() {
1876                match (
1877                    self.board.white().single_square(),
1878                    self.board.black().single_square(),
1879                ) {
1880                    (Some(white_single_knight), Some(black_single_knight)) => {
1881                        self.turn
1882                            == color
1883                                ^ white_single_knight.is_light()
1884                                ^ black_single_knight.is_dark()
1885                    }
1886                    _ => false,
1887                }
1888            } else {
1889                false
1890            }
1891        }
1892
1893        fn variant_outcome(&self) -> Outcome {
1894            if self.us().is_empty() || self.is_stalemate() {
1895                Outcome::Known(KnownOutcome::Decisive {
1896                    winner: self.turn(),
1897                })
1898            } else {
1899                Outcome::Unknown
1900            }
1901        }
1902
1903        fn update_zobrist_hash<V: ZobristValue>(
1904            &self,
1905            current: V,
1906            m: Move,
1907            _mode: EnPassantMode,
1908        ) -> Option<V> {
1909            do_update_zobrist_hash(current, m, self.turn, &self.castles, self.ep_square)
1910        }
1911    }
1912
1913    /// A King of the Hill position.
1914    #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
1915    pub struct KingOfTheHill {
1916        chess: Chess,
1917    }
1918
1919    impl KingOfTheHill {
1920        pub const fn new() -> KingOfTheHill {
1921            KingOfTheHill {
1922                chess: Chess::new(),
1923            }
1924        }
1925    }
1926
1927    impl FromSetup for KingOfTheHill {
1928        fn from_setup(
1929            setup: Setup,
1930            mode: CastlingMode,
1931        ) -> Result<KingOfTheHill, PositionError<KingOfTheHill>> {
1932            let (chess, _, _, errors) = Chess::from_setup_unchecked(setup, mode);
1933            PositionError {
1934                errors,
1935                pos: KingOfTheHill { chess },
1936            }
1937            .strict()
1938        }
1939    }
1940
1941    #[cfg(feature = "arbitrary")]
1942    impl arbitrary::Arbitrary<'_> for KingOfTheHill {
1943        fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
1944            arbitrary_position(u.arbitrary_iter()?)
1945        }
1946
1947        fn arbitrary_take_rest(u: arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1948            arbitrary_position(u.arbitrary_take_rest_iter()?)
1949        }
1950    }
1951
1952    impl Position for KingOfTheHill {
1953        fn board(&self) -> &Board {
1954            self.chess.board()
1955        }
1956
1957        fn promoted(&self) -> Bitboard {
1958            Bitboard::EMPTY
1959        }
1960
1961        fn castles(&self) -> &Castles {
1962            self.chess.castles()
1963        }
1964
1965        fn pockets(&self) -> Option<&ByColor<ByRole<u8>>> {
1966            None
1967        }
1968
1969        fn turn(&self) -> Color {
1970            self.chess.turn()
1971        }
1972
1973        fn maybe_ep_square(&self) -> Option<Square> {
1974            self.chess.maybe_ep_square()
1975        }
1976
1977        fn remaining_checks(&self) -> Option<&ByColor<RemainingChecks>> {
1978            None
1979        }
1980
1981        fn halfmoves(&self) -> u32 {
1982            self.chess.halfmoves()
1983        }
1984
1985        fn fullmoves(&self) -> NonZeroU32 {
1986            self.chess.fullmoves()
1987        }
1988
1989        fn play_unchecked(&mut self, m: Move) {
1990            self.chess.play_unchecked(m);
1991        }
1992
1993        fn legal_moves(&self) -> MoveList {
1994            if self.is_variant_end() {
1995                MoveList::new()
1996            } else {
1997                self.chess.legal_moves()
1998            }
1999        }
2000
2001        fn castling_moves(&self, side: CastlingSide) -> MoveList {
2002            if self.is_variant_end() {
2003                MoveList::new()
2004            } else {
2005                self.chess.castling_moves(side)
2006            }
2007        }
2008
2009        fn en_passant_moves(&self) -> MoveList {
2010            if self.is_variant_end() {
2011                MoveList::new()
2012            } else {
2013                self.chess.en_passant_moves()
2014            }
2015        }
2016
2017        fn san_candidates(&self, role: Role, to: Square) -> MoveList {
2018            if self.is_variant_end() {
2019                MoveList::new()
2020            } else {
2021                self.chess.san_candidates(role, to)
2022            }
2023        }
2024
2025        fn has_insufficient_material(&self, _color: Color) -> bool {
2026            // Even a lone king can walk onto the hill.
2027            false
2028        }
2029
2030        fn is_variant_end(&self) -> bool {
2031            (self.chess.board().kings() & Bitboard::CENTER).any()
2032        }
2033
2034        fn variant_outcome(&self) -> Outcome {
2035            for color in Color::ALL {
2036                if (self.board().by_color(color) & self.board().kings() & Bitboard::CENTER).any() {
2037                    return Outcome::Known(KnownOutcome::Decisive { winner: color });
2038                }
2039            }
2040            Outcome::Unknown
2041        }
2042
2043        fn update_zobrist_hash<V: ZobristValue>(
2044            &self,
2045            current: V,
2046            m: Move,
2047            mode: EnPassantMode,
2048        ) -> Option<V>
2049        where
2050            Self: Sized,
2051        {
2052            self.chess.update_zobrist_hash(current, m, mode)
2053        }
2054    }
2055
2056    /// A Three-Check position.
2057    #[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
2058    pub struct ThreeCheck {
2059        chess: Chess,
2060        remaining_checks: ByColor<RemainingChecks>,
2061    }
2062
2063    impl ThreeCheck {
2064        pub const fn new() -> ThreeCheck {
2065            ThreeCheck {
2066                chess: Chess::new(),
2067                remaining_checks: ByColor {
2068                    black: RemainingChecks::new(3),
2069                    white: RemainingChecks::new(3),
2070                },
2071            }
2072        }
2073    }
2074
2075    impl FromSetup for ThreeCheck {
2076        fn from_setup(
2077            setup: Setup,
2078            mode: CastlingMode,
2079        ) -> Result<ThreeCheck, PositionError<ThreeCheck>> {
2080            let (chess, _, remaining_checks, mut errors) = Chess::from_setup_unchecked(setup, mode);
2081
2082            let remaining_checks = remaining_checks.unwrap_or_default();
2083            if remaining_checks.iter().all(|remaining| remaining.is_zero()) {
2084                errors |= PositionErrorKinds::VARIANT;
2085            }
2086
2087            PositionError {
2088                errors,
2089                pos: ThreeCheck {
2090                    chess,
2091                    remaining_checks,
2092                },
2093            }
2094            .strict()
2095        }
2096    }
2097
2098    #[cfg(feature = "arbitrary")]
2099    impl arbitrary::Arbitrary<'_> for ThreeCheck {
2100        fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
2101            arbitrary_position(u.arbitrary_iter()?)
2102        }
2103
2104        fn arbitrary_take_rest(u: arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
2105            arbitrary_position(u.arbitrary_take_rest_iter()?)
2106        }
2107    }
2108
2109    impl Position for ThreeCheck {
2110        fn board(&self) -> &Board {
2111            self.chess.board()
2112        }
2113
2114        fn promoted(&self) -> Bitboard {
2115            Bitboard::EMPTY
2116        }
2117
2118        fn pockets(&self) -> Option<&ByColor<ByRole<u8>>> {
2119            None
2120        }
2121
2122        fn turn(&self) -> Color {
2123            self.chess.turn()
2124        }
2125
2126        fn castles(&self) -> &Castles {
2127            self.chess.castles()
2128        }
2129
2130        fn maybe_ep_square(&self) -> Option<Square> {
2131            self.chess.maybe_ep_square()
2132        }
2133
2134        fn remaining_checks(&self) -> Option<&ByColor<RemainingChecks>> {
2135            Some(&self.remaining_checks)
2136        }
2137
2138        fn halfmoves(&self) -> u32 {
2139            self.chess.halfmoves()
2140        }
2141
2142        fn fullmoves(&self) -> NonZeroU32 {
2143            self.chess.fullmoves
2144        }
2145
2146        fn play_unchecked(&mut self, m: Move) {
2147            let turn = self.chess.turn();
2148            self.chess.play_unchecked(m);
2149            if self.is_check() {
2150                let checks = self.remaining_checks.get_mut(turn);
2151                *checks = checks.saturating_sub(1);
2152            }
2153        }
2154
2155        fn legal_moves(&self) -> MoveList {
2156            if self.is_variant_end() {
2157                MoveList::new()
2158            } else {
2159                self.chess.legal_moves()
2160            }
2161        }
2162
2163        fn castling_moves(&self, side: CastlingSide) -> MoveList {
2164            if self.is_variant_end() {
2165                MoveList::new()
2166            } else {
2167                self.chess.castling_moves(side)
2168            }
2169        }
2170
2171        fn en_passant_moves(&self) -> MoveList {
2172            if self.is_variant_end() {
2173                MoveList::new()
2174            } else {
2175                self.chess.en_passant_moves()
2176            }
2177        }
2178
2179        fn san_candidates(&self, role: Role, to: Square) -> MoveList {
2180            if self.is_variant_end() {
2181                MoveList::new()
2182            } else {
2183                self.chess.san_candidates(role, to)
2184            }
2185        }
2186
2187        fn has_insufficient_material(&self, color: Color) -> bool {
2188            // Any remaining piece can give check.
2189            (self.board().by_color(color) & !self.board().kings()).is_empty()
2190        }
2191
2192        fn is_irreversible(&self, m: Move) -> bool {
2193            self.chess.is_irreversible(m) || self.chess.gives_check(m)
2194        }
2195
2196        fn is_variant_end(&self) -> bool {
2197            self.remaining_checks
2198                .iter()
2199                .any(|remaining| remaining.is_zero())
2200        }
2201
2202        fn variant_outcome(&self) -> Outcome {
2203            self.remaining_checks
2204                .find(|remaining| remaining.is_zero())
2205                .map_or(Outcome::Unknown, |winner| {
2206                    Outcome::Known(KnownOutcome::Decisive { winner })
2207                })
2208        }
2209    }
2210
2211    /// A Crazyhouse position.
2212    #[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
2213    pub struct Crazyhouse {
2214        chess: Chess,
2215        promoted: Bitboard,
2216        pockets: ByColor<ByRole<u8>>,
2217    }
2218
2219    impl Crazyhouse {
2220        pub const fn new() -> Crazyhouse {
2221            Crazyhouse {
2222                chess: Chess::new(),
2223                promoted: Bitboard::EMPTY,
2224                pockets: ByColor {
2225                    black: ByRole {
2226                        pawn: 0,
2227                        knight: 0,
2228                        bishop: 0,
2229                        rook: 0,
2230                        queen: 0,
2231                        king: 0,
2232                    },
2233                    white: ByRole {
2234                        pawn: 0,
2235                        knight: 0,
2236                        bishop: 0,
2237                        rook: 0,
2238                        queen: 0,
2239                        king: 0,
2240                    },
2241                },
2242            }
2243        }
2244
2245        fn our_pocket(&self) -> &ByRole<u8> {
2246            self.pockets.get(self.turn())
2247        }
2248
2249        fn our_pocket_mut(&mut self) -> &mut ByRole<u8> {
2250            let turn = self.turn();
2251            self.pockets.get_mut(turn)
2252        }
2253
2254        fn legal_put_squares(&self) -> Bitboard {
2255            let checkers = self.checkers();
2256
2257            if checkers.is_empty() {
2258                !self.board().occupied()
2259            } else if let Some(checker) = checkers.single_square() {
2260                let king = self
2261                    .board()
2262                    .king_of(self.turn())
2263                    .expect("king in crazyhouse");
2264                attacks::between(checker, king)
2265            } else {
2266                Bitboard(0)
2267            }
2268        }
2269    }
2270
2271    impl FromSetup for Crazyhouse {
2272        fn from_setup(
2273            setup: Setup,
2274            mode: CastlingMode,
2275        ) -> Result<Crazyhouse, PositionError<Crazyhouse>> {
2276            let promoted = setup.promoted
2277                & setup.board.occupied()
2278                & !setup.board.pawns()
2279                & !setup.board.kings();
2280            let (chess, pockets, _, mut errors) = Chess::from_setup_unchecked(setup, mode);
2281            let pockets = pockets.unwrap_or_default();
2282
2283            if pockets.white.king > 0 || pockets.black.king > 0 {
2284                errors |= PositionErrorKinds::TOO_MANY_KINGS;
2285            }
2286
2287            if pockets.count() + chess.board().occupied().count() > 64 {
2288                errors |= PositionErrorKinds::VARIANT;
2289            }
2290
2291            errors -= PositionErrorKinds::TOO_MUCH_MATERIAL;
2292
2293            if promoted.count()
2294                + chess.board().pawns().count()
2295                + usize::from(pockets.white.pawn)
2296                + usize::from(pockets.black.pawn)
2297                > 16
2298                || (chess.board().knights() & !promoted).count()
2299                    + usize::from(pockets.white.knight)
2300                    + usize::from(pockets.black.knight)
2301                    > 4
2302                || (chess.board().bishops() & !promoted).count()
2303                    + usize::from(pockets.white.bishop)
2304                    + usize::from(pockets.black.bishop)
2305                    > 4
2306                || (chess.board().rooks() & !promoted).count()
2307                    + usize::from(pockets.white.rook)
2308                    + usize::from(pockets.black.rook)
2309                    > 4
2310                || (chess.board().queens() & !promoted).count()
2311                    + usize::from(pockets.white.queen)
2312                    + usize::from(pockets.black.queen)
2313                    > 2
2314            {
2315                errors |= PositionErrorKinds::TOO_MUCH_MATERIAL;
2316            }
2317
2318            PositionError {
2319                errors,
2320                pos: Crazyhouse {
2321                    chess,
2322                    promoted,
2323                    pockets,
2324                },
2325            }
2326            .strict()
2327        }
2328    }
2329
2330    #[cfg(feature = "arbitrary")]
2331    impl arbitrary::Arbitrary<'_> for Crazyhouse {
2332        fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
2333            arbitrary_position(u.arbitrary_iter()?)
2334        }
2335
2336        fn arbitrary_take_rest(u: arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
2337            arbitrary_position(u.arbitrary_take_rest_iter()?)
2338        }
2339    }
2340
2341    impl Position for Crazyhouse {
2342        fn board(&self) -> &Board {
2343            self.chess.board()
2344        }
2345
2346        fn promoted(&self) -> Bitboard {
2347            self.promoted
2348        }
2349
2350        fn pockets(&self) -> Option<&ByColor<ByRole<u8>>> {
2351            Some(&self.pockets)
2352        }
2353
2354        fn turn(&self) -> Color {
2355            self.chess.turn()
2356        }
2357
2358        fn castles(&self) -> &Castles {
2359            self.chess.castles()
2360        }
2361
2362        fn maybe_ep_square(&self) -> Option<Square> {
2363            self.chess.maybe_ep_square()
2364        }
2365
2366        fn remaining_checks(&self) -> Option<&ByColor<RemainingChecks>> {
2367            None
2368        }
2369
2370        fn halfmoves(&self) -> u32 {
2371            self.chess.halfmoves()
2372        }
2373
2374        fn fullmoves(&self) -> NonZeroU32 {
2375            self.chess.fullmoves()
2376        }
2377
2378        fn play_unchecked(&mut self, m: Move) {
2379            match m {
2380                Move::Normal {
2381                    capture: Some(capture),
2382                    to,
2383                    ..
2384                } => {
2385                    let capture = if self.promoted.contains(to) {
2386                        Role::Pawn
2387                    } else {
2388                        capture
2389                    };
2390
2391                    *self.our_pocket_mut().get_mut(capture) += 1;
2392                }
2393                Move::EnPassant { .. } => {
2394                    self.our_pocket_mut().pawn += 1;
2395                }
2396                Move::Put { role, .. } => {
2397                    *self.our_pocket_mut().get_mut(role) -= 1;
2398                }
2399                _ => {}
2400            }
2401
2402            do_move(
2403                &mut self.chess.board,
2404                &mut self.promoted,
2405                &mut self.chess.turn,
2406                &mut self.chess.castles,
2407                &mut self.chess.ep_square,
2408                &mut self.chess.halfmoves,
2409                &mut self.chess.fullmoves,
2410                m,
2411            );
2412        }
2413
2414        fn legal_moves(&self) -> MoveList {
2415            let mut moves = self.chess.legal_moves();
2416
2417            let pocket = self.our_pocket();
2418            let targets = self.legal_put_squares();
2419
2420            for to in targets {
2421                for role in [Role::Knight, Role::Bishop, Role::Rook, Role::Queen] {
2422                    if *pocket.get(role) > 0 {
2423                        moves.push(Move::Put { role, to });
2424                    }
2425                }
2426            }
2427
2428            if pocket.pawn > 0 {
2429                for to in targets & !Bitboard::BACKRANKS {
2430                    moves.push(Move::Put {
2431                        role: Role::Pawn,
2432                        to,
2433                    });
2434                }
2435            }
2436
2437            moves
2438        }
2439
2440        fn castling_moves(&self, side: CastlingSide) -> MoveList {
2441            self.chess.castling_moves(side)
2442        }
2443
2444        fn en_passant_moves(&self) -> MoveList {
2445            self.chess.en_passant_moves()
2446        }
2447
2448        fn san_candidates(&self, role: Role, to: Square) -> MoveList {
2449            let mut moves = self.chess.san_candidates(role, to);
2450
2451            if *self.our_pocket().get(role) > 0
2452                && self.legal_put_squares().contains(to)
2453                && (role != Role::Pawn || !Bitboard::BACKRANKS.contains(to))
2454            {
2455                moves.push(Move::Put { role, to });
2456            }
2457
2458            moves
2459        }
2460
2461        fn is_irreversible(&self, m: Move) -> bool {
2462            match m {
2463                Move::Castle { .. } => true,
2464                Move::Normal { role, from, to, .. } => {
2465                    self.chess.castles.castling_rights().contains(from)
2466                        || self.chess.castles.castling_rights().contains(to)
2467                        || (role == Role::King && self.chess.castles.has_color(self.turn()))
2468                }
2469                _ => false,
2470            }
2471        }
2472
2473        fn has_insufficient_material(&self, _color: Color) -> bool {
2474            // In practise no material can leave the game, but this is simple
2475            // to implement anyway. Bishops can be captured and put onto a
2476            // different color complex.
2477            self.board().occupied().count() + self.pockets.count() <= 3
2478                && self.promoted.is_empty()
2479                && self.board().pawns().is_empty()
2480                && self.board().rooks_and_queens().is_empty()
2481                && self.pockets.white.pawn == 0
2482                && self.pockets.black.pawn == 0
2483                && self.pockets.white.rook == 0
2484                && self.pockets.black.rook == 0
2485                && self.pockets.white.queen == 0
2486                && self.pockets.black.queen == 0
2487        }
2488
2489        fn is_variant_end(&self) -> bool {
2490            false
2491        }
2492        fn variant_outcome(&self) -> Outcome {
2493            Outcome::Unknown
2494        }
2495
2496        fn update_zobrist_hash<V: ZobristValue>(
2497            &self,
2498            mut current: V,
2499            m: Move,
2500            _mode: EnPassantMode,
2501        ) -> Option<V> {
2502            if self.chess.ep_square.is_some() {
2503                return None;
2504            }
2505
2506            match m {
2507                Move::Normal {
2508                    role,
2509                    from,
2510                    capture,
2511                    to,
2512                    promotion,
2513                } if (role != Role::Pawn || Square::abs_diff(from, to) != 16)
2514                    && role != Role::King
2515                    && !self.castles().castling_rights().contains(from)
2516                    && !self.castles().castling_rights().contains(to) =>
2517                {
2518                    current ^= V::zobrist_for_white_turn();
2519                    current ^= V::zobrist_for_piece(from, role.of(self.turn()));
2520                    current ^= V::zobrist_for_piece(to, promotion.unwrap_or(role).of(self.turn()));
2521                    if let Some(capture) = capture {
2522                        current ^= V::zobrist_for_piece(to, capture.of(!self.turn()));
2523                        let capture = if self.promoted.contains(to) {
2524                            current ^= V::zobrist_for_promoted(to);
2525                            Role::Pawn
2526                        } else {
2527                            capture
2528                        };
2529                        let pocket_pieces = self.pockets[self.turn()][capture];
2530                        current ^= V::zobrist_for_pocket(self.turn(), capture, pocket_pieces);
2531                        current ^= V::zobrist_for_pocket(self.turn(), capture, pocket_pieces + 1);
2532                    }
2533                    if self.promoted.contains(from) {
2534                        current ^= V::zobrist_for_promoted(from);
2535                    }
2536                    if self.promoted.contains(from) || promotion.is_some() {
2537                        current ^= V::zobrist_for_promoted(to);
2538                    }
2539                    Some(current)
2540                }
2541                Move::Put { role, to } => {
2542                    current ^= V::zobrist_for_white_turn();
2543                    current ^= V::zobrist_for_piece(to, role.of(self.turn()));
2544                    let pocket_pieces = self.pockets[self.turn()][role];
2545                    current ^= V::zobrist_for_pocket(self.turn(), role, pocket_pieces);
2546                    current ^= V::zobrist_for_pocket(self.turn(), role, pocket_pieces - 1);
2547                    Some(current)
2548                }
2549                _ => None,
2550            }
2551        }
2552    }
2553
2554    /// A Racing Kings position.
2555    #[derive(Clone, Debug)]
2556    pub struct RacingKings {
2557        board: Board,
2558        turn: Color,
2559        castles: Castles,
2560        halfmoves: u32,
2561        fullmoves: NonZeroU32,
2562    }
2563
2564    impl RacingKings {
2565        pub const fn new() -> RacingKings {
2566            RacingKings {
2567                board: Board::racing_kings(),
2568                turn: White,
2569                castles: Castles::empty(CastlingMode::Standard),
2570                halfmoves: 0,
2571                fullmoves: NonZeroU32::MIN,
2572            }
2573        }
2574    }
2575
2576    impl Default for RacingKings {
2577        fn default() -> RacingKings {
2578            RacingKings::new()
2579        }
2580    }
2581
2582    impl Hash for RacingKings {
2583        fn hash<H: Hasher>(&self, state: &mut H) {
2584            self.board.hash(state);
2585            self.turn.hash(state);
2586            self.castles.castling_rights().hash(state);
2587        }
2588    }
2589
2590    impl PartialEq for RacingKings {
2591        fn eq(&self, other: &Self) -> bool {
2592            self.board == other.board
2593                && self.turn == other.turn
2594                && self.castles.castling_rights() == other.castles.castling_rights()
2595        }
2596    }
2597
2598    impl Eq for RacingKings {}
2599
2600    impl FromSetup for RacingKings {
2601        fn from_setup(
2602            setup: Setup,
2603            mode: CastlingMode,
2604        ) -> Result<RacingKings, PositionError<RacingKings>> {
2605            let mut errors = PositionErrorKinds::empty();
2606
2607            if setup.castling_rights.any() {
2608                errors |= PositionErrorKinds::INVALID_CASTLING_RIGHTS;
2609            }
2610
2611            if setup.board.pawns().any() {
2612                errors |= PositionErrorKinds::VARIANT;
2613            }
2614
2615            for color in Color::ALL {
2616                let us = setup.board.by_color(color);
2617                if (setup.board.knights() & us).count() > 2
2618                    || (setup.board.bishops() & us).count() > 2
2619                    || (setup.board.rooks() & us).count() > 2
2620                    || (setup.board.queens() & us).more_than_one()
2621                {
2622                    errors |= PositionErrorKinds::TOO_MUCH_MATERIAL;
2623                }
2624            }
2625
2626            if setup.ep_square.is_some() {
2627                errors |= PositionErrorKinds::INVALID_EP_SQUARE;
2628            }
2629
2630            let pos = RacingKings {
2631                board: setup.board,
2632                turn: setup.turn,
2633                castles: Castles::empty(mode),
2634                halfmoves: setup.halfmoves,
2635                fullmoves: setup.fullmoves,
2636            };
2637
2638            if pos.is_check() {
2639                errors |= PositionErrorKinds::IMPOSSIBLE_CHECK;
2640            }
2641
2642            if pos.turn().is_black()
2643                && (pos.board().white() & pos.board().kings() & Rank::Eighth).any()
2644                && (pos.board().black() & pos.board().kings() & Rank::Eighth).any()
2645            {
2646                errors |= PositionErrorKinds::VARIANT;
2647            }
2648
2649            errors |= validate(&pos, None);
2650
2651            PositionError { pos, errors }.strict()
2652        }
2653    }
2654
2655    #[cfg(feature = "arbitrary")]
2656    impl arbitrary::Arbitrary<'_> for RacingKings {
2657        fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
2658            arbitrary_position(u.arbitrary_iter()?)
2659        }
2660
2661        fn arbitrary_take_rest(u: arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
2662            arbitrary_position(u.arbitrary_take_rest_iter()?)
2663        }
2664    }
2665
2666    impl Position for RacingKings {
2667        fn board(&self) -> &Board {
2668            &self.board
2669        }
2670
2671        fn promoted(&self) -> Bitboard {
2672            Bitboard::EMPTY
2673        }
2674
2675        fn pockets(&self) -> Option<&ByColor<ByRole<u8>>> {
2676            None
2677        }
2678
2679        fn turn(&self) -> Color {
2680            self.turn
2681        }
2682
2683        fn castles(&self) -> &Castles {
2684            &self.castles
2685        }
2686
2687        fn maybe_ep_square(&self) -> Option<Square> {
2688            None
2689        }
2690
2691        fn remaining_checks(&self) -> Option<&ByColor<RemainingChecks>> {
2692            None
2693        }
2694
2695        fn halfmoves(&self) -> u32 {
2696            self.halfmoves
2697        }
2698
2699        fn fullmoves(&self) -> NonZeroU32 {
2700            self.fullmoves
2701        }
2702
2703        fn play_unchecked(&mut self, m: Move) {
2704            do_move(
2705                &mut self.board,
2706                &mut Bitboard(0),
2707                &mut self.turn,
2708                &mut self.castles,
2709                &mut None,
2710                &mut self.halfmoves,
2711                &mut self.fullmoves,
2712                m,
2713            );
2714        }
2715
2716        fn legal_moves(&self) -> MoveList {
2717            let mut moves = MoveList::new();
2718
2719            if self.is_variant_end() {
2720                return moves;
2721            }
2722
2723            // Generate all legal moves (no castling, no ep).
2724            let target = !self.us();
2725            gen_non_king(self, target, &mut moves);
2726            let king = self
2727                .board()
2728                .king_of(self.turn())
2729                .expect("king in racingkings");
2730            gen_safe_king(self, king, target, &mut moves);
2731
2732            let blockers = slider_blockers(self.board(), self.them(), king);
2733            if blockers.any() {
2734                moves.retain(|m| is_safe(self, king, *m, blockers));
2735            }
2736
2737            // Do not allow giving check. This could be implemented more
2738            // efficiently.
2739            moves.retain(|m| {
2740                let mut after = self.clone();
2741                after.play_unchecked(*m);
2742                !after.is_check()
2743            });
2744
2745            moves
2746        }
2747
2748        fn has_insufficient_material(&self, _color: Color) -> bool {
2749            // Even a lone king can win the race.
2750            false
2751        }
2752
2753        fn is_variant_end(&self) -> bool {
2754            let in_goal = self.board().kings() & Rank::Eighth;
2755            if in_goal.is_empty() {
2756                return false;
2757            }
2758
2759            if self.turn().is_white() || (in_goal & self.board().black()).any() {
2760                return true;
2761            }
2762
2763            // White has reached the backrank. Check if black can catch up.
2764            let black_king = self.board().king_of(Black).expect("king in racingkings");
2765            for target in attacks::king_attacks(black_king) & Rank::Eighth & !self.board().black() {
2766                if self
2767                    .king_attackers(target, White, self.board().occupied())
2768                    .is_empty()
2769                {
2770                    return false;
2771                }
2772            }
2773
2774            true
2775        }
2776
2777        fn variant_outcome(&self) -> Outcome {
2778            if self.is_variant_end() {
2779                let in_goal = self.board().kings() & Rank::Eighth;
2780                if (in_goal & self.board().white()).any() && (in_goal & self.board().black()).any()
2781                {
2782                    Outcome::Known(KnownOutcome::Draw)
2783                } else if (in_goal & self.board().white()).any() {
2784                    Outcome::Known(KnownOutcome::Decisive { winner: White })
2785                } else {
2786                    Outcome::Known(KnownOutcome::Decisive { winner: Black })
2787                }
2788            } else {
2789                Outcome::Unknown
2790            }
2791        }
2792
2793        fn update_zobrist_hash<V: ZobristValue>(
2794            &self,
2795            mut current: V,
2796            m: Move,
2797            _mode: EnPassantMode,
2798        ) -> Option<V> {
2799            match m {
2800                Move::Normal {
2801                    role,
2802                    from,
2803                    capture,
2804                    to,
2805                    promotion,
2806                } => {
2807                    current ^= V::zobrist_for_white_turn();
2808                    current ^= V::zobrist_for_piece(from, role.of(self.turn));
2809                    current ^= V::zobrist_for_piece(to, promotion.unwrap_or(role).of(self.turn));
2810                    if let Some(capture) = capture {
2811                        current ^= V::zobrist_for_piece(to, capture.of(!self.turn));
2812                    }
2813                    Some(current)
2814                }
2815                _ => None,
2816            }
2817        }
2818    }
2819
2820    /// A Horde position.
2821    #[derive(Clone, Debug)]
2822    pub struct Horde {
2823        board: Board,
2824        turn: Color,
2825        castles: Castles,
2826        ep_square: Option<EnPassant>,
2827        halfmoves: u32,
2828        fullmoves: NonZeroU32,
2829    }
2830
2831    impl Default for Horde {
2832        fn default() -> Horde {
2833            let mut castles = Castles::default();
2834            castles.discard_color(White);
2835
2836            Horde {
2837                board: Board::horde(),
2838                turn: White,
2839                castles,
2840                ep_square: None,
2841                halfmoves: 0,
2842                fullmoves: NonZeroU32::MIN,
2843            }
2844        }
2845    }
2846
2847    impl Hash for Horde {
2848        fn hash<H: Hasher>(&self, state: &mut H) {
2849            self.board.hash(state);
2850            self.turn.hash(state);
2851            self.castles.castling_rights().hash(state);
2852        }
2853    }
2854
2855    impl PartialEq for Horde {
2856        fn eq(&self, other: &Self) -> bool {
2857            self.board == other.board
2858                && self.turn == other.turn
2859                && self.castles.castling_rights() == other.castles.castling_rights()
2860                && self.legal_ep_square() == other.legal_ep_square()
2861        }
2862    }
2863
2864    impl Eq for Horde {}
2865
2866    impl FromSetup for Horde {
2867        fn from_setup(setup: Setup, mode: CastlingMode) -> Result<Horde, PositionError<Horde>> {
2868            let mut errors = PositionErrorKinds::empty();
2869
2870            let castles = match Castles::from_setup(&setup, mode) {
2871                Ok(castles) => castles,
2872                Err(castles) => {
2873                    errors |= PositionErrorKinds::INVALID_CASTLING_RIGHTS;
2874                    castles
2875                }
2876            };
2877
2878            let ep_square = match EnPassant::from_setup(&setup) {
2879                Ok(ep_square) => ep_square,
2880                Err(()) => {
2881                    errors |= PositionErrorKinds::INVALID_EP_SQUARE;
2882                    None
2883                }
2884            };
2885
2886            let pos = Horde {
2887                board: setup.board,
2888                turn: setup.turn,
2889                castles,
2890                ep_square,
2891                halfmoves: setup.halfmoves,
2892                fullmoves: setup.fullmoves,
2893            };
2894
2895            errors |= validate(&pos, ep_square)
2896                - PositionErrorKinds::PAWNS_ON_BACKRANK
2897                - PositionErrorKinds::MISSING_KING
2898                - PositionErrorKinds::TOO_MUCH_MATERIAL;
2899
2900            if pos.board().kings().is_empty() {
2901                errors |= PositionErrorKinds::MISSING_KING;
2902            } else if pos.board().kings().more_than_one() {
2903                errors |= PositionErrorKinds::TOO_MANY_KINGS;
2904            }
2905
2906            for color in Color::ALL {
2907                let us = pos.board.by_color(color);
2908                if (pos.board().kings() & us).any() {
2909                    if !is_standard_material(pos.board(), color) {
2910                        errors |= PositionErrorKinds::TOO_MUCH_MATERIAL;
2911                    }
2912                    if (pos.board().pawns() & us & Bitboard::BACKRANKS).any() {
2913                        errors |= PositionErrorKinds::PAWNS_ON_BACKRANK;
2914                    }
2915                } else {
2916                    if us.count() > 36 {
2917                        errors |= PositionErrorKinds::TOO_MUCH_MATERIAL;
2918                    }
2919                    if (pos.board().pawns() & us & (!color).backrank()).any() {
2920                        errors |= PositionErrorKinds::PAWNS_ON_BACKRANK;
2921                    }
2922                }
2923            }
2924
2925            PositionError { pos, errors }.strict()
2926        }
2927    }
2928
2929    #[cfg(feature = "arbitrary")]
2930    impl arbitrary::Arbitrary<'_> for Horde {
2931        fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
2932            arbitrary_position(u.arbitrary_iter()?)
2933        }
2934
2935        fn arbitrary_take_rest(u: arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
2936            arbitrary_position(u.arbitrary_take_rest_iter()?)
2937        }
2938    }
2939
2940    impl Position for Horde {
2941        fn board(&self) -> &Board {
2942            &self.board
2943        }
2944
2945        fn promoted(&self) -> Bitboard {
2946            Bitboard::EMPTY
2947        }
2948
2949        fn pockets(&self) -> Option<&ByColor<ByRole<u8>>> {
2950            None
2951        }
2952
2953        fn turn(&self) -> Color {
2954            self.turn
2955        }
2956
2957        fn castles(&self) -> &Castles {
2958            &self.castles
2959        }
2960
2961        fn maybe_ep_square(&self) -> Option<Square> {
2962            self.ep_square.map(EnPassant::square)
2963        }
2964
2965        fn remaining_checks(&self) -> Option<&ByColor<RemainingChecks>> {
2966            None
2967        }
2968
2969        fn halfmoves(&self) -> u32 {
2970            self.halfmoves
2971        }
2972
2973        fn fullmoves(&self) -> NonZeroU32 {
2974            self.fullmoves
2975        }
2976
2977        fn play_unchecked(&mut self, m: Move) {
2978            do_move(
2979                &mut self.board,
2980                &mut Bitboard(0),
2981                &mut self.turn,
2982                &mut self.castles,
2983                &mut self.ep_square,
2984                &mut self.halfmoves,
2985                &mut self.fullmoves,
2986                m,
2987            );
2988        }
2989
2990        fn legal_moves(&self) -> MoveList {
2991            let mut moves = MoveList::new();
2992
2993            let king = self.board().king_of(self.turn());
2994            let has_ep = gen_en_passant(self.board(), self.turn(), self.ep_square, &mut moves);
2995
2996            let checkers = self.checkers();
2997            if checkers.is_empty() {
2998                let target = !self.us();
2999                gen_non_king(self, target, &mut moves);
3000                if let Some(king) = king {
3001                    gen_safe_king(self, king, target, &mut moves);
3002                    gen_castling_moves(
3003                        self,
3004                        &self.castles,
3005                        king,
3006                        CastlingSide::KingSide,
3007                        &mut moves,
3008                    );
3009                    gen_castling_moves(
3010                        self,
3011                        &self.castles,
3012                        king,
3013                        CastlingSide::QueenSide,
3014                        &mut moves,
3015                    );
3016                }
3017            } else {
3018                evasions(self, king.expect("king in check"), checkers, &mut moves);
3019            }
3020
3021            if let Some(king) = king {
3022                let blockers = slider_blockers(self.board(), self.them(), king);
3023                if blockers.any() || has_ep {
3024                    moves.retain(|m| is_safe(self, king, *m, blockers));
3025                }
3026            }
3027
3028            moves
3029        }
3030
3031        fn is_variant_end(&self) -> bool {
3032            self.board().white().is_empty() || self.board().black().is_empty()
3033        }
3034
3035        #[allow(clippy::nonminimal_bool)] // Aids commentary
3036        fn has_insufficient_material(&self, color: Color) -> bool {
3037            #[derive(Copy, Clone)]
3038            enum SquareColor {
3039                Dark,
3040                Light,
3041            }
3042            impl From<SquareColor> for Bitboard {
3043                fn from(square_color: SquareColor) -> Bitboard {
3044                    match square_color {
3045                        SquareColor::Light => Bitboard::LIGHT_SQUARES,
3046                        SquareColor::Dark => Bitboard::DARK_SQUARES,
3047                    }
3048                }
3049            }
3050            impl Not for SquareColor {
3051                type Output = SquareColor;
3052
3053                fn not(self) -> SquareColor {
3054                    match self {
3055                        SquareColor::Dark => SquareColor::Light,
3056                        SquareColor::Light => SquareColor::Dark,
3057                    }
3058                }
3059            }
3060
3061            // The side with the king can always win by capturing the horde.
3062            if (self.board.by_color(color) & self.board.kings()).any() {
3063                return false;
3064            }
3065
3066            let has_bishop_pair = |side: Color| -> bool {
3067                let bishops = self.board.bishops() & self.board.by_color(side);
3068                (bishops & Bitboard::DARK_SQUARES).any()
3069                    && (bishops & Bitboard::LIGHT_SQUARES).any()
3070            };
3071
3072            // By this point: color is the horde.
3073            let horde = self.board.material_side(color);
3074            let horde_bishops = |square_color: SquareColor| -> u8 {
3075                (Bitboard::from(square_color) & self.board.by_color(color) & self.board.bishops())
3076                    .count() as u8
3077            };
3078            let horde_bishop_color = if horde_bishops(SquareColor::Light) >= 1 {
3079                SquareColor::Light
3080            } else {
3081                SquareColor::Dark
3082            };
3083            // Two same color bishops suffice to cover all the light and dark squares
3084            // around the enemy king.
3085            let horde_num = horde.pawn
3086                + horde.knight
3087                + horde.rook
3088                + horde.queen
3089                + min(horde_bishops(SquareColor::Dark), 2)
3090                + min(horde_bishops(SquareColor::Light), 2);
3091
3092            let pieces = self.board.material_side(!color);
3093            let pieces_bishops = |square_color: SquareColor| -> u8 {
3094                (Bitboard::from(square_color) & self.board.by_color(!color) & self.board.bishops())
3095                    .count() as u8
3096            };
3097            let pieces_num = pieces.count() as u8;
3098            let pieces_of_type_not = |piece: u8| -> u8 { pieces_num - piece };
3099
3100            if horde_num == 0 {
3101                return true;
3102            }
3103            if horde_num >= 4 {
3104                // Four or more white pieces can always deliver mate.
3105                return false;
3106            }
3107            if (horde.pawn >= 1 || horde.queen >= 1) && horde_num >= 2 {
3108                // Pawns/queens are never insufficient material when paired with any other
3109                // piece (a pawn promotes to a queen and delivers mate).
3110                return false;
3111            }
3112            if horde.rook >= 1 && horde_num >= 2 {
3113                // A rook is insufficient material only when it is paired with a bishop
3114                // against a lone king. The horde can mate in any other case.
3115                // A rook on A1 and a bishop on C3 mate a king on B1 when there is a
3116                // friendly pawn/opposite-color-bishop/rook/queen on C2.
3117                // A rook on B8 and a bishop C3 mate a king on A1 when there is a friendly
3118                // knight on A2.
3119                if !(horde_num == 2
3120                    && horde.rook == 1
3121                    && horde.bishop == 1
3122                    && pieces_of_type_not(pieces_bishops(horde_bishop_color)) == 1)
3123                {
3124                    return false;
3125                }
3126            }
3127
3128            if horde_num == 1 {
3129                if pieces_num == 1 {
3130                    // A lone piece cannot mate a lone king.
3131                    return true;
3132                } else if horde.queen == 1 {
3133                    // The horde has a lone queen.
3134                    // A lone queen mates a king on A1 bounded by:
3135                    //  -- a pawn/rook on A2
3136                    //  -- two same color bishops on A2, B1
3137                    // We ignore every other mating case, since it can be reduced to
3138                    // the two previous cases (e.g. a black pawn on A2 and a black
3139                    // bishop on B1).
3140                    return !(pieces.pawn >= 1
3141                        || pieces.rook >= 1
3142                        || pieces_bishops(SquareColor::Light) >= 2
3143                        || pieces_bishops(SquareColor::Dark) >= 2);
3144                } else if horde.pawn == 1 {
3145                    // Promote the pawn to a queen or a knight and check whether white
3146                    // can mate.
3147                    let pawn_square = (self.board.pawns() & self.board.by_color(color))
3148                        .single_square()
3149                        .unwrap();
3150                    let mut promote_to_queen = self.clone();
3151                    promote_to_queen
3152                        .board
3153                        .set_piece_at(pawn_square, color.queen());
3154                    let mut promote_to_knight = self.clone();
3155                    promote_to_knight
3156                        .board
3157                        .set_piece_at(pawn_square, color.knight());
3158                    return promote_to_queen.has_insufficient_material(color)
3159                        && promote_to_knight.has_insufficient_material(color);
3160                } else if horde.rook == 1 {
3161                    // A lone rook mates a king on A8 bounded by a pawn/rook on A7 and a
3162                    // pawn/knight on B7. We ignore every other case, since it can be
3163                    // reduced to the two previous cases.
3164                    // (e.g. three pawns on A7, B7, C7)
3165                    return !(pieces.pawn >= 2
3166                        || (pieces.rook >= 1 && pieces.pawn >= 1)
3167                        || (pieces.rook >= 1 && pieces.knight >= 1)
3168                        || (pieces.pawn >= 1 && pieces.knight >= 1));
3169                } else if horde.bishop == 1 {
3170                    // The horde has a lone bishop.
3171                    return !(
3172                        // The king can be mated on A1 if there is a pawn/opposite-color-bishop
3173                        // on A2 and an opposite-color-bishop on B1.
3174                        // If black has two or more pawns, white gets the benefit of the doubt;
3175                        // there is an outside chance that white promotes its pawns to
3176                        // opposite-color-bishops and selfmates theirself.
3177                        // Every other case that the king is mated by the bishop requires that
3178                        // black has two pawns or two opposite-color-bishop or a pawn and an
3179                        // opposite-color-bishop.
3180                        // For example a king on A3 can be mated if there is
3181                        // a pawn/opposite-color-bishop on A4, a pawn/opposite-color-bishop on
3182                        // B3, a pawn/bishop/rook/queen on A2 and any other piece on B2.
3183                        pieces_bishops(!horde_bishop_color) >= 2
3184                            || (pieces_bishops(!horde_bishop_color) >= 1 && pieces.pawn >= 1)
3185                            || pieces.pawn >= 2
3186                    );
3187                } else if horde.knight == 1 {
3188                    // The horde has a lone knight.
3189                    return !(
3190                        // The king on A1 can be smother mated by a knight on C2 if there is
3191                        // a pawn/knight/bishop on B2, a knight/rook on B1 and any other piece
3192                        // on A2.
3193                        // Moreover, when black has four or more pieces and two of them are
3194                        // pawns, black can promote their pawns and selfmate theirself.
3195                        pieces_num >= 4
3196                            && (pieces.knight >= 2
3197                                || pieces.pawn >= 2
3198                                || (pieces.rook >= 1 && pieces.knight >= 1)
3199                                || (pieces.rook >= 1 && pieces.bishop >= 1)
3200                                || (pieces.knight >= 1 && pieces.bishop >= 1)
3201                                || (pieces.rook >= 1 && pieces.pawn >= 1)
3202                                || (pieces.knight >= 1 && pieces.pawn >= 1)
3203                                || (pieces.bishop >= 1 && pieces.pawn >= 1)
3204                                || (has_bishop_pair(!color) && pieces.pawn >= 1))
3205                            && (pieces_bishops(SquareColor::Dark) < 2
3206                                || pieces_of_type_not(pieces_bishops(SquareColor::Dark)) >= 3)
3207                            && (pieces_bishops(SquareColor::Light) < 2
3208                                || pieces_of_type_not(pieces_bishops(SquareColor::Light)) >= 3)
3209                    );
3210                }
3211
3212            // By this point, we only need to deal with white's minor pieces.
3213            } else if horde_num == 2 {
3214                if pieces_num == 1 {
3215                    // Two minor pieces cannot mate a lone king.
3216                    return true;
3217                } else if horde.knight == 2 {
3218                    // A king on A1 is mated by two knights, if it is obstructed by a
3219                    // pawn/bishop/knight on B2. On the other hand, if black only has
3220                    // major pieces it is a draw.
3221                    return pieces.pawn + pieces.bishop + pieces.knight < 1;
3222                } else if has_bishop_pair(color) {
3223                    return !(
3224                        // A king on A1 obstructed by a pawn/bishop on A2 is mated
3225                        // by the bishop pair.
3226                        pieces.pawn >= 1 || pieces.bishop >= 1 ||
3227                            // A pawn/bishop/knight on B4, a pawn/bishop/rook/queen on
3228                            // A4 and the king on A3 enable Boden's mate by the bishop
3229                            // pair. In every other case white cannot win.
3230                            ( pieces.knight >= 1 && pieces.rook + pieces.queen >= 1 )
3231                    );
3232                } else if horde.bishop >= 1 && horde.knight >= 1 {
3233                    // The horde has a bishop and a knight.
3234                    return !(
3235                        // A king on A1 obstructed by a pawn/opposite-color-bishop on
3236                        // A2 is mated by a knight on D2 and a bishop on C3.
3237                        pieces.pawn >= 1 || pieces_bishops(!horde_bishop_color) >= 1 ||
3238                            // A king on A1 bounded by two friendly pieces on A2 and B1 is
3239                            // mated when the knight moves from D4 to C2 so that both the
3240                            // knight and the bishop deliver check.
3241                            pieces_of_type_not( pieces_bishops(horde_bishop_color) ) >=3
3242                    );
3243                } else {
3244                    // The horde has two or more bishops on the same color.
3245                    // White can only win if black has enough material to obstruct
3246                    // the squares of the opposite color around the king.
3247                    return !(
3248                        // A king on A1 obstructed by a pawn/opposite-bishop/knight
3249                        // on A2 and a opposite-bishop/knight on B1 is mated by two
3250                        // bishops on B2 and C3. This position is theoretically
3251                        // achievable even when black has two pawns or when they
3252                        // have a pawn and an opposite color bishop.
3253                        (pieces.pawn >= 1 && pieces_bishops(!horde_bishop_color) >= 1)
3254                            || (pieces.pawn >= 1 && pieces.knight >= 1)
3255                            || (pieces_bishops(!horde_bishop_color) >= 1 && pieces.knight >= 1)
3256                            || (pieces_bishops(!horde_bishop_color) >= 2)
3257                            || pieces.knight >= 2
3258                            || pieces.pawn >= 2
3259                        // In every other case, white can only draw.
3260                    );
3261                }
3262            } else if horde_num == 3 {
3263                // A king in the corner is mated by two knights and a bishop or three
3264                // knights or the bishop pair and a knight/bishop.
3265                if (horde.knight == 2 && horde.bishop == 1)
3266                    || horde.knight == 3
3267                    || has_bishop_pair(color)
3268                {
3269                    return false;
3270                } else {
3271                    // White has two same color bishops and a knight.
3272                    // A king on A1 is mated by a bishop on B2, a bishop on C1 and a
3273                    // knight on C3, as long as there is another black piece to waste
3274                    // a tempo.
3275                    return pieces_num == 1;
3276                }
3277            }
3278
3279            true
3280        }
3281
3282        fn variant_outcome(&self) -> Outcome {
3283            if self.board().occupied().is_empty() {
3284                Outcome::Known(KnownOutcome::Draw)
3285            } else if self.board().white().is_empty() {
3286                Outcome::Known(KnownOutcome::Decisive { winner: Black })
3287            } else if self.board().black().is_empty() {
3288                Outcome::Known(KnownOutcome::Decisive { winner: White })
3289            } else {
3290                Outcome::Unknown
3291            }
3292        }
3293
3294        fn update_zobrist_hash<V: ZobristValue>(
3295            &self,
3296            current: V,
3297            m: Move,
3298            _mode: EnPassantMode,
3299        ) -> Option<V> {
3300            do_update_zobrist_hash(current, m, self.turn, &self.castles, self.ep_square)
3301        }
3302    }
3303
3304    fn add_king_promotions(moves: &mut MoveList) {
3305        let mut king_promotions = MoveList::new();
3306
3307        for m in &moves[..] {
3308            if let Move::Normal {
3309                role,
3310                from,
3311                capture,
3312                to,
3313                promotion: Some(Role::Queen),
3314            } = *m
3315            {
3316                king_promotions.push(Move::Normal {
3317                    role,
3318                    from,
3319                    capture,
3320                    to,
3321                    promotion: Some(Role::King),
3322                });
3323            }
3324        }
3325
3326        moves.extend(king_promotions);
3327    }
3328}
3329
3330#[allow(clippy::too_many_arguments)] // But typesafe
3331fn do_move(
3332    board: &mut Board,
3333    promoted: &mut Bitboard,
3334    turn: &mut Color,
3335    castles: &mut Castles,
3336    ep_square: &mut Option<EnPassant>,
3337    halfmoves: &mut u32,
3338    fullmoves: &mut NonZeroU32,
3339    m: Move,
3340) {
3341    let color = *turn;
3342    ep_square.take();
3343
3344    *halfmoves = if m.is_zeroing() {
3345        0
3346    } else {
3347        halfmoves.saturating_add(1)
3348    };
3349
3350    match m {
3351        Move::Normal {
3352            role,
3353            from,
3354            capture,
3355            to,
3356            promotion,
3357        } => {
3358            if role == Role::Pawn && to - from == 16 && from.rank() == Rank::Second {
3359                *ep_square = from.offset(8).map(EnPassant);
3360            } else if role == Role::Pawn && from - to == 16 && from.rank() == Rank::Seventh {
3361                *ep_square = from.offset(-8).map(EnPassant);
3362            }
3363
3364            if role == Role::King {
3365                castles.discard_color(color);
3366            } else if role == Role::Rook {
3367                castles.discard_rook(from);
3368            }
3369
3370            if capture == Some(Role::Rook) {
3371                castles.discard_rook(to);
3372            }
3373
3374            board.discard_piece_at(from);
3375            board.set_piece_at(to, promotion.map_or(role.of(color), |p| p.of(color)));
3376
3377            let is_promoted = promoted.remove(from) || promotion.is_some();
3378            promoted.set(to, is_promoted);
3379        }
3380        Move::Castle { king, rook } => {
3381            let side = CastlingSide::from_queen_side(rook < king);
3382            board.discard_piece_at(king);
3383            board.discard_piece_at(rook);
3384            board.set_new_piece_at(
3385                Square::from_coords(side.rook_to_file(), rook.rank()),
3386                color.rook(),
3387            );
3388            board.set_new_piece_at(
3389                Square::from_coords(side.king_to_file(), king.rank()),
3390                color.king(),
3391            );
3392            castles.discard_color(color);
3393        }
3394        Move::EnPassant { from, to } => {
3395            board.discard_piece_at(Square::from_coords(to.file(), from.rank())); // captured pawn
3396            board.discard_piece_at(from);
3397            board.set_new_piece_at(to, color.pawn());
3398        }
3399        Move::Put { role, to } => {
3400            board.set_new_piece_at(to, Piece { color, role });
3401        }
3402    }
3403
3404    if color.is_black() {
3405        *fullmoves = NonZeroU32::new(fullmoves.get().saturating_add(1)).unwrap();
3406    }
3407
3408    *turn = !color;
3409}
3410
3411fn validate<P: Position>(pos: &P, ep_square: Option<EnPassant>) -> PositionErrorKinds {
3412    let mut errors = PositionErrorKinds::empty();
3413
3414    if pos.board().occupied().is_empty() {
3415        errors |= PositionErrorKinds::EMPTY_BOARD;
3416    }
3417
3418    if (pos.board().pawns() & Bitboard::BACKRANKS).any() {
3419        errors |= PositionErrorKinds::PAWNS_ON_BACKRANK;
3420    }
3421
3422    for color in Color::ALL {
3423        let kings = pos.board().kings() & pos.board().by_color(color);
3424        if kings.is_empty() {
3425            errors |= PositionErrorKinds::MISSING_KING;
3426        } else if kings.more_than_one() {
3427            errors |= PositionErrorKinds::TOO_MANY_KINGS;
3428        }
3429
3430        if !is_standard_material(pos.board(), color) {
3431            errors |= PositionErrorKinds::TOO_MUCH_MATERIAL;
3432        }
3433    }
3434
3435    if let Some(their_king) = pos.board().king_of(!pos.turn())
3436        && pos
3437            .king_attackers(their_king, pos.turn(), pos.board().occupied())
3438            .any()
3439    {
3440        errors |= PositionErrorKinds::OPPOSITE_CHECK;
3441    }
3442
3443    let checkers = pos.checkers();
3444    if let (Some(a), Some(b), Some(our_king)) = (
3445        checkers.first(),
3446        checkers.last(),
3447        pos.board().king_of(pos.turn()),
3448    ) {
3449        if let Some(ep_square) = ep_square {
3450            // The pushed pawn must be the only checker, or it has uncovered
3451            // check by a single sliding piece.
3452            if a != b
3453                || (a != ep_square.pawn_pushed_to()
3454                    && pos
3455                        .king_attackers(
3456                            our_king,
3457                            !pos.turn(),
3458                            pos.board()
3459                                .occupied()
3460                                .without(ep_square.pawn_pushed_to())
3461                                .with(ep_square.pawn_pushed_from()),
3462                        )
3463                        .any())
3464            {
3465                errors |= PositionErrorKinds::IMPOSSIBLE_CHECK;
3466            }
3467        } else {
3468            // There can be at most two checkers, and discovered checkers
3469            // cannot be aligned.
3470            if a != b && (checkers.count() > 2 || attacks::aligned(a, our_king, b)) {
3471                errors |= PositionErrorKinds::IMPOSSIBLE_CHECK;
3472            }
3473        }
3474    }
3475
3476    // Multiple steppers cannot be checkers.
3477    if (checkers & pos.board().steppers()).more_than_one() {
3478        errors |= PositionErrorKinds::IMPOSSIBLE_CHECK;
3479    }
3480
3481    errors
3482}
3483
3484fn do_update_zobrist_hash<V: ZobristValue>(
3485    mut current: V,
3486    m: Move,
3487    turn: Color,
3488    castles: &Castles,
3489    ep_square: Option<EnPassant>,
3490) -> Option<V> {
3491    if ep_square.is_some() {
3492        return None;
3493    }
3494
3495    match m {
3496        Move::Normal {
3497            role,
3498            from,
3499            capture,
3500            to,
3501            promotion,
3502        } if (role != Role::Pawn || Square::abs_diff(from, to) != 16)
3503            && role != Role::King
3504            && !castles.castling_rights().contains(from)
3505            && !castles.castling_rights().contains(to) =>
3506        {
3507            current ^= V::zobrist_for_white_turn();
3508            current ^= V::zobrist_for_piece(from, role.of(turn));
3509            current ^= V::zobrist_for_piece(to, promotion.unwrap_or(role).of(turn));
3510            if let Some(capture) = capture {
3511                current ^= V::zobrist_for_piece(to, capture.of(!turn));
3512            }
3513            Some(current)
3514        }
3515        _ => None,
3516    }
3517}
3518
3519const fn is_standard_material(board: &Board, color: Color) -> bool {
3520    let our = board.by_color(color);
3521    let promoted_pieces = board
3522        .queens()
3523        .intersect_const(our)
3524        .count()
3525        .saturating_sub(1)
3526        + board.rooks().intersect_const(our).count().saturating_sub(2)
3527        + board
3528            .knights()
3529            .intersect_const(our)
3530            .count()
3531            .saturating_sub(2)
3532        + board
3533            .bishops()
3534            .intersect_const(our)
3535            .intersect_const(Bitboard::LIGHT_SQUARES)
3536            .count()
3537            .saturating_sub(1)
3538        + board
3539            .bishops()
3540            .intersect_const(our)
3541            .intersect_const(Bitboard::DARK_SQUARES)
3542            .count()
3543            .saturating_sub(1);
3544    board.pawns().intersect_const(our).count() + promoted_pieces <= 8
3545}
3546
3547fn gen_non_king<P: Position>(pos: &P, target: Bitboard, moves: &mut MoveList) {
3548    gen_pawn_moves(pos, target, moves);
3549    KnightTag::gen_moves(pos, target, moves);
3550    BishopTag::gen_moves(pos, target, moves);
3551    RookTag::gen_moves(pos, target, moves);
3552    QueenTag::gen_moves(pos, target, moves);
3553}
3554
3555fn gen_safe_king<P: Position>(pos: &P, king: Square, target: Bitboard, moves: &mut MoveList) {
3556    (attacks::king_attacks(king) & target).for_each(|to| {
3557        if pos
3558            .board()
3559            .attacks_to(to, !pos.turn(), pos.board().occupied())
3560            .is_empty()
3561        {
3562            moves.push(Move::Normal {
3563                role: Role::King,
3564                from: king,
3565                capture: pos.board().role_at(to),
3566                to,
3567                promotion: None,
3568            });
3569        }
3570    });
3571}
3572
3573fn evasions<P: Position>(pos: &P, king: Square, checkers: Bitboard, moves: &mut MoveList) {
3574    let sliders = checkers & pos.board().sliders();
3575
3576    let mut attacked = Bitboard(0);
3577    sliders.for_each(|checker| {
3578        attacked |= attacks::ray(checker, king) ^ checker;
3579    });
3580
3581    gen_safe_king(pos, king, !pos.us() & !attacked, moves);
3582
3583    if let Some(checker) = checkers.single_square() {
3584        let target = attacks::between(king, checker).with(checker);
3585        gen_non_king(pos, target, moves);
3586    }
3587}
3588
3589fn gen_castling_moves<P: Position>(
3590    pos: &P,
3591    castles: &Castles,
3592    king: Square,
3593    side: CastlingSide,
3594    moves: &mut MoveList,
3595) {
3596    if let Some(rook) = castles.rook(pos.turn(), side) {
3597        let path = castles.path(pos.turn(), side);
3598        if (path & pos.board().occupied()).any() {
3599            return;
3600        }
3601
3602        let king_to = side.king_to(pos.turn());
3603        if pos
3604            .king_attackers(
3605                king_to,
3606                !pos.turn(),
3607                pos.board().occupied() ^ king ^ rook ^ side.rook_to(pos.turn()),
3608            )
3609            .any()
3610        {
3611            return;
3612        }
3613
3614        let king_path = attacks::between(king, king_to).with(king);
3615        for sq in king_path {
3616            if pos
3617                .king_attackers(sq, !pos.turn(), pos.board().occupied() ^ king)
3618                .any()
3619            {
3620                return;
3621            }
3622        }
3623
3624        moves.push(Move::Castle { king, rook });
3625    }
3626}
3627
3628trait Stepper {
3629    const ROLE: Role;
3630
3631    fn attacks(from: Square) -> Bitboard;
3632
3633    fn gen_moves<P: Position>(pos: &P, target: Bitboard, moves: &mut MoveList) {
3634        pos.our(Self::ROLE).for_each(|from| {
3635            (Self::attacks(from) & target).for_each(|to| {
3636                moves.push(Move::Normal {
3637                    role: Self::ROLE,
3638                    from,
3639                    capture: pos.board().role_at(to),
3640                    to,
3641                    promotion: None,
3642                });
3643            });
3644        });
3645    }
3646}
3647
3648trait Slider {
3649    const ROLE: Role;
3650
3651    fn attacks(from: Square, occupied: Bitboard) -> Bitboard;
3652
3653    fn gen_moves<P: Position>(pos: &P, target: Bitboard, moves: &mut MoveList) {
3654        pos.our(Self::ROLE).for_each(|from| {
3655            (Self::attacks(from, pos.board().occupied()) & target).for_each(|to| {
3656                moves.push(Move::Normal {
3657                    role: Self::ROLE,
3658                    from,
3659                    capture: pos.board().role_at(to),
3660                    to,
3661                    promotion: None,
3662                });
3663            });
3664        });
3665    }
3666}
3667
3668enum KnightTag {}
3669enum BishopTag {}
3670enum RookTag {}
3671enum QueenTag {}
3672
3673impl Stepper for KnightTag {
3674    const ROLE: Role = Role::Knight;
3675
3676    #[inline]
3677    fn attacks(from: Square) -> Bitboard {
3678        attacks::knight_attacks(from)
3679    }
3680}
3681
3682impl Slider for BishopTag {
3683    const ROLE: Role = Role::Bishop;
3684
3685    #[inline]
3686    fn attacks(from: Square, occupied: Bitboard) -> Bitboard {
3687        attacks::bishop_attacks(from, occupied)
3688    }
3689}
3690
3691impl Slider for RookTag {
3692    const ROLE: Role = Role::Rook;
3693
3694    #[inline]
3695    fn attacks(from: Square, occupied: Bitboard) -> Bitboard {
3696        attacks::rook_attacks(from, occupied)
3697    }
3698}
3699
3700impl Slider for QueenTag {
3701    const ROLE: Role = Role::Queen;
3702
3703    #[inline]
3704    fn attacks(from: Square, occupied: Bitboard) -> Bitboard {
3705        attacks::queen_attacks(from, occupied)
3706    }
3707}
3708
3709fn gen_pawn_moves<P: Position>(pos: &P, target: Bitboard, moves: &mut MoveList) {
3710    // Safety of unchecked offset calculations: If we shift a set of squares
3711    // by an offset, then the negated offset is valid for all resulting
3712    // squares.
3713
3714    // Generate captures.
3715    #[inline(always)]
3716    fn gen_pawn_captures<P: Position>(
3717        pos: &P,
3718        dir: Direction,
3719        target: Bitboard,
3720        moves: &mut MoveList,
3721    ) {
3722        let captures = dir.translate(pos.our(Role::Pawn)) & pos.them() & target;
3723
3724        (captures & !Bitboard::BACKRANKS).for_each(|to| {
3725            // Safety: See above.
3726            let from = unsafe { to.offset_unchecked(-dir.offset()) };
3727            moves.push(Move::Normal {
3728                role: Role::Pawn,
3729                from,
3730                capture: pos.board().role_at(to),
3731                to,
3732                promotion: None,
3733            });
3734        });
3735
3736        (captures & Bitboard::BACKRANKS).for_each(|to| {
3737            // Safety: See above.
3738            let from = unsafe { to.offset_unchecked(-dir.offset()) };
3739            push_promotions(moves, from, to, pos.board().role_at(to));
3740        });
3741    }
3742    gen_pawn_captures(
3743        pos,
3744        pos.turn()
3745            .fold_wb(Direction::NorthWest, Direction::SouthWest),
3746        target,
3747        moves,
3748    );
3749    gen_pawn_captures(
3750        pos,
3751        pos.turn()
3752            .fold_wb(Direction::NorthEast, Direction::SouthEast),
3753        target,
3754        moves,
3755    );
3756
3757    // Generate single-step advances.
3758    let single_moves =
3759        pos.our(Role::Pawn).shift(pos.turn().fold_wb(8, -8)) & !pos.board().occupied();
3760
3761    (single_moves & target & !Bitboard::BACKRANKS).for_each(|to| {
3762        // Safety: See above.
3763        let from = unsafe { to.offset_unchecked(pos.turn().fold_wb(-8, 8)) };
3764        moves.push(Move::Normal {
3765            role: Role::Pawn,
3766            from,
3767            capture: None,
3768            to,
3769            promotion: None,
3770        });
3771    });
3772
3773    (single_moves & target & Bitboard::BACKRANKS).for_each(|to| {
3774        // Safety: See above.
3775        let from = unsafe { to.offset_unchecked(pos.turn().fold_wb(-8, 8)) };
3776        push_promotions(moves, from, to, None);
3777    });
3778
3779    // Generate double-step advances.
3780    let double_moves = single_moves.shift(pos.turn().fold_wb(8, -8))
3781        & pos.turn().fold_wb(Bitboard::SOUTH, Bitboard::NORTH)
3782        & !pos.board().occupied();
3783
3784    (double_moves & target).for_each(|to| {
3785        // Safety: See above.
3786        let from = unsafe { to.offset_unchecked(pos.turn().fold_wb(-16, 16)) };
3787        moves.push(Move::Normal {
3788            role: Role::Pawn,
3789            from,
3790            capture: None,
3791            to,
3792            promotion: None,
3793        });
3794    });
3795}
3796
3797fn push_promotions(moves: &mut MoveList, from: Square, to: Square, capture: Option<Role>) {
3798    for promotion in [Role::Queen, Role::Rook, Role::Bishop, Role::Knight] {
3799        moves.push(Move::Normal {
3800            role: Role::Pawn,
3801            from,
3802            capture,
3803            to,
3804            promotion: Some(promotion),
3805        });
3806    }
3807}
3808
3809fn gen_en_passant(
3810    board: &Board,
3811    turn: Color,
3812    ep_square: Option<EnPassant>,
3813    moves: &mut MoveList,
3814) -> bool {
3815    let mut found = false;
3816
3817    if let Some(EnPassant(to)) = ep_square {
3818        (board.pawns() & board.by_color(turn) & attacks::pawn_attacks(!turn, to)).for_each(
3819            |from| {
3820                moves.push(Move::EnPassant { from, to });
3821                found = true;
3822            },
3823        );
3824    }
3825
3826    found
3827}
3828
3829fn slider_blockers(board: &Board, enemy: Bitboard, king: Square) -> Bitboard {
3830    let snipers = (attacks::rook_attacks(king, Bitboard(0)) & board.rooks_and_queens())
3831        | (attacks::bishop_attacks(king, Bitboard(0)) & board.bishops_and_queens());
3832
3833    let mut blockers = Bitboard(0);
3834    (snipers & enemy).for_each(|sniper| {
3835        let b = attacks::between(king, sniper) & board.occupied();
3836        if !b.more_than_one() {
3837            blockers.add(b);
3838        }
3839    });
3840
3841    blockers
3842}
3843
3844fn is_safe<P: Position>(pos: &P, king: Square, m: Move, blockers: Bitboard) -> bool {
3845    match m {
3846        Move::Normal { from, to, .. } => {
3847            !blockers.contains(from) || attacks::aligned(from, to, king)
3848        }
3849        Move::EnPassant { from, to } => {
3850            let capture = Square::from_coords(to.file(), from.rank());
3851            pos.board()
3852                .attacks_to(
3853                    king,
3854                    !pos.turn(),
3855                    pos.board()
3856                        .occupied()
3857                        .toggled(from)
3858                        .toggled(capture)
3859                        .with(to),
3860                )
3861                .without(capture)
3862                .is_empty()
3863        }
3864        _ => true,
3865    }
3866}
3867
3868fn filter_san_candidates(role: Role, to: Square, moves: &mut MoveList) {
3869    moves.retain(|m| match *m {
3870        Move::Normal { role: r, to: t, .. } | Move::Put { role: r, to: t } => to == t && role == r,
3871        Move::EnPassant { to: t, .. } => role == Role::Pawn && t == to,
3872        Move::Castle { .. } => false,
3873    });
3874}
3875
3876#[cfg(test)]
3877mod tests {
3878    use super::*;
3879    use crate::fen::{Epd, Fen};
3880
3881    #[cfg(feature = "alloc")]
3882    struct _AssertObjectSafe(alloc::boxed::Box<dyn Position>);
3883
3884    fn setup_fen<T: Position + FromSetup>(fen: &str) -> T {
3885        fen.parse::<Fen>()
3886            .expect("valid fen")
3887            .into_position::<T>(CastlingMode::Chess960)
3888            .expect("legal position")
3889    }
3890
3891    #[test]
3892    fn test_most_known_legals() {
3893        let pos: Chess = setup_fen("R6R/3Q4/1Q4Q1/4Q3/2Q4Q/Q4Q2/pp1Q4/kBNN1KB1 w - - 0 1");
3894        assert_eq!(pos.legal_moves().len(), 218);
3895    }
3896
3897    #[test]
3898    fn test_pinned_san_candidate() {
3899        let pos: Chess = setup_fen("R2r2k1/6pp/1Np2p2/1p2pP2/4p3/4K3/3r2PP/8 b - - 5 37");
3900
3901        let moves = pos.san_candidates(Role::Rook, Square::D3);
3902
3903        assert_eq!(
3904            moves[0],
3905            Move::Normal {
3906                role: Role::Rook,
3907                from: Square::D2,
3908                capture: None,
3909                to: Square::D3,
3910                promotion: None,
3911            }
3912        );
3913
3914        assert_eq!(moves.len(), 1);
3915    }
3916
3917    #[test]
3918    fn test_promotion() {
3919        let pos: Chess = setup_fen("3r3K/6PP/8/8/8/2k5/8/8 w - - 0 1");
3920
3921        let moves = pos.legal_moves();
3922        assert!(moves.iter().all(|m| m.role() == Role::Pawn));
3923        assert!(moves.iter().all(|m| m.is_promotion()));
3924    }
3925
3926    fn assert_insufficient_material<P>(fen: &str, white: bool, black: bool)
3927    where
3928        P: Position + FromSetup,
3929    {
3930        let pos: P = setup_fen(fen);
3931
3932        assert_eq!(
3933            pos.has_insufficient_material(White),
3934            white,
3935            "expected white {}",
3936            if white { "cannot win" } else { "can win " }
3937        );
3938        assert_eq!(
3939            pos.has_insufficient_material(Black),
3940            black,
3941            "expected black {}",
3942            if black { "cannot win" } else { "can win" }
3943        );
3944    }
3945
3946    #[test]
3947    fn test_insufficient_material() {
3948        assert_insufficient_material::<Chess>("8/5k2/8/8/8/8/3K4/8 w - - 0 1", true, true);
3949        assert_insufficient_material::<Chess>("8/3k4/8/8/2N5/8/3K4/8 b - - 0 1", true, true);
3950        assert_insufficient_material::<Chess>("8/4rk2/8/8/8/8/3K4/8 w - - 0 1", true, false);
3951        assert_insufficient_material::<Chess>("8/4qk2/8/8/8/8/3K4/8 w - - 0 1", true, false);
3952        assert_insufficient_material::<Chess>("8/4bk2/8/8/8/8/3KB3/8 w - - 0 1", false, false);
3953        assert_insufficient_material::<Chess>("8/8/3Q4/2bK4/B7/8/1k6/8 w - - 1 68", false, false);
3954        assert_insufficient_material::<Chess>("8/5k2/8/8/8/4B3/3K1B2/8 w - - 0 1", true, true);
3955        assert_insufficient_material::<Chess>("5K2/8/8/1B6/8/k7/6b1/8 w - - 0 39", true, true);
3956        assert_insufficient_material::<Chess>("8/8/8/4k3/5b2/3K4/8/2B5 w - - 0 33", true, true);
3957        assert_insufficient_material::<Chess>("3b4/8/8/6b1/8/8/R7/K1k5 w - - 0 1", false, true);
3958    }
3959
3960    #[test]
3961    fn test_outcome() {
3962        for (fen, outcome) in [
3963            (
3964                "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
3965                Outcome::Unknown,
3966            ),
3967            (
3968                "2k5/8/8/8/8/8/8/3KB3 w - - 0 1",
3969                Outcome::Known(KnownOutcome::Draw),
3970            ),
3971            (
3972                "8/8/8/8/8/Q1K5/8/1k6 b - - 0 1",
3973                Outcome::Known(KnownOutcome::Draw),
3974            ),
3975            (
3976                "8/8/8/8/8/Q7/2K5/k7 b - - 0 1",
3977                Outcome::Known(KnownOutcome::Decisive { winner: White }),
3978            ),
3979        ] {
3980            let pos: Chess = setup_fen(fen);
3981            assert_eq!(pos.outcome(), outcome);
3982        }
3983    }
3984
3985    #[test]
3986    #[cfg(feature = "std")]
3987    fn test_eq() {
3988        fn hash<T: Hash>(value: &T) -> u64 {
3989            let mut hasher = std::collections::hash_map::DefaultHasher::new();
3990            value.hash(&mut hasher);
3991            hasher.finish()
3992        }
3993
3994        assert_eq!(Chess::default(), Chess::default());
3995        assert_eq!(hash(&Chess::default()), hash(&Chess::default()));
3996        assert_ne!(
3997            Chess::default(),
3998            Chess::default().swap_turn().expect("swap turn legal")
3999        );
4000
4001        // Check that castling paths do not interfere.
4002        let pos: Chess = setup_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBK1BNR w KQkq - 0 1");
4003        let pos_after_move_played = pos
4004            .play(Move::Normal {
4005                role: Role::King,
4006                from: Square::D1,
4007                to: Square::E1,
4008                capture: None,
4009                promotion: None,
4010            })
4011            .expect("Ke1 is legal");
4012        let pos_after_move =
4013            setup_fen::<Chess>("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB1KBNR b kq - 1 1");
4014        assert_eq!(pos_after_move, pos_after_move_played);
4015        assert_eq!(hash(&pos_after_move), hash(&pos_after_move_played));
4016
4017        // Check that promoted pieces are irrelevant in standard chess.
4018        let pos: Chess = setup_fen("rnbqkbn1/pppppppP/8/8/8/8/PPPPPPP1/RNB~QKBNR w KQq - 0 26");
4019        let pos_after_queen_promotion = pos
4020            .clone()
4021            .play(Move::Normal {
4022                role: Role::Pawn,
4023                from: Square::H7,
4024                to: Square::H8,
4025                capture: None,
4026                promotion: Some(Role::Queen),
4027            })
4028            .expect("h8=Q is legal");
4029        let pos_after_knight_promotion = pos
4030            .play(Move::Normal {
4031                role: Role::Pawn,
4032                from: Square::H7,
4033                to: Square::H8,
4034                capture: None,
4035                promotion: Some(Role::Knight),
4036            })
4037            .expect("h8=N is legal");
4038        let final_pos: Chess =
4039            setup_fen("rnbqkbnQ/ppppppp1/8/8/8/8/PPPPPPP1/RNBQKBNR b KQq - 0 26");
4040        assert_eq!(pos_after_queen_promotion, final_pos);
4041        assert_eq!(hash(&pos_after_queen_promotion), hash(&final_pos));
4042        assert_ne!(pos_after_knight_promotion, final_pos);
4043
4044        // Check that irrelevant en passant is treated as such.
4045        let pos: Chess = setup_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1");
4046        let pos_with_irrelevant_ep: Chess =
4047            setup_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1");
4048        assert_eq!(pos, pos_with_irrelevant_ep);
4049        assert_eq!(hash(&pos), hash(&pos_with_irrelevant_ep));
4050    }
4051
4052    #[cfg(feature = "variant")]
4053    #[test]
4054    fn test_variant_insufficient_material() {
4055        use super::variant::*;
4056
4057        let false_negative = false;
4058
4059        assert_insufficient_material::<Atomic>("8/3k4/8/8/2N5/8/3K4/8 b - - 0 1", true, true);
4060        assert_insufficient_material::<Atomic>("8/4rk2/8/8/8/8/3K4/8 w - - 0 1", true, true);
4061        assert_insufficient_material::<Atomic>("8/4qk2/8/8/8/8/3K4/8 w - - 0 1", true, false);
4062        assert_insufficient_material::<Atomic>("8/1k6/8/2n5/8/3NK3/8/8 b - - 0 1", false, false);
4063        assert_insufficient_material::<Atomic>("8/4bk2/8/8/8/8/3KB3/8 w - - 0 1", true, true);
4064        assert_insufficient_material::<Atomic>("4b3/5k2/8/8/8/8/3KB3/8 w - - 0 1", false, false);
4065        assert_insufficient_material::<Atomic>("3Q4/5kKB/8/8/8/8/8/8 b - - 0 1", false, true);
4066        assert_insufficient_material::<Atomic>("8/5k2/8/8/8/8/5K2/4bb2 w - - 0 1", true, false);
4067        assert_insufficient_material::<Atomic>("8/5k2/8/8/8/8/5K2/4nb2 w - - 0 1", true, false);
4068
4069        assert_insufficient_material::<Antichess>("8/4bk2/8/8/8/8/3KB3/8 w - - 0 1", false, false);
4070        assert_insufficient_material::<Antichess>("4b3/5k2/8/8/8/8/3KB3/8 w - - 0 1", false, false);
4071        assert_insufficient_material::<Antichess>("8/8/8/6b1/8/3B4/4B3/5B2 w - - 0 1", true, true);
4072        assert_insufficient_material::<Antichess>("8/8/5b2/8/8/3B4/3B4/8 w - - 0 1", true, false);
4073        assert_insufficient_material::<Antichess>(
4074            "8/5p2/5P2/8/3B4/1bB5/8/8 b - - 0 1",
4075            false_negative,
4076            false_negative,
4077        );
4078        assert_insufficient_material::<Antichess>(
4079            "8/8/6b1/8/3P4/8/5B2/8 w - - 0 1",
4080            false_negative,
4081            false,
4082        );
4083
4084        assert_insufficient_material::<KingOfTheHill>(
4085            "8/5k2/8/8/8/8/3K4/8 w - - 0 1",
4086            false,
4087            false,
4088        );
4089
4090        assert_insufficient_material::<RacingKings>("8/5k2/8/8/8/8/3K4/8 w - - 0 1", false, false);
4091
4092        assert_insufficient_material::<ThreeCheck>("8/5k2/8/8/8/8/3K4/8 w - - 0 1", true, true);
4093        assert_insufficient_material::<ThreeCheck>("8/5k2/8/8/8/8/3K2N1/8 w - - 0 1", false, true);
4094
4095        assert_insufficient_material::<Crazyhouse>("8/5k2/8/8/8/8/3K2N1/8 w - - 0 1", true, true);
4096        assert_insufficient_material::<Crazyhouse>(
4097            "8/5k2/8/8/8/5B2/3KB3/8 w - - 0 1",
4098            false,
4099            false,
4100        );
4101        assert_insufficient_material::<Crazyhouse>(
4102            "8/8/8/8/3k4/3N~4/3K4/8 w - - 0 1",
4103            false,
4104            false,
4105        );
4106
4107        assert_insufficient_material::<Horde>("8/5k2/8/8/8/4NN2/8/8 w - - 0 1", true, false);
4108        assert_insufficient_material::<Horde>("8/8/8/7k/7P/7P/8/8 b - - 0 58", false, false);
4109    }
4110
4111    #[cfg(feature = "variant")]
4112    #[test]
4113    fn test_exploded_king_loses_castling_rights() {
4114        use super::variant::Atomic;
4115
4116        let pos: Atomic = setup_fen("rnb1kbnr/pppppppp/8/4q3/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1");
4117
4118        let pos = pos
4119            .play(Move::Normal {
4120                role: Role::Queen,
4121                from: Square::E5,
4122                to: Square::E2,
4123                capture: Some(Role::Pawn),
4124                promotion: None,
4125            })
4126            .expect("Qxe2# is legal");
4127
4128        assert_eq!(
4129            pos.castles().castling_rights(),
4130            Bitboard::from(Square::A8) | Bitboard::from(Square::H8)
4131        );
4132        assert_eq!(
4133            pos.castles().rook(Color::White, CastlingSide::QueenSide),
4134            None
4135        );
4136        assert_eq!(
4137            pos.castles().rook(Color::White, CastlingSide::KingSide),
4138            None
4139        );
4140        assert_eq!(
4141            pos.castles().rook(Color::Black, CastlingSide::QueenSide),
4142            Some(Square::A8)
4143        );
4144        assert_eq!(
4145            pos.castles().rook(Color::Black, CastlingSide::KingSide),
4146            Some(Square::H8)
4147        );
4148    }
4149
4150    #[cfg(feature = "variant")]
4151    #[test]
4152    fn test_atomic_exploded_king() {
4153        use super::variant::Atomic;
4154
4155        let pos: Atomic = setup_fen("rn5r/pp4pp/2p3Nn/5p2/1b2P1PP/8/PPP2P2/R1B1KB1R b KQ - 0 9");
4156
4157        assert_eq!(
4158            pos.outcome(),
4159            Outcome::Known(KnownOutcome::Decisive {
4160                winner: Color::White
4161            })
4162        );
4163    }
4164
4165    #[cfg(feature = "variant")]
4166    #[test]
4167    fn test_racing_kings_end() {
4168        use super::variant::RacingKings;
4169
4170        // Both players reached the backrank.
4171        let pos: RacingKings = setup_fen("kr3NK1/1q2R3/8/8/8/5n2/2N5/1rb2B1R w - - 11 14");
4172        assert!(pos.is_variant_end());
4173        assert_eq!(pos.variant_outcome(), Outcome::Known(KnownOutcome::Draw));
4174
4175        // White to move is lost because black reached the backrank.
4176        let pos: RacingKings = setup_fen("1k6/6K1/8/8/8/8/8/8 w - - 0 1");
4177        assert!(pos.is_variant_end());
4178        assert_eq!(
4179            pos.variant_outcome(),
4180            Outcome::Known(KnownOutcome::Decisive {
4181                winner: Color::Black
4182            })
4183        );
4184
4185        // Black is given a chance to catch up.
4186        let pos: RacingKings = setup_fen("1K6/7k/8/8/8/8/8/8 b - - 0 1");
4187        assert_eq!(pos.variant_outcome(), Outcome::Unknown);
4188
4189        // Black near backrank but cannot move there.
4190        let pos: RacingKings = setup_fen("2KR4/k7/2Q5/4q3/8/8/8/2N5 b - - 0 1");
4191        assert!(pos.is_variant_end());
4192        assert_eq!(
4193            pos.variant_outcome(),
4194            Outcome::Known(KnownOutcome::Decisive {
4195                winner: Color::White
4196            })
4197        );
4198    }
4199
4200    #[cfg(feature = "variant")]
4201    #[test]
4202    fn test_antichess_insufficient_material() {
4203        use super::variant::Antichess;
4204
4205        for (fen, possible_winner) in [
4206            // https://lichess.org/aWVWduVV
4207            ("8/8/8/1n2N3/8/8/8/8 w - - 0 32", Color::Black),
4208            ("8/3N4/8/1n6/8/8/8/8 b - - 1 32", Color::Black),
4209            // https://lichess.org/EzRIcUxc
4210            ("8/8/8/8/2N5/8/8/n7 w - - 0 30", Color::Black),
4211            ("8/8/8/4N3/8/8/8/n7 b - - 1 30", Color::Black),
4212            ("8/8/8/4N3/8/8/2n5/8 w - - 2 31", Color::Black),
4213            ("8/8/6N1/8/8/8/2n5/8 b - - 3 31", Color::Black),
4214            ("8/8/6N1/8/8/4n3/8/8 w - - 4 32", Color::Black),
4215            ("5N2/8/8/8/8/4n3/8/8 b - - 5 32", Color::Black),
4216            ("5N2/8/8/5n2/8/8/8/8 w - - 6 33", Color::Black),
4217            // https://lichess.org/te3cf1wG
4218            ("6n1/8/8/4N3/8/8/8/8 b - - 0 27", Color::White),
4219            ("8/8/5n2/4N3/8/8/8/8 w - - 1 28", Color::White),
4220            ("8/3N4/5n2/8/8/8/8/8 b - - 2 28", Color::White),
4221            ("8/3n4/8/8/8/8/8/8 w - - 0 29", Color::White),
4222        ] {
4223            let pos = fen
4224                .parse::<Fen>()
4225                .expect("valid fen")
4226                .into_position::<Antichess>(CastlingMode::Standard)
4227                .expect("legal position");
4228            assert!(
4229                !pos.has_insufficient_material(possible_winner),
4230                "{possible_winner} can win {fen}"
4231            );
4232            assert!(
4233                pos.has_insufficient_material(!possible_winner),
4234                "{possible_winner} can not win {fen}"
4235            );
4236        }
4237    }
4238
4239    #[test]
4240    fn test_aligned_checkers() {
4241        let res = "2Nq4/2K5/1b6/8/7R/3k4/7P/8 w - - 0 1"
4242            .parse::<Fen>()
4243            .expect("valid fen")
4244            .into_position::<Chess>(CastlingMode::Chess960);
4245        assert_eq!(
4246            res.expect_err("impossible check").kinds(),
4247            PositionErrorKinds::IMPOSSIBLE_CHECK
4248        );
4249
4250        let _ = "8/8/5k2/p1q5/PP1rp1P1/3P1N2/2RK1r2/5nN1 w - - 0 3"
4251            .parse::<Fen>()
4252            .expect("valid fen")
4253            .into_position::<Chess>(CastlingMode::Standard)
4254            .expect("checkers aligned with opponent king not relevant");
4255
4256        let res = "8/8/8/1k6/3Pp3/8/8/4KQ2 b - d3 0 1"
4257            .parse::<Fen>()
4258            .expect("valid fen")
4259            .into_position::<Chess>(CastlingMode::Standard);
4260        assert_eq!(
4261            res.expect_err("impossible check due to ep square").kinds(),
4262            PositionErrorKinds::IMPOSSIBLE_CHECK
4263        );
4264    }
4265
4266    #[cfg(feature = "alloc")]
4267    #[test]
4268    fn test_swap_turn() {
4269        use alloc::string::ToString as _;
4270        let pos: Chess = "rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3"
4271            .parse::<Fen>()
4272            .expect("valid fen")
4273            .into_position(CastlingMode::Chess960)
4274            .expect("valid position");
4275        let swapped = pos.swap_turn().expect("swap turn");
4276        assert_eq!(
4277            Fen::from_position(&swapped, EnPassantMode::Always).to_string(),
4278            "rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 3"
4279        );
4280    }
4281
4282    #[test]
4283    fn test_invalid_ep_square() {
4284        let fen: Fen = "4k3/8/8/8/8/8/8/4K3 w - e3 0 1".parse().expect("valid fen");
4285        let err = fen
4286            .into_position::<Chess>(CastlingMode::Standard)
4287            .expect_err("invalid ep square");
4288        assert_eq!(err.kinds(), PositionErrorKinds::INVALID_EP_SQUARE);
4289        assert_eq!(
4290            err.ignore_invalid_ep_square()
4291                .expect("now valid")
4292                .maybe_ep_square(),
4293            None
4294        );
4295    }
4296
4297    #[test]
4298    fn test_check_with_unrelated_ep_square() {
4299        let fen: Fen = "rnbqk1nr/bb3p1p/1q2r3/2pPp3/3P4/7P/1PP1NpPP/R1BQKBNR w KQkq c6 0 1"
4300            .parse()
4301            .expect("valid fen");
4302        let pos = fen
4303            .into_position::<Chess>(CastlingMode::Standard)
4304            .expect_err("impossible check")
4305            .ignore_impossible_check()
4306            .expect("legal otherwise");
4307        assert!(pos.san_candidates(Role::Pawn, Square::C6).is_empty());
4308        assert!(pos.en_passant_moves().is_empty());
4309        assert_eq!(pos.legal_moves().len(), 2);
4310    }
4311
4312    #[test]
4313    fn test_put_in_standard() {
4314        let pos = Chess::default();
4315        assert!(!pos.is_legal(Move::Put {
4316            role: Role::Pawn,
4317            to: Square::D4
4318        }));
4319    }
4320
4321    #[test]
4322    fn test_from_setup() {
4323        for (epd, expected_error_kinds) in [
4324            (
4325                "1rrrrrk1/1PPPPPPP/8/8/8/8/8/6K1 b - -",
4326                PositionErrorKinds::IMPOSSIBLE_CHECK,
4327            ),
4328            (
4329                "1rrrrrk1/PPPPPPPP/8/8/8/8/8/6K1 b - -",
4330                PositionErrorKinds::IMPOSSIBLE_CHECK,
4331            ),
4332            (
4333                "1rrrrrkr/PPPPPPPP/8/8/8/8/8/6K1 b - -",
4334                PositionErrorKinds::IMPOSSIBLE_CHECK,
4335            ),
4336            (
4337                "1q4k1/3r1Ppp/5NP1/pP6/8/1Q6/3B4/2K2R2 b - -",
4338                PositionErrorKinds::IMPOSSIBLE_CHECK,
4339            ),
4340            (
4341                "2b5/1nbn4/n3n3/1kn5/n3n3/1n1n4/5RQ1/2KQ1R2 w K -",
4342                PositionErrorKinds::IMPOSSIBLE_CHECK,
4343            ),
4344            (
4345                "1n1b1Q2/1b4rp/1q5P/2Pppr2/p2kP1pR/2NP4/P2Bq1K1/RQ3Br1 w - -",
4346                PositionErrorKinds::default(),
4347            ),
4348            (
4349                "1n1b1Q2/1b4rp/1q5P/2Pppr2/p2kP1pR/2NP4/P2Bq1K1/RQ3Bq1 w - -",
4350                PositionErrorKinds::default(),
4351            ),
4352            (
4353                "1n1b1Q2/qb4rp/7P/2Pppr2/p2kP1p1/2NP4/P2Bq1KR/RQ3Br1 w - -",
4354                PositionErrorKinds::default(),
4355            ),
4356            (
4357                "1n1b1Q2/qb4rp/7P/2Pppr2/p2kP1p1/2NP4/P2Bq1KR/RQ3Bq1 w - -",
4358                PositionErrorKinds::default(),
4359            ),
4360        ] {
4361            let error_kinds = epd
4362                .parse::<Epd>()
4363                .expect("valid epd")
4364                .into_position::<Chess>(CastlingMode::Chess960)
4365                .map_err(|err| err.kinds())
4366                .err()
4367                .unwrap_or_default();
4368            assert_eq!(error_kinds, expected_error_kinds, "epd: {epd}");
4369        }
4370    }
4371}