From 36535f292aaa73501ad80258e8673247af7ecf6a Mon Sep 17 00:00:00 2001 From: Paul-Nicolas Madelaine Date: Mon, 17 Nov 2025 22:07:05 +0100 Subject: [PATCH] add details about the maximum number of moves --- src/moves.rs | 15 ++++++++++----- src/position.rs | 2 -- tests/tests.rs | 11 +++++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/moves.rs b/src/moves.rs index 3dc3d76..c4e35bb 100644 --- a/src/moves.rs +++ b/src/moves.rs @@ -196,16 +196,21 @@ impl<'l> Move<'l> { } } -/// A list of legal moves on the same position. +/// The list of all the legal moves on a position. It is returned by [`Position::legal_moves`]. /// -/// It can be obtained using the [`Position::legal_moves`] method. This type is an iterator over -/// [`Move`] objects. +/// This type lies on the stack and has a fixed capacity of 218 moves, as there exists no reachable +/// position with more legal moves[^1]. +/// +/// [^1]: In 1964, Nenad Petrović published a +/// [composition](https://lichess.org/editor/R6R/3Q4/1Q4Q1/4Q3/2Q4Q/Q4Q2/pp1Q4/kBNN1KB1_w_-_-_0_1) +/// with 218 legal moves. 60 years later, this was proven to be optimal by +/// [Tobs40](https://lichess.org/@/Tobs40/blog/why-a-reachable-position-can-have-at-most-218-playable-moves/a5xdxeqs). #[must_use] pub struct Moves<'l> { position: &'l Position, is_check: bool, en_passant_is_legal: bool, - array: ArrayVec, + array: ArrayVec, } impl<'l> MoveGen for Moves<'l> { @@ -350,7 +355,7 @@ impl<'l> IntoIterator for &'l Moves<'l> { /// An iterator over legal moves. pub struct MovesIntoIter<'l> { position: &'l Position, - iter: ArrayVecIntoIter, + iter: ArrayVecIntoIter, } impl<'l> Iterator for MovesIntoIter<'l> { type Item = Move<'l>; diff --git a/src/position.rs b/src/position.rs index a22b5b2..adce0b9 100644 --- a/src/position.rs +++ b/src/position.rs @@ -59,8 +59,6 @@ use core::ops::ControlFlow; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Position(Setup); -pub(crate) const MAX_LEGAL_MOVES: usize = 218; - const PAWN: u8 = Role::Pawn as u8; const KNIGHT: u8 = Role::Knight as u8; const BISHOP: u8 = Role::Bishop as u8; diff --git a/tests/tests.rs b/tests/tests.rs index f22dc40..1efe806 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -318,3 +318,14 @@ fn san() { Err(InvalidSan::Ambiguous), ); } + +#[test] +fn max_legal_moves() { + let position = Setup::from_text_record("R6R/3Q4/1Q4Q1/4Q3/2Q4Q/Q4Q2/pp1Q4/kBNN1KB1 w - -") + .unwrap() + .into_position() + .unwrap(); + assert_eq!(position.count_legal_moves(), 218); + assert_eq!(position.legal_moves().len(), 218); + assert_eq!(position.perft(2), 99); +}