split code
This commit is contained in:
parent
b3a1e476b2
commit
01253b66de
6 changed files with 403 additions and 381 deletions
|
|
@ -14,9 +14,9 @@
|
|||
//! [`IllegalPositionReason`](setup::IllegalPositionReason) to know more). Legal moves are then
|
||||
//! generated using the [`Position::legal_moves`](position::Position::legal_moves) method or
|
||||
//! obtained from chess notation like [`UciMove`](uci::UciMove) or [`San`](san::San). Moves are
|
||||
//! represented with the [`Move<'l>`](position::Move) type, which holds a reference to the origin
|
||||
//! represented with the [`Move<'l>`](moves::Move) type, which holds a reference to the origin
|
||||
//! position (hence the lifetime), this ensures the move is played on the correct position.
|
||||
//! Finally, moves are played using the [`Move::make`](position::Move) method which returns a new
|
||||
//! Finally, moves are played using the [`Move::make`](moves::Move::make) method which returns a new
|
||||
//! [`Position`](position::Position), and on it goes.
|
||||
//!
|
||||
//! ## Example
|
||||
|
|
@ -78,6 +78,7 @@ pub(crate) mod magics;
|
|||
|
||||
pub mod bitboard;
|
||||
pub mod board;
|
||||
pub mod moves;
|
||||
pub mod position;
|
||||
pub mod san;
|
||||
pub mod setup;
|
||||
|
|
|
|||
375
src/moves.rs
Normal file
375
src/moves.rs
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
//! Move representation.
|
||||
|
||||
use crate::array_vec::*;
|
||||
use crate::bitboard::*;
|
||||
use crate::board::*;
|
||||
use crate::position::*;
|
||||
use crate::san::*;
|
||||
use crate::uci::*;
|
||||
|
||||
use std::iter::ExactSizeIterator;
|
||||
use std::iter::FusedIterator;
|
||||
|
||||
/// A legal move.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Move<'l> {
|
||||
position: &'l Position,
|
||||
raw: RawMove,
|
||||
}
|
||||
|
||||
impl<'l> Move<'l> {
|
||||
pub(crate) unsafe fn new_unchecked(position: &'l Position, raw: RawMove) -> Self {
|
||||
Self { position, raw }
|
||||
}
|
||||
|
||||
/// Returns the position after playing the move.
|
||||
///
|
||||
/// This sets the en passant square after a double pawn advance, even when there is no pawn to
|
||||
/// capture or when capturing is not legal.
|
||||
pub fn make(self) -> Position {
|
||||
let mut position = self.position.clone();
|
||||
unsafe { position.play_unchecked(self.raw) };
|
||||
position
|
||||
}
|
||||
|
||||
/// Returns the position the move is played on.
|
||||
#[inline]
|
||||
pub fn position(self) -> &'l Position {
|
||||
self.position
|
||||
}
|
||||
|
||||
/// Returns the type of piece that moves.
|
||||
#[inline]
|
||||
pub fn role(self) -> Role {
|
||||
self.raw.role()
|
||||
}
|
||||
|
||||
/// Returns the origin square of the move.
|
||||
#[inline]
|
||||
pub fn from(self) -> Square {
|
||||
self.raw.from()
|
||||
}
|
||||
|
||||
/// Returns the target square of the move.
|
||||
#[inline]
|
||||
pub fn to(self) -> Square {
|
||||
self.raw.to()
|
||||
}
|
||||
|
||||
/// Returns the type of piece that the pawn is promoted to, if the move is a promotion.
|
||||
#[inline]
|
||||
pub fn promotion(self) -> Option<Role> {
|
||||
self.raw.promotion()
|
||||
}
|
||||
|
||||
/// Returns `true` if the move is a capture.
|
||||
#[inline]
|
||||
pub fn is_capture(self) -> bool {
|
||||
let setup = self.position.as_setup();
|
||||
self.raw.kind == MoveType::EnPassant
|
||||
|| !((setup.p_b_q | setup.n_b_k | setup.r_q_k) & self.to().bitboard()).is_empty()
|
||||
}
|
||||
|
||||
/// Returns the type of piece that is captured, if the move is a capture.
|
||||
#[inline]
|
||||
pub fn captured(self) -> Option<Role> {
|
||||
match self.raw.kind {
|
||||
MoveType::EnPassant => Some(Role::Pawn),
|
||||
_ => self.position.as_setup().get_role(self.raw.to),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the UCI notation of the move.
|
||||
#[inline]
|
||||
pub fn to_uci(self) -> UciMove {
|
||||
self.raw.uci()
|
||||
}
|
||||
|
||||
/// Returns the standard algebraic notation of the move.
|
||||
pub fn to_san(self) -> San {
|
||||
struct MoveGenImpl<const ROLE: u8> {
|
||||
to: Bitboard,
|
||||
candidates: Bitboard,
|
||||
}
|
||||
impl<const ROLE: u8> MoveGenImpl<ROLE> {
|
||||
#[inline]
|
||||
fn new(to: Square) -> Self {
|
||||
Self {
|
||||
to: to.bitboard(),
|
||||
candidates: Bitboard::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<const ROLE: u8> MoveGen for MoveGenImpl<ROLE> {
|
||||
#[inline]
|
||||
fn roles(&self, role: Role) -> bool {
|
||||
role as u8 == ROLE
|
||||
}
|
||||
#[inline]
|
||||
fn to(&self) -> Bitboard {
|
||||
self.to
|
||||
}
|
||||
#[inline]
|
||||
fn is_check(&mut self) {}
|
||||
#[inline]
|
||||
fn en_passant_is_legal(&mut self) {}
|
||||
#[inline]
|
||||
fn extend<I>(&mut self, iter: I)
|
||||
where
|
||||
I: Iterator<Item = RawMove> + ExactSizeIterator,
|
||||
{
|
||||
iter.for_each(|raw| {
|
||||
debug_assert!(raw.role() as u8 == ROLE);
|
||||
debug_assert!(self.to.contains(raw.to()));
|
||||
self.candidates.insert(raw.from);
|
||||
});
|
||||
}
|
||||
}
|
||||
San {
|
||||
inner: match self.raw.kind {
|
||||
MoveType::CastleShort => SanInner::Castle(CastlingSide::Short),
|
||||
MoveType::CastleLong => SanInner::Castle(CastlingSide::Long),
|
||||
MoveType::PawnAdvance
|
||||
| MoveType::PawnAdvancePromotion
|
||||
| MoveType::PawnDoubleAdvance => SanInner::Normal {
|
||||
role: Role::Pawn,
|
||||
file: None,
|
||||
rank: None,
|
||||
capture: false,
|
||||
target: self.to(),
|
||||
promotion: self.promotion(),
|
||||
},
|
||||
MoveType::PawnAttack | MoveType::PawnAttackPromotion | MoveType::EnPassant => {
|
||||
SanInner::Normal {
|
||||
role: Role::Pawn,
|
||||
file: Some(self.from().file()),
|
||||
rank: None,
|
||||
capture: true,
|
||||
target: self.to(),
|
||||
promotion: self.promotion(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
fn aux<const ROLE: u8>(m: &Move) -> SanInner {
|
||||
let mut moves = MoveGenImpl::<ROLE>::new(m.to());
|
||||
m.position().generate_moves(&mut moves);
|
||||
let candidates = moves.candidates;
|
||||
let (file, rank) = if candidates == m.from().bitboard() {
|
||||
(None, None)
|
||||
} else if candidates & m.from().file().bitboard() == m.from().bitboard() {
|
||||
(Some(m.from().file()), None)
|
||||
} else if candidates & m.from().rank().bitboard() == m.from().bitboard() {
|
||||
(None, Some(m.from().rank()))
|
||||
} else {
|
||||
(Some(m.from().file()), Some(m.from().rank()))
|
||||
};
|
||||
SanInner::Normal {
|
||||
role: m.role(),
|
||||
file,
|
||||
rank,
|
||||
capture: m.is_capture(),
|
||||
target: m.to(),
|
||||
promotion: None,
|
||||
}
|
||||
}
|
||||
match self.role() {
|
||||
Role::Pawn => aux::<1>(&self),
|
||||
Role::Knight => aux::<2>(&self),
|
||||
Role::Bishop => aux::<3>(&self),
|
||||
Role::Rook => aux::<4>(&self),
|
||||
Role::Queen => aux::<5>(&self),
|
||||
Role::King => aux::<6>(&self),
|
||||
}
|
||||
}
|
||||
},
|
||||
suffix: {
|
||||
let pos = self.make();
|
||||
let mut moves = MateMoveGenImpl::new();
|
||||
pos.generate_moves(&mut moves);
|
||||
moves.is_check.then(|| match moves.is_mate {
|
||||
true => SanSuffix::Checkmate,
|
||||
false => SanSuffix::Check,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of legal moves on the same position.
|
||||
///
|
||||
/// It can be obtained using the [`Position::legal_moves`] method. This type is an iterator over
|
||||
/// [`Move`] objects.
|
||||
pub struct Moves<'l> {
|
||||
position: &'l Position,
|
||||
is_check: bool,
|
||||
en_passant_is_legal: bool,
|
||||
array: ArrayVec<RawMove, MAX_LEGAL_MOVES>,
|
||||
}
|
||||
|
||||
impl<'l> MoveGen for Moves<'l> {
|
||||
#[inline]
|
||||
fn is_check(&mut self) {
|
||||
self.is_check = true;
|
||||
}
|
||||
#[inline]
|
||||
fn en_passant_is_legal(&mut self) {
|
||||
self.en_passant_is_legal = true;
|
||||
}
|
||||
#[inline]
|
||||
fn extend<I>(&mut self, iter: I)
|
||||
where
|
||||
I: Iterator<Item = RawMove> + ExactSizeIterator,
|
||||
{
|
||||
iter.for_each(|raw| unsafe { self.array.push_unchecked(raw) });
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> Moves<'l> {
|
||||
#[inline]
|
||||
pub(crate) fn compute(position: &'l Position) -> Moves<'l> {
|
||||
fn aux(position: &Position, moves: &mut Moves) {
|
||||
position.generate_moves(moves);
|
||||
}
|
||||
let mut moves = Moves {
|
||||
position,
|
||||
is_check: false,
|
||||
en_passant_is_legal: false,
|
||||
array: ArrayVec::new(),
|
||||
};
|
||||
aux(position, &mut moves);
|
||||
moves
|
||||
}
|
||||
|
||||
/// Returns the position on which the moves are played.
|
||||
#[inline]
|
||||
pub fn position(&self) -> &Position {
|
||||
self.position
|
||||
}
|
||||
|
||||
/// Iterates over the moves.
|
||||
#[inline]
|
||||
pub fn iter(&'l self) -> MovesIter<'l> {
|
||||
MovesIter {
|
||||
position: self.position,
|
||||
iter: (&self.array).into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of moves in the list.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.array.len()
|
||||
}
|
||||
|
||||
/// Returns `true` if en passant is legal.
|
||||
#[inline]
|
||||
pub fn en_passant_is_legal(&self) -> bool {
|
||||
self.en_passant_is_legal
|
||||
}
|
||||
|
||||
/// Returns `true` if the king is in check.
|
||||
#[inline]
|
||||
pub fn is_check(&self) -> bool {
|
||||
self.is_check
|
||||
}
|
||||
|
||||
/// Returns the move at the given index, if it exists.
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<Move<'l>> {
|
||||
self.array.get(index).copied().map(|raw| Move {
|
||||
position: self.position,
|
||||
raw,
|
||||
})
|
||||
}
|
||||
|
||||
/// Sorts the moves in the list.
|
||||
///
|
||||
/// See [`slice::sort_unstable_by`] for potential panics.
|
||||
#[inline]
|
||||
pub fn sort_by<F>(&mut self, mut compare: F)
|
||||
where
|
||||
F: FnMut(Move, Move) -> std::cmp::Ordering,
|
||||
{
|
||||
self.array.as_slice_mut().sort_unstable_by(|a, b| {
|
||||
compare(
|
||||
Move {
|
||||
position: self.position,
|
||||
raw: *a,
|
||||
},
|
||||
Move {
|
||||
position: self.position,
|
||||
raw: *b,
|
||||
},
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over legal moves.
|
||||
pub struct MovesIter<'l> {
|
||||
position: &'l Position,
|
||||
iter: ArrayVecIter<'l, RawMove, MAX_LEGAL_MOVES>,
|
||||
}
|
||||
impl<'l> Iterator for MovesIter<'l> {
|
||||
type Item = Move<'l>;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|raw| Move {
|
||||
position: self.position,
|
||||
raw,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<'l> FusedIterator for MovesIter<'l> {}
|
||||
impl<'l> ExactSizeIterator for MovesIter<'l> {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
impl<'l> IntoIterator for &'l Moves<'l> {
|
||||
type Item = Move<'l>;
|
||||
type IntoIter = MovesIter<'l>;
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over legal moves.
|
||||
pub struct MovesIntoIter<'l> {
|
||||
position: &'l Position,
|
||||
iter: ArrayVecIntoIter<RawMove, MAX_LEGAL_MOVES>,
|
||||
}
|
||||
impl<'l> Iterator for MovesIntoIter<'l> {
|
||||
type Item = Move<'l>;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|raw| Move {
|
||||
position: self.position,
|
||||
raw,
|
||||
})
|
||||
}
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
impl<'l> FusedIterator for MovesIntoIter<'l> {}
|
||||
impl<'l> ExactSizeIterator for MovesIntoIter<'l> {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
impl<'l> IntoIterator for Moves<'l> {
|
||||
type Item = Move<'l>;
|
||||
type IntoIter = MovesIntoIter<'l>;
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
MovesIntoIter {
|
||||
position: self.position,
|
||||
iter: self.array.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
400
src/position.rs
400
src/position.rs
|
|
@ -1,9 +1,9 @@
|
|||
//! **Move generation.**
|
||||
|
||||
use crate::array_vec::*;
|
||||
use crate::bitboard::*;
|
||||
use crate::board::*;
|
||||
use crate::lookup;
|
||||
use crate::moves::*;
|
||||
use crate::san::*;
|
||||
use crate::setup::*;
|
||||
use crate::uci::*;
|
||||
|
|
@ -57,7 +57,7 @@ use std::iter::FusedIterator;
|
|||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Position(Setup);
|
||||
|
||||
const MAX_LEGAL_MOVES: usize = 218;
|
||||
pub(crate) const MAX_LEGAL_MOVES: usize = 218;
|
||||
|
||||
impl Position {
|
||||
/// Returns the initial position of a chess game.
|
||||
|
|
@ -79,17 +79,7 @@ impl Position {
|
|||
/// Returns all the legal moves on the position.
|
||||
#[inline]
|
||||
pub fn legal_moves<'l>(&'l self) -> Moves<'l> {
|
||||
fn aux(position: &Position, moves: &mut Moves) {
|
||||
position.generate_moves(moves);
|
||||
}
|
||||
let mut moves = Moves {
|
||||
position: self,
|
||||
is_check: false,
|
||||
en_passant_is_legal: false,
|
||||
array: ArrayVec::new(),
|
||||
};
|
||||
aux(self, &mut moves);
|
||||
moves
|
||||
Moves::compute(self)
|
||||
}
|
||||
|
||||
/// Counts the legal moves on the position.
|
||||
|
|
@ -287,7 +277,7 @@ impl Position {
|
|||
let mut moves = MoveGenImpl::<ROLE>::new(role, from.bitboard(), to.bitboard());
|
||||
position.generate_moves(&mut moves);
|
||||
let raw = moves.found.ok_or(InvalidUciMove::Illegal)?;
|
||||
Ok(Move { position, raw })
|
||||
Ok(unsafe { Move::new_unchecked(position, raw) })
|
||||
}
|
||||
let promotion = if role == Role::Pawn {
|
||||
promotion.unwrap_or(Role::Pawn)
|
||||
|
|
@ -408,7 +398,7 @@ impl Position {
|
|||
None => Err(InvalidSan::Illegal),
|
||||
Some(raw) => match moves.found_other {
|
||||
true => Err(InvalidSan::Ambiguous),
|
||||
false => Ok(Move { position, raw }),
|
||||
false => Ok(unsafe { Move::new_unchecked(position, raw) }),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -431,345 +421,17 @@ impl std::fmt::Debug for Position {
|
|||
}
|
||||
}
|
||||
|
||||
/// A legal move.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Move<'l> {
|
||||
position: &'l Position,
|
||||
raw: RawMove,
|
||||
}
|
||||
|
||||
impl<'l> Move<'l> {
|
||||
/// Returns the position after playing the move.
|
||||
///
|
||||
/// This sets the en passant square after a double pawn advance, even when there is no pawn to
|
||||
/// capture or when capturing is not legal.
|
||||
pub fn make(self) -> Position {
|
||||
let mut position = self.position.clone();
|
||||
unsafe { position.play_unchecked(self.raw) };
|
||||
position
|
||||
}
|
||||
|
||||
/// Returns the position the move is played on.
|
||||
#[inline]
|
||||
pub fn position(self) -> &'l Position {
|
||||
self.position
|
||||
}
|
||||
|
||||
/// Returns the type of piece that moves.
|
||||
#[inline]
|
||||
pub fn role(self) -> Role {
|
||||
self.raw.role()
|
||||
}
|
||||
|
||||
/// Returns the origin square of the move.
|
||||
#[inline]
|
||||
pub fn from(self) -> Square {
|
||||
self.raw.from()
|
||||
}
|
||||
|
||||
/// Returns the target square of the move.
|
||||
#[inline]
|
||||
pub fn to(self) -> Square {
|
||||
self.raw.to()
|
||||
}
|
||||
|
||||
/// Returns the type of piece that the pawn is promoted to, if the move is a promotion.
|
||||
#[inline]
|
||||
pub fn promotion(self) -> Option<Role> {
|
||||
self.raw.promotion()
|
||||
}
|
||||
|
||||
/// Returns `true` if the move is a capture.
|
||||
#[inline]
|
||||
pub fn is_capture(self) -> bool {
|
||||
self.raw.kind == MoveType::EnPassant
|
||||
|| !((self.position.0.p_b_q | self.position.0.n_b_k | self.position.0.r_q_k)
|
||||
& self.to().bitboard())
|
||||
.is_empty()
|
||||
}
|
||||
|
||||
/// Returns the type of piece that is captured, if the move is a capture.
|
||||
#[inline]
|
||||
pub fn captured(self) -> Option<Role> {
|
||||
match self.raw.kind {
|
||||
MoveType::EnPassant => Some(Role::Pawn),
|
||||
_ => self.position.0.get_role(self.raw.to),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the UCI notation of the move.
|
||||
#[inline]
|
||||
pub fn to_uci(self) -> UciMove {
|
||||
self.raw.uci()
|
||||
}
|
||||
|
||||
/// Returns the standard algebraic notation of the move.
|
||||
pub fn to_san(self) -> San {
|
||||
struct MoveGenImpl<const ROLE: u8> {
|
||||
to: Bitboard,
|
||||
candidates: Bitboard,
|
||||
}
|
||||
impl<const ROLE: u8> MoveGenImpl<ROLE> {
|
||||
#[inline]
|
||||
fn new(to: Square) -> Self {
|
||||
Self {
|
||||
to: to.bitboard(),
|
||||
candidates: Bitboard::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<const ROLE: u8> MoveGen for MoveGenImpl<ROLE> {
|
||||
#[inline]
|
||||
fn roles(&self, role: Role) -> bool {
|
||||
role as u8 == ROLE
|
||||
}
|
||||
#[inline]
|
||||
fn to(&self) -> Bitboard {
|
||||
self.to
|
||||
}
|
||||
#[inline]
|
||||
fn is_check(&mut self) {}
|
||||
#[inline]
|
||||
fn en_passant_is_legal(&mut self) {}
|
||||
#[inline]
|
||||
fn extend<I>(&mut self, iter: I)
|
||||
where
|
||||
I: Iterator<Item = RawMove> + ExactSizeIterator,
|
||||
{
|
||||
iter.for_each(|raw| {
|
||||
debug_assert!(raw.role() as u8 == ROLE);
|
||||
debug_assert!(self.to.contains(raw.to()));
|
||||
self.candidates.insert(raw.from);
|
||||
});
|
||||
}
|
||||
}
|
||||
San {
|
||||
inner: match self.raw.kind {
|
||||
MoveType::CastleShort => SanInner::Castle(CastlingSide::Short),
|
||||
MoveType::CastleLong => SanInner::Castle(CastlingSide::Long),
|
||||
MoveType::PawnAdvance
|
||||
| MoveType::PawnAdvancePromotion
|
||||
| MoveType::PawnDoubleAdvance => SanInner::Normal {
|
||||
role: Role::Pawn,
|
||||
file: None,
|
||||
rank: None,
|
||||
capture: false,
|
||||
target: self.to(),
|
||||
promotion: self.promotion(),
|
||||
},
|
||||
MoveType::PawnAttack | MoveType::PawnAttackPromotion | MoveType::EnPassant => {
|
||||
SanInner::Normal {
|
||||
role: Role::Pawn,
|
||||
file: Some(self.from().file()),
|
||||
rank: None,
|
||||
capture: true,
|
||||
target: self.to(),
|
||||
promotion: self.promotion(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
fn aux<const ROLE: u8>(m: &Move) -> SanInner {
|
||||
let mut moves = MoveGenImpl::<ROLE>::new(m.to());
|
||||
m.position().generate_moves(&mut moves);
|
||||
let candidates = moves.candidates;
|
||||
let (file, rank) = if candidates == m.from().bitboard() {
|
||||
(None, None)
|
||||
} else if candidates & m.from().file().bitboard() == m.from().bitboard() {
|
||||
(Some(m.from().file()), None)
|
||||
} else if candidates & m.from().rank().bitboard() == m.from().bitboard() {
|
||||
(None, Some(m.from().rank()))
|
||||
} else {
|
||||
(Some(m.from().file()), Some(m.from().rank()))
|
||||
};
|
||||
SanInner::Normal {
|
||||
role: m.role(),
|
||||
file,
|
||||
rank,
|
||||
capture: m.is_capture(),
|
||||
target: m.to(),
|
||||
promotion: None,
|
||||
}
|
||||
}
|
||||
match self.role() {
|
||||
Role::Pawn => aux::<1>(&self),
|
||||
Role::Knight => aux::<2>(&self),
|
||||
Role::Bishop => aux::<3>(&self),
|
||||
Role::Rook => aux::<4>(&self),
|
||||
Role::Queen => aux::<5>(&self),
|
||||
Role::King => aux::<6>(&self),
|
||||
}
|
||||
}
|
||||
},
|
||||
suffix: {
|
||||
let pos = self.make();
|
||||
let mut moves = MateMoveGenImpl::new();
|
||||
pos.generate_moves(&mut moves);
|
||||
moves.is_check.then(|| match moves.is_mate {
|
||||
true => SanSuffix::Checkmate,
|
||||
false => SanSuffix::Check,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of legal moves on the same position.
|
||||
///
|
||||
/// It can be obtained using the [`Position::legal_moves`] method. This type is an iterator over
|
||||
/// [`Move`] objects.
|
||||
pub struct Moves<'l> {
|
||||
position: &'l Position,
|
||||
is_check: bool,
|
||||
en_passant_is_legal: bool,
|
||||
array: ArrayVec<RawMove, MAX_LEGAL_MOVES>,
|
||||
}
|
||||
|
||||
impl<'l> Moves<'l> {
|
||||
/// Returns the position on which the moves are played.
|
||||
#[inline]
|
||||
pub fn position(&self) -> &Position {
|
||||
self.position
|
||||
}
|
||||
|
||||
/// Iterates over the moves.
|
||||
#[inline]
|
||||
pub fn iter(&'l self) -> MovesIter<'l> {
|
||||
MovesIter {
|
||||
position: self.position,
|
||||
iter: (&self.array).into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of moves in the list.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.array.len()
|
||||
}
|
||||
|
||||
/// Returns `true` if en passant is legal.
|
||||
#[inline]
|
||||
pub fn en_passant_is_legal(&self) -> bool {
|
||||
self.en_passant_is_legal
|
||||
}
|
||||
|
||||
/// Returns `true` if the king is in check.
|
||||
#[inline]
|
||||
pub fn is_check(&self) -> bool {
|
||||
self.is_check
|
||||
}
|
||||
|
||||
/// Returns the move at the given index, if it exists.
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<Move<'l>> {
|
||||
self.array.get(index).copied().map(|raw| Move {
|
||||
position: self.position,
|
||||
raw,
|
||||
})
|
||||
}
|
||||
|
||||
/// Sorts the moves in the list.
|
||||
///
|
||||
/// See [`slice::sort_unstable_by`] for potential panics.
|
||||
#[inline]
|
||||
pub fn sort_by<F>(&mut self, mut compare: F)
|
||||
where
|
||||
F: FnMut(Move, Move) -> std::cmp::Ordering,
|
||||
{
|
||||
self.array.as_slice_mut().sort_unstable_by(|a, b| {
|
||||
compare(
|
||||
Move {
|
||||
position: self.position,
|
||||
raw: *a,
|
||||
},
|
||||
Move {
|
||||
position: self.position,
|
||||
raw: *b,
|
||||
},
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over legal moves.
|
||||
pub struct MovesIter<'l> {
|
||||
position: &'l Position,
|
||||
iter: ArrayVecIter<'l, RawMove, MAX_LEGAL_MOVES>,
|
||||
}
|
||||
impl<'l> Iterator for MovesIter<'l> {
|
||||
type Item = Move<'l>;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|raw| Move {
|
||||
position: self.position,
|
||||
raw,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<'l> FusedIterator for MovesIter<'l> {}
|
||||
impl<'l> ExactSizeIterator for MovesIter<'l> {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
impl<'l> IntoIterator for &'l Moves<'l> {
|
||||
type Item = Move<'l>;
|
||||
type IntoIter = MovesIter<'l>;
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over legal moves.
|
||||
pub struct MovesIntoIter<'l> {
|
||||
position: &'l Position,
|
||||
iter: ArrayVecIntoIter<RawMove, MAX_LEGAL_MOVES>,
|
||||
}
|
||||
impl<'l> Iterator for MovesIntoIter<'l> {
|
||||
type Item = Move<'l>;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|raw| Move {
|
||||
position: self.position,
|
||||
raw,
|
||||
})
|
||||
}
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
impl<'l> FusedIterator for MovesIntoIter<'l> {}
|
||||
impl<'l> ExactSizeIterator for MovesIntoIter<'l> {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
impl<'l> IntoIterator for Moves<'l> {
|
||||
type Item = Move<'l>;
|
||||
type IntoIter = MovesIntoIter<'l>;
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
MovesIntoIter {
|
||||
position: self.position,
|
||||
iter: self.array.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct RawMove {
|
||||
kind: MoveType,
|
||||
role: Role,
|
||||
from: Square,
|
||||
to: Square,
|
||||
pub kind: MoveType,
|
||||
pub role: Role,
|
||||
pub from: Square,
|
||||
pub to: Square,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
enum MoveType {
|
||||
pub(crate) enum MoveType {
|
||||
CastleShort,
|
||||
CastleLong,
|
||||
KingMove,
|
||||
|
|
@ -784,15 +446,15 @@ enum MoveType {
|
|||
|
||||
impl RawMove {
|
||||
#[inline]
|
||||
fn from(&self) -> Square {
|
||||
pub fn from(&self) -> Square {
|
||||
self.from
|
||||
}
|
||||
#[inline]
|
||||
fn to(&self) -> Square {
|
||||
pub fn to(&self) -> Square {
|
||||
self.to
|
||||
}
|
||||
#[inline]
|
||||
fn role(&self) -> Role {
|
||||
pub fn role(&self) -> Role {
|
||||
match self.kind {
|
||||
MoveType::CastleShort | MoveType::CastleLong | MoveType::KingMove => Role::King,
|
||||
MoveType::PieceMove => self.role,
|
||||
|
|
@ -805,14 +467,14 @@ impl RawMove {
|
|||
}
|
||||
}
|
||||
#[inline]
|
||||
fn promotion(&self) -> Option<Role> {
|
||||
pub fn promotion(&self) -> Option<Role> {
|
||||
match self.kind {
|
||||
MoveType::PawnAdvancePromotion | MoveType::PawnAttackPromotion => Some(self.role),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn uci(&self) -> UciMove {
|
||||
pub fn uci(&self) -> UciMove {
|
||||
UciMove {
|
||||
from: self.from(),
|
||||
to: self.to(),
|
||||
|
|
@ -821,7 +483,7 @@ impl RawMove {
|
|||
}
|
||||
}
|
||||
|
||||
trait MoveGen {
|
||||
pub(crate) trait MoveGen {
|
||||
#[inline]
|
||||
fn roles(&self, _role: Role) -> bool {
|
||||
true
|
||||
|
|
@ -842,31 +504,13 @@ trait MoveGen {
|
|||
I: Iterator<Item = RawMove> + ExactSizeIterator;
|
||||
}
|
||||
|
||||
impl<'l> MoveGen for Moves<'l> {
|
||||
#[inline]
|
||||
fn is_check(&mut self) {
|
||||
self.is_check = true;
|
||||
}
|
||||
#[inline]
|
||||
fn en_passant_is_legal(&mut self) {
|
||||
self.en_passant_is_legal = true;
|
||||
}
|
||||
#[inline]
|
||||
fn extend<I>(&mut self, iter: I)
|
||||
where
|
||||
I: Iterator<Item = RawMove> + ExactSizeIterator,
|
||||
{
|
||||
iter.for_each(|raw| unsafe { self.array.push_unchecked(raw) });
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// SAFETY: The position must be valid.
|
||||
pub(crate) unsafe fn from_setup(setup: Setup) -> Self {
|
||||
Self(setup)
|
||||
}
|
||||
|
||||
fn generate_moves<T>(&self, moves: &mut T)
|
||||
pub(crate) fn generate_moves<T>(&self, moves: &mut T)
|
||||
where
|
||||
T: MoveGen,
|
||||
{
|
||||
|
|
@ -1197,7 +841,7 @@ impl Position {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn play_unchecked(&mut self, m: RawMove) {
|
||||
pub(crate) unsafe fn play_unchecked(&mut self, m: RawMove) {
|
||||
let Self(setup) = self;
|
||||
|
||||
setup.en_passant = OptionSquare::None;
|
||||
|
|
@ -1351,13 +995,13 @@ fn aux_play_castle(setup: &mut Setup, side: CastlingSide) {
|
|||
setup.castling_rights.unset(setup.turn, CastlingSide::Long);
|
||||
}
|
||||
|
||||
struct MateMoveGenImpl {
|
||||
is_check: bool,
|
||||
is_mate: bool,
|
||||
pub(crate) struct MateMoveGenImpl {
|
||||
pub is_check: bool,
|
||||
pub is_mate: bool,
|
||||
}
|
||||
impl MateMoveGenImpl {
|
||||
#[inline]
|
||||
fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
is_check: false,
|
||||
is_mate: true,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
//! Examples: *`e4`*, *`Qxd8#`*, *`O-O`*, *`h7h8=Q`*
|
||||
|
||||
use crate::board::*;
|
||||
use crate::moves::*;
|
||||
use crate::position::*;
|
||||
|
||||
/// **The standard algebraic notation of a move.**
|
||||
|
|
|
|||
|
|
@ -634,7 +634,7 @@ pub enum IllegalPositionReason {
|
|||
/// There is an impossible number of pieces.
|
||||
///
|
||||
/// Enforcing this enables to put an upper limit on the number of legal moves on any position,
|
||||
/// allowing to reduce the size of [`Moves`].
|
||||
/// allowing to reduce the size of [`Moves`](crate::moves::Moves).
|
||||
TooMuchMaterial = 64,
|
||||
/// The pawn that can be taken en passant is pinned diagonally to the playing king.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
//! Examples: *`e2e4`*, *`d1d8`*, *`e1g1`* (short castling), *`h7h8q`* (promotion)
|
||||
|
||||
use crate::board::*;
|
||||
use crate::moves::*;
|
||||
use crate::position::*;
|
||||
|
||||
/// **The UCI notation of a move.**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue