#![allow(clippy::inline_always)]
mod checks;
mod drops;
mod evasions;
mod generate;
mod pieces;
mod promotions;
mod recaptures;
mod types;
#[cfg(test)]
mod tests;
#[cfg(any(feature = "book", feature = "records"))]
use std::marker::PhantomData;
use crate::board::Position;
use crate::board::move_list::{Move32List, MoveList};
use crate::types::{Bitboard, Color, Move, Move32, Piece, PieceType, Square};
pub use checks::{
generate_checks, generate_checks_drops_part, generate_checks_drops_part_move32,
generate_checks_move32, generate_quiet_checks, generate_quiet_checks_move32,
};
pub use evasions::{generate_evasions, generate_evasions_move32};
pub use generate::{
generate_legal_all, generate_legal_all_into, generate_legal_evasions,
generate_legal_evasions_all, generate_legal_evasions_all_into, generate_legal_evasions_into,
generate_moves, generate_moves_into, generate_moves_to, generate_moves_to_into,
};
pub use recaptures::{
generate_recaptures, generate_recaptures_all, generate_recaptures_all_move32,
generate_recaptures_move32,
};
pub use types::{
CapturePlusPro, CapturePlusProAll, Captures, CapturesAll, Checks, ChecksAll, Evasions,
EvasionsAll, Legal, LegalAll, MoveGenType, NonEvasions, NonEvasionsAll, QuietChecks,
QuietChecksAll, Quiets, QuietsAll, QuietsProMinus, QuietsProMinusAll, Recaptures,
RecapturesAll,
};
pub(crate) trait ColorMarker {
const COLOR: Color;
const THEM: Color;
const IS_BLACK: bool;
}
pub(crate) struct Black;
pub(crate) struct White;
impl ColorMarker for Black {
const COLOR: Color = Color::BLACK;
const THEM: Color = Color::WHITE;
const IS_BLACK: bool = true;
}
impl ColorMarker for White {
const COLOR: Color = Color::WHITE;
const THEM: Color = Color::BLACK;
const IS_BLACK: bool = false;
}
pub trait MoveSink {
fn push_move(&mut self, mv: Move);
fn retain_unordered<F>(&mut self, f: F)
where
F: FnMut(Move) -> bool;
#[inline]
fn push_normal(&mut self, from: Square, to: Square, _piece: Piece) {
self.push_move(Move::normal_fast(from, to));
}
#[inline]
fn push_promotion(&mut self, from: Square, to: Square, _piece: Piece) {
self.push_move(Move::promotion_fast(from, to));
}
#[inline]
fn push_promotion_then_optional_normal(
&mut self,
from: Square,
to: Square,
piece: Piece,
include_normal: bool,
) {
self.push_promotion(from, to, piece);
if include_normal {
self.push_normal(from, to, piece);
}
}
#[inline]
fn push_promotion_then_normals_with_piece(
&mut self,
from: Square,
piece: Piece,
mut destinations: Bitboard,
include_normal: bool,
) {
while let Some(to) = destinations.pop_lsb() {
self.push_promotion_then_optional_normal(from, to, piece, include_normal);
}
}
#[inline]
fn push_normals_with_piece(&mut self, from: Square, piece: Piece, mut destinations: Bitboard) {
while let Some(to) = destinations.pop_lsb() {
self.push_normal(from, to, piece);
}
}
#[inline]
fn push_promotions_with_piece(
&mut self,
from: Square,
piece: Piece,
mut destinations: Bitboard,
) {
while let Some(to) = destinations.pop_lsb() {
self.push_promotion(from, to, piece);
}
}
#[inline]
fn push_drop(&mut self, pt: PieceType, to: Square, _color: Color) {
self.push_move(Move::drop_fast(pt, to));
}
#[inline]
fn push_drop_encoded(&mut self, base: u32, to: Square) {
self.push_move(Move::from_raw((base as u16) | to.raw() as u16));
}
#[inline]
fn push_moves_from(&mut self, from: Square, mut destinations: Bitboard) {
let base = (from.raw() as u16) << 7;
while let Some(to) = destinations.pop_lsb() {
let mv = Move::from_raw(base | to.raw() as u16);
self.push_move(mv);
}
}
#[inline]
fn push_promotions_from(&mut self, from: Square, mut destinations: Bitboard) {
let base = Move::MOVE_PROMOTE | ((from.raw() as u16) << 7);
while let Some(to) = destinations.pop_lsb() {
let mv = Move::from_raw(base | to.raw() as u16);
self.push_move(mv);
}
}
#[inline]
fn push_drops_to(&mut self, piece_type: PieceType, mut destinations: Bitboard) {
let base = u32::from(Move::MOVE_DROP | ((piece_type.to_index() as u16) << 7));
while let Some(to) = destinations.pop_lsb() {
self.push_drop_encoded(base, to);
}
}
}
impl MoveSink for MoveList {
#[inline]
fn push_move(&mut self, mv: Move) {
unsafe { self.push_unchecked(mv) };
}
#[inline]
fn retain_unordered<F>(&mut self, f: F)
where
F: FnMut(Move) -> bool,
{
self.retain_unordered(f);
}
#[inline]
fn push_promotion_then_optional_normal(
&mut self,
from: Square,
to: Square,
_piece: Piece,
include_normal: bool,
) {
unsafe { self.push_unchecked(Move::promotion_fast(from, to)) };
if include_normal {
unsafe { self.push_unchecked(Move::normal_fast(from, to)) };
}
}
#[inline]
fn push_promotion_then_normals_with_piece(
&mut self,
from: Square,
_piece: Piece,
mut destinations: Bitboard,
include_normal: bool,
) {
let promote_base = Move::MOVE_PROMOTE | ((from.raw() as u16) << 7);
let normal_base = (from.raw() as u16) << 7;
while let Some(to) = destinations.pop_lsb() {
let to_raw = to.raw() as u16;
unsafe { self.push_unchecked(Move::from_raw(promote_base | to_raw)) };
if include_normal {
unsafe { self.push_unchecked(Move::from_raw(normal_base | to_raw)) };
}
}
}
}
pub trait Move32Sink {
fn push_move32(&mut self, mv: Move32);
fn retain_unordered<F>(&mut self, f: F)
where
F: FnMut(Move32) -> bool;
}
impl Move32Sink for Move32List {
#[inline]
fn push_move32(&mut self, mv: Move32) {
unsafe { self.push_unchecked(mv) };
}
#[inline]
fn retain_unordered<F>(&mut self, f: F)
where
F: FnMut(Move32) -> bool,
{
self.retain_unordered(f);
}
}
#[cfg(any(feature = "book", feature = "records"))]
pub struct MoveListGen<T: MoveGenType> {
moves: MoveList,
_marker: PhantomData<T>,
}
#[cfg(any(feature = "book", feature = "records"))]
impl<T: MoveGenType + 'static> MoveListGen<T> {
#[must_use]
pub fn new(pos: &Position) -> Self {
let mut moves = MoveList::new();
debug_assert!(!T::IS_RECAPTURES, "MoveListGen::new is not for Recaptures");
generate_moves::<T>(pos, &mut moves);
Self { moves, _marker: PhantomData }
}
#[must_use]
pub fn new_with_target(pos: &Position, target_sq: Square) -> Self {
let mut moves = MoveList::new();
if T::IS_RECAPTURES {
generate_moves_to::<T>(pos, target_sq, &mut moves);
} else {
generate_moves::<T>(pos, &mut moves);
}
Self { moves, _marker: PhantomData }
}
pub fn iter(&self) -> impl Iterator<Item = &Move> {
self.moves.iter()
}
#[must_use]
pub const fn len(&self) -> usize {
self.moves.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.moves.is_empty()
}
#[must_use]
pub fn contains(&self, mv: Move) -> bool {
self.moves.iter().any(|&m| m == mv)
}
#[must_use]
pub fn at(&self, idx: usize) -> Move {
debug_assert!(idx < self.moves.len(), "index out of bounds");
self.moves.as_slice()[idx]
}
}
#[cfg(any(feature = "book", feature = "records"))]
impl<'a, T: MoveGenType> IntoIterator for &'a MoveListGen<T> {
type Item = &'a Move;
type IntoIter = std::slice::Iter<'a, Move>;
fn into_iter(self) -> Self::IntoIter {
self.moves.as_slice().iter()
}
}
struct Move32SinkAdapter<'a, S> {
pos: &'a Position,
sink: &'a mut S,
}
#[inline(always)]
fn make_move32_fast(mv_raw: u16, piece_after: Piece) -> Move32 {
Move32::from_raw((u32::from(piece_after.raw() as u8 & 0x1f) << 16) | u32::from(mv_raw))
}
impl<S: Move32Sink> MoveSink for Move32SinkAdapter<'_, S> {
#[inline]
fn push_move(&mut self, mv: Move) {
let mv32 = unsafe { self.pos.move32_from_move_fast(mv) };
self.sink.push_move32(mv32);
}
#[inline]
fn push_normal(&mut self, from: Square, to: Square, piece: Piece) {
let mv32 = make_move32_fast(Move::normal_fast(from, to).raw(), piece);
self.sink.push_move32(mv32);
}
#[inline]
fn push_promotion(&mut self, from: Square, to: Square, piece: Piece) {
let mv32 = make_move32_fast(Move::promotion_fast(from, to).raw(), piece.promote());
self.sink.push_move32(mv32);
}
#[inline]
fn push_promotion_then_optional_normal(
&mut self,
from: Square,
to: Square,
piece: Piece,
include_normal: bool,
) {
let to_raw = to.raw() as u32;
let promote_base = (u32::from(piece.promote().raw() as u8 & 0x1f) << 16)
| u32::from(Move::MOVE_PROMOTE)
| ((from.raw() as u32) << 7);
let mv32 = Move32::from_raw(promote_base | to_raw);
self.sink.push_move32(mv32);
if include_normal {
let normal_base =
(u32::from(piece.raw() as u8 & 0x1f) << 16) | ((from.raw() as u32) << 7);
let mv32 = Move32::from_raw(normal_base | to_raw);
self.sink.push_move32(mv32);
}
}
#[inline]
fn push_promotion_then_normals_with_piece(
&mut self,
from: Square,
piece: Piece,
mut destinations: Bitboard,
include_normal: bool,
) {
let promote_base = (u32::from(piece.promote().raw() as u8 & 0x1f) << 16)
| u32::from(Move::MOVE_PROMOTE)
| ((from.raw() as u32) << 7);
let normal_base = (u32::from(piece.raw() as u8 & 0x1f) << 16) | ((from.raw() as u32) << 7);
while let Some(to) = destinations.pop_lsb() {
let to_raw = to.raw() as u32;
let mv32 = Move32::from_raw(promote_base | to_raw);
self.sink.push_move32(mv32);
if include_normal {
let mv32 = Move32::from_raw(normal_base | to_raw);
self.sink.push_move32(mv32);
}
}
}
#[inline]
fn push_normals_with_piece(&mut self, from: Square, piece: Piece, mut destinations: Bitboard) {
let base = (u32::from(piece.raw() as u8 & 0x1f) << 16) | ((from.raw() as u32) << 7);
while let Some(to) = destinations.pop_lsb() {
let mv32 = Move32::from_raw(base | to.raw() as u32);
self.sink.push_move32(mv32);
}
}
#[inline]
fn push_promotions_with_piece(
&mut self,
from: Square,
piece: Piece,
mut destinations: Bitboard,
) {
let promoted = piece.promote();
let base = (u32::from(promoted.raw() as u8 & 0x1f) << 16)
| u32::from(Move::MOVE_PROMOTE)
| ((from.raw() as u32) << 7);
while let Some(to) = destinations.pop_lsb() {
let mv32 = Move32::from_raw(base | to.raw() as u32);
self.sink.push_move32(mv32);
}
}
#[inline]
fn push_drop(&mut self, pt: PieceType, to: Square, color: Color) {
let piece = Piece::from_parts(color, pt);
let base = (u32::from(piece.raw() as u8 & 0x1f) << 16)
| u32::from(Move::MOVE_DROP | ((pt.to_index() as u16) << 7));
let mv32 = Move32::from_raw(base | to.raw() as u32);
self.sink.push_move32(mv32);
}
#[inline]
fn push_drop_encoded(&mut self, base: u32, to: Square) {
let mv32 = Move32::from_raw(base | to.raw() as u32);
self.sink.push_move32(mv32);
}
#[inline]
fn retain_unordered<F>(&mut self, mut f: F)
where
F: FnMut(Move) -> bool,
{
self.sink.retain_unordered(|mv32| f(mv32.to_move()));
}
}
pub fn generate_moves_move32_into<T: MoveGenType + 'static, S: Move32Sink>(
pos: &Position,
sink: &mut S,
) {
let mut adapter = Move32SinkAdapter { pos, sink };
generate::generate_moves_into::<T, _>(pos, &mut adapter);
}
pub fn generate_moves_move32<T: MoveGenType + 'static>(pos: &Position, list: &mut Move32List) {
generate_moves_move32_into::<T, _>(pos, list);
}
pub fn generate_moves_to_move32_into<T: MoveGenType + 'static, S: Move32Sink>(
pos: &Position,
target_sq: Square,
sink: &mut S,
) {
let mut adapter = Move32SinkAdapter { pos, sink };
generate::generate_moves_to_into::<T, _>(pos, target_sq, &mut adapter);
}
pub fn generate_moves_to_move32<T: MoveGenType + 'static>(
pos: &Position,
target_sq: Square,
list: &mut Move32List,
) {
generate_moves_to_move32_into::<T, _>(pos, target_sq, list);
}
#[inline]
fn retain_generated_legal_move32_for_color<C: ColorMarker>(
pos: &Position,
sink: &mut impl Move32Sink,
) {
let us = C::COLOR;
let king_sq = pos.king_square(us);
if king_sq.is_none() {
return;
}
let blockers = pos.blockers_for_king(us);
sink.retain_unordered(|mv32| {
if mv32.is_drop() {
return true;
}
let from = mv32.from_sq();
if from != king_sq && !blockers.test(from) {
return true;
}
if from == king_sq {
if C::IS_BLACK {
!pos.is_attacked_by_color_with_king_for::<false>(mv32.to_sq(), king_sq)
} else {
!pos.is_attacked_by_color_with_king_for::<true>(mv32.to_sq(), king_sq)
}
} else {
Bitboard::is_aligned(from, mv32.to_sq(), king_sq)
}
});
}
#[inline]
fn generate_legal_all_move32_into_non_evasions_for_color<C: ColorMarker>(
pos: &Position,
sink: &mut impl Move32Sink,
) {
let us = C::COLOR;
let bb = pos.bitboards();
let our_pieces = bb.color_pieces(us);
let occupied = bb.occupied();
let target = !our_pieces;
let pawn_target = target;
{
let mut adapter = Move32SinkAdapter { pos, sink };
pieces::generate_pawn_moves::<NonEvasionsAll, C>(pos, &mut adapter, pawn_target);
pieces::generate_lance_moves::<NonEvasionsAll, C>(pos, &mut adapter, target, occupied);
pieces::generate_knight_moves::<NonEvasionsAll, C>(pos, &mut adapter, target);
pieces::generate_silver_moves::<NonEvasionsAll, C>(pos, &mut adapter, target);
pieces::generate_br_moves::<NonEvasionsAll, C>(pos, &mut adapter, target, occupied);
pieces::generate_gold_hdk_moves::<NonEvasionsAll, C>(pos, &mut adapter, target, occupied);
}
retain_generated_legal_move32_for_color::<C>(pos, sink);
let mut adapter = Move32SinkAdapter { pos, sink };
drops::generate_drops_color::<NonEvasionsAll, C>(pos, &mut adapter);
}
#[inline]
fn generate_legal_evasions_move32_into_for_color<T: MoveGenType + 'static, C: ColorMarker>(
pos: &Position,
sink: &mut impl Move32Sink,
) {
let mut adapter = Move32SinkAdapter { pos, sink };
evasions::generate_evasions_for_color::<T, C>(pos, &mut adapter);
retain_generated_legal_move32_for_color::<C>(pos, sink);
}
pub fn generate_legal_evasions_move32_into<S: Move32Sink>(pos: &Position, sink: &mut S) {
debug_assert!(!pos.checkers().is_empty(), "generate_legal_evasions_move32_into expects check");
match pos.turn() {
Color::BLACK => generate_legal_evasions_move32_into_for_color::<Evasions, Black>(pos, sink),
Color::WHITE => generate_legal_evasions_move32_into_for_color::<Evasions, White>(pos, sink),
}
}
pub fn generate_legal_evasions_move32(pos: &Position, list: &mut Move32List) {
generate_legal_evasions_move32_into(pos, list);
}
pub fn generate_legal_evasions_all_move32_into<S: Move32Sink>(pos: &Position, sink: &mut S) {
debug_assert!(
!pos.checkers().is_empty(),
"generate_legal_evasions_all_move32_into expects check"
);
match pos.turn() {
Color::BLACK => {
generate_legal_evasions_move32_into_for_color::<EvasionsAll, Black>(pos, sink)
}
Color::WHITE => {
generate_legal_evasions_move32_into_for_color::<EvasionsAll, White>(pos, sink)
}
}
}
pub fn generate_legal_evasions_all_move32(pos: &Position, list: &mut Move32List) {
generate_legal_evasions_all_move32_into(pos, list);
}
pub fn generate_legal_all_move32_into<S: Move32Sink>(pos: &Position, sink: &mut S) {
let in_check = !pos.checkers().is_empty();
match pos.turn() {
Color::BLACK => {
if in_check {
generate_legal_evasions_all_move32_into(pos, sink);
} else {
generate_legal_all_move32_into_non_evasions_for_color::<Black>(pos, sink);
}
}
Color::WHITE => {
if in_check {
generate_legal_evasions_all_move32_into(pos, sink);
} else {
generate_legal_all_move32_into_non_evasions_for_color::<White>(pos, sink);
}
}
}
}
#[inline]
fn generate_legal_all_move32_non_evasions_for_color<C: ColorMarker>(
pos: &Position,
list: &mut Move32List,
) {
let us = C::COLOR;
let bb = pos.bitboards();
let our_pieces = bb.color_pieces(us);
let occupied = bb.occupied();
let target = !our_pieces;
let pawn_target = target;
{
let mut adapter = Move32SinkAdapter { pos, sink: list };
pieces::generate_pawn_moves::<NonEvasionsAll, C>(pos, &mut adapter, pawn_target);
pieces::generate_lance_moves::<NonEvasionsAll, C>(pos, &mut adapter, target, occupied);
pieces::generate_knight_moves::<NonEvasionsAll, C>(pos, &mut adapter, target);
pieces::generate_silver_moves::<NonEvasionsAll, C>(pos, &mut adapter, target);
pieces::generate_br_moves::<NonEvasionsAll, C>(pos, &mut adapter, target, occupied);
pieces::generate_gold_hdk_moves::<NonEvasionsAll, C>(pos, &mut adapter, target, occupied);
}
retain_generated_legal_move32_for_color::<C>(pos, list);
drops::generate_drops_color_move32::<NonEvasionsAll, C>(pos, list);
}
pub fn generate_legal_all_move32(pos: &Position, list: &mut Move32List) {
let in_check = !pos.checkers().is_empty();
match pos.turn() {
Color::BLACK => {
if in_check {
generate_legal_evasions_all_move32(pos, list);
} else {
generate_legal_all_move32_non_evasions_for_color::<Black>(pos, list);
}
}
Color::WHITE => {
if in_check {
generate_legal_evasions_all_move32(pos, list);
} else {
generate_legal_all_move32_non_evasions_for_color::<White>(pos, list);
}
}
}
}