use crate::array_vec::*;
use crate::bitboard::*;
use crate::board::*;
use crate::lookup;
use crate::san::*;
use crate::setup::*;
use crate::uci::*;
use core::convert::Infallible;
use core::iter::{ExactSizeIterator, FusedIterator};
use core::ops::ControlFlow;
const PAWN: u8 = Role::Pawn as u8;
const KNIGHT: u8 = Role::Knight as u8;
const BISHOP: u8 = Role::Bishop as u8;
const ROOK: u8 = Role::Rook as u8;
const QUEEN: u8 = Role::Queen as u8;
const KING: u8 = Role::King as u8;
#[must_use]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Position(Setup);
impl Position {
#[inline]
pub fn new() -> Self {
let setup = Setup::from_bitboards_inner([
Bitboard(0x2CFF00000000FF2C),
Bitboard(0x7600000000000076),
Bitboard(0x9900000000000099),
Bitboard(0xFFFF000000000000),
Bitboard(0x8100000000000081),
]);
Self::from_setup_inner(setup)
}
#[inline]
pub fn legal_moves<'l>(&'l self) -> Moves<'l> {
fn aux<'l>(position: &'l Position, list: &mut RawMoves) {
let ControlFlow::Continue(()) = position.generate_moves(list);
}
let mut list = RawMoves {
is_check: false,
en_passant_is_legal: false,
array: ArrayVec::new(),
};
aux(self, &mut list);
Moves {
position: self,
list,
}
}
#[must_use]
pub fn count_legal_moves(&self) -> usize {
struct CountMoves(usize);
impl MoveGen<Infallible> for CountMoves {
#[inline]
fn extend<S: MoveSet>(&mut self, s: S) -> ControlFlow<Infallible> {
self.0 += s.len();
ControlFlow::Continue(())
}
}
let mut count = CountMoves(0);
let ControlFlow::Continue(()) = self.generate_moves(&mut count);
count.0
}
#[must_use]
pub fn is_check(&self) -> bool {
struct MoveGenImpl(Bitboard);
impl MoveGen<core::convert::Infallible> for MoveGenImpl {
#[inline]
fn checkers(&mut self, b: Bitboard) {
self.0 = b;
}
}
let mut m = MoveGenImpl(Bitboard::new());
let ControlFlow::Continue(()) = self.generate_moves(&mut m);
!m.0.is_empty()
}
#[must_use]
pub fn is_mate(&self) -> bool {
struct MoveGenImpl;
impl MoveGen<()> for MoveGenImpl {
#[inline]
fn extend<S: MoveSet>(&mut self, s: S) -> ControlFlow<()> {
match s.is_empty() {
true => ControlFlow::Continue(()),
false => ControlFlow::Break(()),
}
}
}
match self.generate_moves(&mut MoveGenImpl) {
ControlFlow::Continue(()) => true,
ControlFlow::Break(()) => false,
}
}
#[must_use]
pub fn en_passant_is_legal(&self) -> bool {
struct MoveGenImpl(bool);
impl MoveGen<core::convert::Infallible> for MoveGenImpl {
#[inline]
fn en_passant(&mut self) {
self.0 = true;
}
}
let mut m = MoveGenImpl(false);
let ControlFlow::Continue(()) = self.generate_moves(&mut m);
m.0
}
#[inline]
pub fn remove_en_passant_target_square(self) -> Self {
let setup = self.into_setup().set_en_passant_target_square(None);
Self::from_setup_inner(setup)
}
#[inline]
pub fn remove_castling_rights(self, color: Color, castle: Castle) -> Self {
Self::from_setup_inner(self.into_setup().set_castling_rights(color, castle, false))
}
#[inline]
pub fn pass(&self) -> Option<Self> {
if self.is_check() {
None
} else {
Some(Self::from_setup_inner(
self.clone()
.into_setup()
.switch_turn()
.set_en_passant_target_square(None),
))
}
}
#[inline]
pub fn mirror(&self) -> Self {
Self::from_setup_inner(self.as_setup().mirror())
}
pub fn perft(&self, depth: usize) -> u128 {
fn aux(position: &Position, depth: usize) -> u128 {
match depth.checked_sub(1) {
None => position.count_legal_moves() as u128,
Some(depth) => position
.legal_moves()
.into_iter()
.map(|m| aux(&m.make(), depth))
.sum(),
}
}
match depth.checked_sub(1) {
None => 1,
Some(depth) => aux(self, depth),
}
}
#[inline]
pub unsafe fn play_unchecked(&self, m: RawMove) -> Self {
self.play_inner(m)
}
#[inline]
pub fn into_setup(self) -> Setup {
self.0
}
#[inline]
pub(crate) fn from_setup_inner(setup: Setup) -> Self {
debug_assert!(setup.illegal_reasons().is_empty());
Self(setup)
}
#[inline]
pub(crate) fn play_inner(&self, m: RawMove) -> Self {
match self.turn() {
Color::White => play_move::<0>(self, m),
Color::Black => play_move::<1>(self, m),
}
}
#[inline]
pub(crate) fn candidates(
&self,
role: Role,
from: Bitboard,
to: Square,
promotion: Option<Role>,
) -> Candidates {
struct MoveGenImpl<const ROLE: u8> {
kind: MoveType,
to: Square,
mask: Bitboard,
}
impl<const ROLE: u8> MoveGen<Infallible> for MoveGenImpl<ROLE> {
const ROLE: u8 = ROLE;
#[inline]
fn extend<S: MoveSet>(&mut self, s: S) -> ControlFlow<Infallible> {
let mut x = false;
s.filter(self.to).for_each(|raw| {
assert!(!x || raw.promotion.is_some());
x = true;
self.kind = raw.kind;
self.mask.insert(raw.from);
});
ControlFlow::Continue(())
}
}
#[inline]
fn aux<const ROLE: u8>(position: &Position, to: Square) -> (MoveType, Bitboard) {
let mut m = MoveGenImpl::<ROLE> {
kind: MoveType::KingMove,
to,
mask: Bitboard::new(),
};
let ControlFlow::Continue(()) = position.generate_moves(&mut m);
(m.kind, m.mask)
}
let [a, b, c, _, _] = self.bitboards();
let ((kind, mask1), mask2) = match role {
Role::Pawn => (aux::<PAWN>(self, to), a & !b & !c),
Role::Knight => (aux::<KNIGHT>(self, to), !a & b & !c),
Role::Bishop => (aux::<BISHOP>(self, to), a & b & !c),
Role::Rook => (aux::<ROOK>(self, to), !a & !b & c),
Role::Queen => (aux::<QUEEN>(self, to), a & !b & c),
Role::King => (aux::<KING>(self, to), !a & b & c),
};
let mask = mask1 & mask2;
let from = match (role, to.rank(), promotion) {
(_, _, Some(Role::Pawn | Role::King)) => Bitboard::new(),
(Role::Pawn, Rank::First | Rank::Eighth, None) => Bitboard::new(),
(Role::Pawn, Rank::First | Rank::Eighth, _) => from & mask,
(Role::Pawn, _, Some(_)) => Bitboard::new(),
(Role::Pawn, _, _) => from & mask,
(_, _, Some(_)) => Bitboard::new(),
_ => from & mask,
};
Candidates {
kind,
from,
to,
promotion,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct RawMoves {
pub is_check: bool,
pub en_passant_is_legal: bool,
pub array: ArrayVec<RawMove, 218>,
}
impl MoveGen<Infallible> for RawMoves {
#[inline]
fn checkers(&mut self, b: Bitboard) {
self.is_check = !b.is_empty();
}
#[inline]
fn en_passant(&mut self) {
self.en_passant_is_legal = true;
}
#[inline]
fn extend<S: MoveSet>(&mut self, s: S) -> ControlFlow<Infallible> {
s.for_each(|raw| self.array.push(raw));
ControlFlow::Continue(())
}
}
#[must_use]
#[derive(Clone, Copy)]
pub struct Move<'l> {
position: &'l Position,
raw: RawMove,
}
impl<'l> Move<'l> {
#[inline]
pub fn position(self) -> &'l Position {
self.position
}
#[inline]
pub fn raw(self) -> RawMove {
self.raw
}
#[inline]
pub fn make(self) -> Position {
self.position().play_inner(self.raw())
}
#[inline]
pub fn role(self) -> Role {
self.position().as_setup().role(self.from()).unwrap()
}
#[inline]
pub fn from(self) -> Square {
self.raw().from()
}
#[inline]
pub fn to(self) -> Square {
self.raw().to()
}
#[inline]
pub fn promotion(self) -> Option<Role> {
self.raw().promotion()
}
#[inline]
pub fn is_capture(self) -> bool {
let [a, b, c, _, _] = self.position().bitboards();
self.raw().kind == MoveType::EnPassant || (a | b | c).contains(self.to())
}
#[inline]
pub fn captured(self) -> Option<Role> {
match self.raw().kind {
MoveType::EnPassant => Some(Role::Pawn),
_ => self.position().as_setup().role(self.to()),
}
}
#[inline]
pub fn castle(self) -> Option<Castle> {
match self.raw().kind {
MoveType::CastleShort => Some(Castle::Short),
MoveType::CastleLong => Some(Castle::Long),
_ => None,
}
}
#[inline]
pub fn to_uci(self) -> UciMove {
self.raw().to_uci()
}
pub fn to_san(self) -> San {
San {
inner: match self.castle() {
Some(castle) => SanInner::Castle(castle),
None => {
let role = self.role();
let from = self.from();
let file = from.file();
let rank = from.rank();
let capture = self.is_capture();
let target = self.to();
let promotion = self.promotion();
if role == Role::King {
SanInner::Normal {
role,
file: None,
rank: None,
capture,
target,
promotion,
}
} else if role == Role::Pawn {
SanInner::Normal {
role,
file: capture.then_some(file),
rank: None,
capture,
target,
promotion,
}
} else {
let candidates = self
.position()
.candidates(role, !from.bitboard(), target, promotion)
.bitboard();
if candidates.is_empty() {
SanInner::Normal {
role,
file: None,
rank: None,
capture,
target,
promotion,
}
} else if (candidates & file.bitboard()).is_empty() {
SanInner::Normal {
role,
file: Some(file),
rank: None,
capture,
target,
promotion,
}
} else if (candidates & rank.bitboard()).is_empty() {
SanInner::Normal {
role,
file: None,
rank: Some(rank),
capture,
target,
promotion,
}
} else {
SanInner::Normal {
role,
file: Some(file),
rank: Some(rank),
capture,
target,
promotion,
}
}
}
}
},
suffix: {
let position = self.make();
if position.is_check() {
if position.is_mate() {
Some(SanSuffix::Checkmate)
} else {
Some(SanSuffix::Check)
}
} else {
None
}
},
}
}
}
#[must_use]
#[derive(Debug, Clone)]
pub struct Moves<'l> {
position: &'l Position,
list: RawMoves,
}
impl<'l> Moves<'l> {
#[inline]
pub fn position(&self) -> &'l Position {
self.position
}
#[inline]
pub fn iter(&'l self) -> MovesIter<'l> {
MovesIter {
inner: InnerMovesIter {
position: self.position(),
iter: self.list().array.iter(),
},
}
}
#[inline]
pub fn len(&self) -> usize {
self.list().array.len()
}
#[inline]
pub fn is_check(&self) -> bool {
self.list().is_check
}
#[inline]
pub fn is_mate(&self) -> bool {
self.list().array.len() == 0
}
#[inline]
pub fn en_passant_is_legal(&self) -> bool {
self.list().en_passant_is_legal
}
#[inline]
pub fn get(&self, index: usize) -> Option<Move<'l>> {
self.list()
.array
.as_slice()
.get(index)
.copied()
.map(move |raw| raw.to_move_inner(self.position()))
}
#[inline]
pub fn sort(&mut self) {
self.list.array.as_mut_slice().sort_unstable()
}
#[inline]
pub fn sort_by<F>(&mut self, mut compare: F)
where
F: FnMut(Move, Move) -> core::cmp::Ordering,
{
self.list.array.as_mut_slice().sort_by(|a, b| {
let a = a.to_move_inner(&self.position);
let b = b.to_move_inner(&self.position);
compare(a, b)
});
}
#[inline]
pub fn sort_unstable_by<F>(&mut self, mut compare: F)
where
F: FnMut(Move, Move) -> core::cmp::Ordering,
{
self.list.array.as_mut_slice().sort_unstable_by(|a, b| {
let a = a.to_move_inner(&self.position);
let b = b.to_move_inner(&self.position);
compare(a, b)
});
}
#[inline]
pub fn select_nth_unstable(&mut self, index: usize) -> Move<'l> {
let (_, raw, _) = self.list.array.as_mut_slice().select_nth_unstable(index);
raw.to_move_inner(&self.position)
}
#[inline]
pub fn select_nth_unstable_by<F>(&mut self, index: usize, mut compare: F) -> Move<'l>
where
F: FnMut(Move, Move) -> core::cmp::Ordering,
{
let (_, raw, _) = self
.list
.array
.as_mut_slice()
.select_nth_unstable_by(index, |a, b| {
let a = a.to_move_inner(&self.position);
let b = b.to_move_inner(&self.position);
compare(a, b)
});
raw.to_move_inner(&self.position)
}
#[inline]
pub(crate) fn list(&'l self) -> &'l RawMoves {
&self.list
}
#[inline]
pub(crate) fn into_list(self) -> RawMoves {
self.list
}
}
#[derive(Debug, Clone)]
pub struct MovesIter<'l> {
inner: InnerMovesIter<'l, ArrayVecIter<'l, RawMove, 218>>,
}
impl<'l> Iterator for MovesIter<'l> {
type Item = Move<'l>;
crate::iterator::mk!();
}
impl<'l> FusedIterator for MovesIter<'l> {}
impl<'l> ExactSizeIterator for MovesIter<'l> {
#[inline]
fn len(&self) -> usize {
self.inner.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()
}
}
#[derive(Debug, Clone)]
pub struct MovesIntoIter<'l> {
inner: InnerMovesIter<'l, ArrayVecIntoIter<RawMove, 218>>,
}
impl<'l> Iterator for MovesIntoIter<'l> {
type Item = Move<'l>;
crate::iterator::mk!();
}
impl<'l> FusedIterator for MovesIntoIter<'l> {}
impl<'l> ExactSizeIterator for MovesIntoIter<'l> {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
}
impl<'l> IntoIterator for Moves<'l> {
type Item = Move<'l>;
type IntoIter = MovesIntoIter<'l>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
let position = self.position();
let iter = self.into_list().array.into_iter();
let inner = InnerMovesIter { position, iter };
MovesIntoIter { inner }
}
}
#[derive(Debug, Clone)]
struct InnerMovesIter<'l, I: Iterator<Item = RawMove>> {
position: &'l Position,
iter: I,
}
impl<'l, I> Iterator for InnerMovesIter<'l, I>
where
I: Iterator<Item = RawMove>,
{
type Item = Move<'l>;
#[inline]
fn next(&mut self) -> Option<Move<'l>> {
self.iter.next().map(|raw| raw.to_move_inner(self.position))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
#[inline]
fn count(self) -> usize
where
Self: Sized,
{
self.iter.count()
}
#[inline]
fn last(self) -> Option<Move<'l>>
where
Self: Sized,
{
self.iter.last().map(|raw| raw.to_move_inner(self.position))
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Move<'l>> {
self.iter.nth(n).map(|raw| raw.to_move_inner(self.position))
}
#[inline]
fn for_each<F>(self, mut f: F)
where
Self: Sized,
F: FnMut(Move<'l>),
{
self.iter
.for_each(|raw| f(raw.to_move_inner(self.position)))
}
#[inline]
fn fold<B, F>(self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Move<'l>) -> B,
{
self.iter
.fold(init, |acc, raw| f(acc, raw.to_move_inner(self.position)))
}
}
impl<'l, I> FusedIterator for InnerMovesIter<'l, I> where I: Iterator<Item = RawMove> + FusedIterator
{}
impl<'l, I> ExactSizeIterator for InnerMovesIter<'l, I>
where
I: Iterator<Item = RawMove> + ExactSizeIterator,
{
#[inline]
fn len(&self) -> usize {
self.iter.len()
}
}
pub(crate) struct Candidates {
kind: MoveType,
from: Bitboard,
to: Square,
promotion: Option<Role>,
}
impl Candidates {
#[inline]
pub fn is_empty(&self) -> bool {
self.from.is_empty()
}
#[inline]
pub fn bitboard(&self) -> Bitboard {
self.from
}
}
impl Iterator for Candidates {
type Item = RawMove;
#[inline]
fn next(&mut self) -> Option<RawMove> {
let Self {
kind,
from,
to,
promotion,
} = self;
Some(RawMove {
kind: *kind,
from: from.next()?,
to: *to,
promotion: *promotion,
})
}
}
impl core::fmt::Debug for Position {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
f.debug_tuple("Position")
.field(&TextRecord(self.as_setup()))
.finish()
}
}
impl Default for Position {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RawMove {
pub(crate) kind: MoveType,
pub(crate) from: Square,
pub(crate) to: Square,
pub(crate) promotion: Option<Role>,
}
impl core::fmt::Debug for RawMove {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}({:?})", self.to_uci(), self.kind)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub(crate) enum MoveType {
CastleShort,
CastleLong,
KingMove,
KnightMove,
DiagonalMove,
LineMove,
PawnAdvance,
PawnAttack,
PawnAdvancePromotion,
PawnAttackPromotion,
PawnDoubleAdvance,
EnPassant,
}
impl RawMove {
#[inline]
pub fn from(self) -> Square {
self.from
}
#[inline]
pub fn to(self) -> Square {
self.to
}
#[inline]
pub fn promotion(self) -> Option<Role> {
self.promotion
}
#[inline]
pub unsafe fn to_move_unchecked<'l>(self, position: &'l Position) -> Move<'l> {
self.to_move_inner(position)
}
#[inline]
pub(crate) fn to_move_inner<'l>(self, position: &'l Position) -> Move<'l> {
Move {
position,
raw: self,
}
}
#[inline]
pub fn to_uci(self) -> UciMove {
UciMove {
from: self.from(),
to: self.to(),
promotion: self.promotion(),
}
}
}
struct MoveSetCastleShort {
from: Square,
to: Square,
}
#[rustfmt::skip]
impl MoveSet for MoveSetCastleShort {
#[inline]
fn len(&self) -> usize { 1 }
#[inline]
fn is_empty(&self) -> bool { false }
#[inline]
fn for_each<F: FnMut(RawMove)>(self, mut f: F) {
let Self { from, to } = self;
f(RawMove { kind: MoveType::CastleShort, from, to, promotion: None });
}
#[inline]
fn filter(self, to: Square) -> impl MoveSet { (self.to == to).then_some(self) }
}
struct MoveSetCastleLong {
from: Square,
to: Square,
}
#[rustfmt::skip]
impl MoveSet for MoveSetCastleLong{
#[inline]
fn len(&self) -> usize { 1 }
#[inline]
fn is_empty(&self) -> bool { false }
#[inline]
fn for_each<F: FnMut(RawMove)>(self, mut f: F) {
let Self { from, to } = self;
f(RawMove { kind: MoveType::CastleLong, from, to, promotion: None });
}
#[inline]
fn filter(self, to: Square) -> impl MoveSet { (self.to == to).then_some(self) }
}
struct MoveSetNormal {
kind: MoveType,
from: Square,
to: Bitboard,
}
#[rustfmt::skip]
impl MoveSet for MoveSetNormal {
#[inline]
fn len(&self) -> usize { self.to.len() }
#[inline]
fn is_empty(&self) -> bool { self.to.is_empty() }
#[inline]
fn for_each<F: FnMut(RawMove)>(self, mut f: F) {
let Self { kind, from, to } = self;
to.for_each(|to| f(RawMove { kind, from, to, promotion: None }));
}
#[inline]
fn filter(mut self, to: Square) -> impl MoveSet { self.to &= to.bitboard(); self }
}
struct MoveSetPawnAttack {
from: Direction,
to: Bitboard,
}
#[rustfmt::skip]
impl MoveSet for MoveSetPawnAttack {
#[inline]
fn len(&self) -> usize {
let mask = Rank::First.bitboard() | Rank::Eighth.bitboard();
self.to.len() + (self.to & mask).len() * 3
}
#[inline]
fn is_empty(&self) -> bool { self.to.is_empty() }
#[inline]
fn for_each<F: FnMut(RawMove)>(self, mut f: F) {
let Self { from, to } = self;
let mask = Rank::First.bitboard() | Rank::Eighth.bitboard();
(to & !mask).for_each(|to| f(RawMove { kind: MoveType::PawnAttack, from: to.trans_unchecked(from) , to, promotion: None }));
(to & mask).for_each(|to| f(RawMove { kind: MoveType::PawnAttackPromotion, from: to.trans_unchecked(from), to, promotion: Some(Role::Queen) }));
(to & mask).for_each(|to| f(RawMove { kind: MoveType::PawnAttackPromotion, from: to.trans_unchecked(from), to, promotion: Some(Role::Rook) }));
(to & mask).for_each(|to| f(RawMove { kind: MoveType::PawnAttackPromotion, from: to.trans_unchecked(from), to, promotion: Some(Role::Bishop) }));
(to & mask).for_each(|to| f(RawMove { kind: MoveType::PawnAttackPromotion, from: to.trans_unchecked(from), to, promotion: Some(Role::Knight) }));
}
#[inline]
fn filter(mut self, to: Square) -> impl MoveSet { self.to &= to.bitboard(); self }
}
struct MoveSetPawnAdvance {
from: Direction,
to: Bitboard,
}
#[rustfmt::skip]
impl MoveSet for MoveSetPawnAdvance {
#[inline]
fn len(&self) -> usize {
let mask = Rank::First.bitboard() | Rank::Eighth.bitboard();
self.to.len() + (self.to & mask).len() * 3
}
#[inline]
fn is_empty(&self) -> bool { self.to.is_empty() }
#[inline]
fn for_each<F: FnMut(RawMove)>(self, mut f: F) {
let Self { from, to } = self;
let mask = Rank::First.bitboard() | Rank::Eighth.bitboard();
(to & !mask).for_each(|to| f(RawMove { kind: MoveType::PawnAdvance, from: to.trans_unchecked(from), to, promotion: None }));
(to & mask).for_each(|to| f(RawMove { kind: MoveType::PawnAdvancePromotion, from: to.trans_unchecked(from), to, promotion: Some(Role::Queen) }));
(to & mask).for_each(|to| f(RawMove { kind: MoveType::PawnAdvancePromotion, from: to.trans_unchecked(from), to, promotion: Some(Role::Rook) }));
(to & mask).for_each(|to| f(RawMove { kind: MoveType::PawnAdvancePromotion, from: to.trans_unchecked(from), to, promotion: Some(Role::Bishop) }));
(to & mask).for_each(|to| f(RawMove { kind: MoveType::PawnAdvancePromotion, from: to.trans_unchecked(from), to, promotion: Some(Role::Knight) }));
}
#[inline]
fn filter(mut self, to: Square) -> impl MoveSet { self.to &= to.bitboard(); self }
}
struct MoveSetPawnDoubleAdvance {
from: Direction,
to: Bitboard,
}
#[rustfmt::skip]
impl MoveSet for MoveSetPawnDoubleAdvance {
#[inline]
fn len(&self) -> usize { self.to.len() }
#[inline]
fn is_empty(&self) -> bool { self.to.is_empty() }
#[inline]
fn for_each<F: FnMut(RawMove)>(self, mut f: F) {
let Self { from, to } = self;
to.for_each(|to| f(RawMove { kind: MoveType::PawnDoubleAdvance, from: to.trans_unchecked(from).trans_unchecked(from), to, promotion: None }));
}
#[inline]
fn filter(mut self, to: Square) -> impl MoveSet { self.to &= to.bitboard(); self }
}
struct MoveSetEnPassant {
from: Square,
to: Square,
}
#[rustfmt::skip]
impl MoveSet for MoveSetEnPassant {
#[inline]
fn len(&self) -> usize { 1 }
#[inline]
fn is_empty(&self) -> bool { false }
#[inline]
fn for_each<F: FnMut(RawMove)>(self, mut f: F) {
let Self { from, to } = self;
f(RawMove { kind: MoveType::EnPassant, from, to, promotion: None });
}
#[inline]
fn filter(self, to: Square) -> impl MoveSet { (self.to == to).then_some(self) }
}
#[rustfmt::skip]
impl<T: MoveSet> MoveSet for Option<T> {
#[inline]
fn len(&self) -> usize { self.as_ref().map(MoveSet::len).unwrap_or(0) }
#[inline]
fn is_empty(&self) -> bool { self.as_ref().map(MoveSet::is_empty).unwrap_or(true) }
#[inline]
fn for_each<F: FnMut(RawMove)>(self, f: F) { self.map(|s| s.for_each(f)); }
#[inline]
fn filter(self, to: Square) -> impl MoveSet { self.map(|s| s.filter(to)) }
}
pub(crate) trait MoveSet {
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn for_each<F: FnMut(RawMove)>(self, _f: F);
fn filter(self, to: Square) -> impl MoveSet;
}
pub(crate) trait MoveGen<T> {
const ROLE: u8 = 0;
#[inline]
fn checkers(&mut self, _b: Bitboard) {}
#[inline]
fn en_passant(&mut self) {}
#[inline]
fn extend<S: MoveSet>(&mut self, _s: S) -> ControlFlow<T> {
ControlFlow::Continue(())
}
}
impl Position {
#[inline]
pub(crate) fn generate_moves<S, T>(&self, moves: &mut T) -> ControlFlow<S>
where
T: MoveGen<S>,
{
match self.turn() {
Color::White => self.generate_moves_aux::<0, S, T>(moves),
Color::Black => self.generate_moves_aux::<1, S, T>(moves),
}
}
#[inline]
pub(crate) fn generate_moves_aux<const COLOR: u8, S, T>(&self, moves: &mut T) -> ControlFlow<S>
where
T: MoveGen<S>,
{
let turn = if COLOR == 0 {
Color::White
} else {
Color::Black
};
let en_passant = self.en_passant_target_square();
debug_assert!(turn == self.turn());
let [a, b, c, d, _] = self.bitboards();
let blockers = a | b | c;
let (us, them) = match turn {
Color::White => (blockers ^ d, d),
Color::Black => (d, blockers ^ d),
};
let pieces = self.pieces();
let ours = *pieces.get(turn);
let theirs = *pieces.get(!turn);
let king_square = ours.king().first().unwrap();
let forward = turn.forward();
if T::ROLE == 0 || T::ROLE == KING {
let attacked = {
let blockers = blockers ^ ours.king();
let king_square = theirs.king().first().unwrap();
let res = lookup::king(king_square);
let res = (theirs.bishop() | theirs.queen())
.fold(res, |acc, sq| acc | lookup::bishop(sq, blockers));
let res = (theirs.rook() | theirs.queen())
.fold(res, |acc, sq| acc | lookup::rook(sq, blockers));
let res = theirs
.knight()
.fold(res, |acc, sq| acc | lookup::knight(sq));
let res = res | theirs.pawn().trans(!forward).trans(Direction::East);
let res = res | theirs.pawn().trans(!forward).trans(Direction::West);
res
};
moves.extend(MoveSetNormal {
kind: MoveType::KingMove,
from: king_square,
to: lookup::king(king_square) & !us & !attacked,
})?;
if self.castling_rights(turn, Castle::Short) {
let (x, y) = match turn {
Color::White => (Bitboard(0x0000000000000070), Bitboard(0x0000000000000060)),
Color::Black => (Bitboard(0x7000000000000000), Bitboard(0x6000000000000000)),
};
if (attacked & x | blockers & y).is_empty() {
let from = king_square;
let to = from
.trans_unchecked(Direction::East)
.trans_unchecked(Direction::East);
moves.extend(MoveSetCastleShort { from, to })?;
}
}
if self.castling_rights(turn, Castle::Long) {
let (x, y) = match turn {
Color::White => (Bitboard(0x000000000000001C), Bitboard(0x000000000000000E)),
Color::Black => (Bitboard(0x1C00000000000000), Bitboard(0x0E00000000000000)),
};
if (attacked & x | blockers & y).is_empty() {
let from = king_square;
let to = from
.trans_unchecked(Direction::West)
.trans_unchecked(Direction::West);
moves.extend(MoveSetCastleLong { from, to })?;
}
}
}
let x = lookup::bishop(king_square, blockers);
let y = lookup::rook(king_square, blockers);
let mut checkers = lookup::pawn_attack(turn, king_square) & theirs.pawn()
| lookup::knight(king_square) & theirs.knight()
| x & (theirs.bishop() | theirs.queen())
| y & (theirs.rook() | theirs.queen());
moves.checkers(checkers);
let checker = checkers.next();
if !checkers.is_empty() {
return ControlFlow::Continue(());
}
let check_path = checker
.map(|checker| lookup::segment(king_square, checker))
.unwrap_or(Bitboard(!0));
let masked_blockers = blockers & !(x | y);
let pinned = ((lookup::bishop(king_square, masked_blockers)
& (theirs.bishop() | theirs.queen()))
| (lookup::rook(king_square, masked_blockers) & (theirs.rook() | theirs.queen())))
.fold(Bitboard::new(), |acc, sq| {
acc | lookup::segment(king_square, sq)
});
if T::ROLE == 0 || T::ROLE == PAWN {
let kside = match turn {
Color::White => Direction::NorthEast,
Color::Black => Direction::SouthEast,
};
let qside = match turn {
Color::White => Direction::NorthWest,
Color::Black => Direction::SouthWest,
};
let third_rank = match turn {
Color::White => Rank::Third,
Color::Black => Rank::Sixth,
};
let adv = (ours.pawn() & (!pinned | king_square.file().bitboard())).trans(forward)
& !blockers;
{
moves.extend(MoveSetPawnAdvance {
from: !forward,
to: adv & check_path,
})?;
}
moves.extend(MoveSetPawnAttack {
from: !kside,
to: (ours.pawn() & (!pinned | lookup::ray(king_square, kside))).trans(kside)
& them
& check_path,
})?;
moves.extend(MoveSetPawnAttack {
from: !qside,
to: (ours.pawn() & (!pinned | lookup::ray(king_square, qside))).trans(qside)
& them
& check_path,
})?;
moves.extend(MoveSetPawnDoubleAdvance {
from: !forward,
to: (adv & third_rank.bitboard()).trans(forward) & !blockers & check_path,
})?;
if let Some(to) = en_passant {
let capture_square = to.trans_unchecked(!forward);
if check_path.contains(to)
|| checker.is_none_or(|checker| checker == capture_square)
{
let candidates = lookup::pawn_attack(!turn, to) & ours.pawn();
let blockers = blockers ^ capture_square.bitboard();
let pinned = pinned
| (lookup::rook(
king_square,
blockers & !(lookup::rook(king_square, blockers)),
) & theirs.rook())
.fold(Bitboard::new(), |acc, sq| {
acc | lookup::segment(king_square, sq)
});
let mut candidates = candidates & (!pinned | lookup::segment(king_square, to));
if let Some(from) = candidates.next() {
moves.en_passant();
moves.extend(MoveSetEnPassant { from, to })?;
if let Some(from) = candidates.next() {
moves.extend(MoveSetEnPassant { from, to })?;
}
}
}
}
}
{
let target_mask = check_path & !us;
if T::ROLE == 0 || T::ROLE == KNIGHT {
(ours.knight() & !pinned).control_for_each(|from| {
moves.extend(MoveSetNormal {
kind: MoveType::KnightMove,
from,
to: target_mask & lookup::knight(from),
})
})?;
}
if T::ROLE == 0 || T::ROLE == QUEEN || T::ROLE == BISHOP {
let mut mask = !pinned;
if T::ROLE == 0 {
mask &= ours.bishop() | ours.queen();
}
if T::ROLE == BISHOP {
mask &= ours.bishop();
}
if T::ROLE == QUEEN {
mask &= ours.queen();
}
mask.control_for_each(|from| {
moves.extend(MoveSetNormal {
kind: MoveType::DiagonalMove,
from,
to: target_mask & lookup::bishop(from, blockers),
})
})?;
}
if T::ROLE == 0 || T::ROLE == QUEEN || T::ROLE == ROOK {
let mut mask = !pinned;
if T::ROLE == 0 {
mask &= ours.rook() | ours.queen();
}
if T::ROLE == ROOK {
mask &= ours.rook();
}
if T::ROLE == QUEEN {
mask &= ours.queen();
}
mask.control_for_each(|from| {
moves.extend(MoveSetNormal {
kind: MoveType::LineMove,
from,
to: target_mask & lookup::rook(from, blockers),
})
})?;
}
}
if checker.is_some() {
return ControlFlow::Continue(());
}
{
let target_mask = !us & pinned;
if T::ROLE == 0 || T::ROLE == QUEEN || T::ROLE == BISHOP {
let mut mask = pinned & lookup::bishop_lines(king_square);
if T::ROLE == 0 {
mask &= ours.bishop() | ours.queen();
}
if T::ROLE == BISHOP {
mask &= ours.bishop();
}
if T::ROLE == QUEEN {
mask &= ours.queen();
}
mask.control_for_each(|from| {
moves.extend(MoveSetNormal {
kind: MoveType::DiagonalMove,
from,
to: target_mask & lookup::line(king_square, from),
})
})?;
}
if T::ROLE == 0 || T::ROLE == QUEEN || T::ROLE == ROOK {
let mut mask = pinned & lookup::rook_lines(king_square);
if T::ROLE == 0 {
mask &= ours.rook() | ours.queen();
}
if T::ROLE == ROOK {
mask &= ours.rook();
}
if T::ROLE == QUEEN {
mask &= ours.queen();
}
mask.control_for_each(|from| {
moves.extend(MoveSetNormal {
kind: MoveType::LineMove,
from,
to: target_mask & lookup::line(king_square, from),
})
})?;
}
}
ControlFlow::Continue(())
}
}
#[inline]
pub(crate) fn play_move<const COLOR: u8>(position: &Position, m: RawMove) -> Position {
let turn = position.turn();
let mut bitboards = position.bitboards();
let RawMove {
kind,
from,
to,
promotion,
} = m;
bitboards[4] &= !(Rank::Third.bitboard() | Rank::Sixth.bitboard());
let castle_mask = Square::A1.bitboard()
| Square::H1.bitboard()
| Square::A8.bitboard()
| Square::H8.bitboard();
bitboards[4] &= !(to.bitboard() & castle_mask);
match kind {
MoveType::CastleShort => aux_play_castle::<COLOR, false>(&mut bitboards),
MoveType::CastleLong => aux_play_castle::<COLOR, true>(&mut bitboards),
MoveType::KingMove => {
bitboards[4] &= !(if COLOR != 0 {
Square::A8.bitboard() | Square::H8.bitboard()
} else {
Square::A1.bitboard() | Square::H1.bitboard()
});
remove::<KING, COLOR>(&mut bitboards, from);
set::<KING, COLOR>(&mut bitboards, to);
}
MoveType::KnightMove => {
remove::<KNIGHT, COLOR>(&mut bitboards, from);
set::<KNIGHT, COLOR>(&mut bitboards, to);
}
MoveType::DiagonalMove => {
aux_play_unset(&mut bitboards, to);
aux_play_copy(&mut bitboards, from, to);
aux_play_unset(&mut bitboards, from);
}
MoveType::LineMove => {
aux_play_unset(&mut bitboards, to);
aux_play_copy(&mut bitboards, from, to);
aux_play_unset(&mut bitboards, from);
bitboards[4] &= !(from.bitboard() & castle_mask);
}
MoveType::PawnAdvance => {
aux_pawn_slide::<COLOR>(&mut bitboards, from, to);
}
MoveType::PawnAttack => {
remove::<PAWN, COLOR>(&mut bitboards, from);
set::<PAWN, COLOR>(&mut bitboards, to);
}
MoveType::PawnAdvancePromotion => {
remove::<PAWN, COLOR>(&mut bitboards, from);
match promotion {
Some(Role::Queen) => put::<QUEEN, COLOR>(&mut bitboards, to),
Some(Role::Rook) => put::<ROOK, COLOR>(&mut bitboards, to),
Some(Role::Bishop) => put::<BISHOP, COLOR>(&mut bitboards, to),
Some(Role::Knight) => put::<KNIGHT, COLOR>(&mut bitboards, to),
Some(Role::Pawn | Role::King) | None => unreachable!(),
}
}
MoveType::PawnAttackPromotion => {
remove::<PAWN, COLOR>(&mut bitboards, from);
match promotion {
Some(Role::Queen) => set::<QUEEN, COLOR>(&mut bitboards, to),
Some(Role::Rook) => set::<ROOK, COLOR>(&mut bitboards, to),
Some(Role::Bishop) => set::<BISHOP, COLOR>(&mut bitboards, to),
Some(Role::Knight) => set::<KNIGHT, COLOR>(&mut bitboards, to),
Some(Role::Pawn | Role::King) | None => unreachable!(),
}
}
MoveType::PawnDoubleAdvance => {
aux_pawn_slide::<COLOR>(&mut bitboards, from, to);
bitboards[4].insert(Square::from_coords(
from.file(),
match turn {
Color::White => Rank::Third,
Color::Black => Rank::Sixth,
},
));
}
MoveType::EnPassant => {
aux_pawn_slide::<COLOR>(&mut bitboards, from, to);
let direction = !turn.forward();
let x = to.bitboard().trans(direction);
let [p_b_q, _, _, black, _] = &mut bitboards;
*p_b_q ^= x;
*black &= !x;
}
}
bitboards[4].0 ^= 2;
Position::from_setup_inner(Setup::from_bitboards_inner(bitboards))
}
#[inline]
fn aux_pawn_slide<const COLOR: u8>(
[p_b_q, _, _, black, _]: &mut [Bitboard; 5],
from: Square,
to: Square,
) {
let x = from.bitboard() | to.bitboard();
*p_b_q ^= x;
if COLOR != 0 {
*black ^= x;
}
}
#[inline]
fn aux_play_unset([a, b, c, d, _]: &mut [Bitboard; 5], square: Square) {
let mask = !square.bitboard();
*a &= mask;
*b &= mask;
*c &= mask;
*d &= mask;
}
#[inline]
fn aux_play_copy([a, b, c, d, _]: &mut [Bitboard; 5], from: Square, to: Square) {
a.0 |= ((a.0 >> from as u8) & 1) << to as u8;
b.0 |= ((b.0 >> from as u8) & 1) << to as u8;
c.0 |= ((c.0 >> from as u8) & 1) << to as u8;
d.0 |= ((d.0 >> from as u8) & 1) << to as u8;
}
#[inline]
fn put<const ROLE: u8, const COLOR: u8>(
[p_b_q, n_b_k, r_q_k, black, _]: &mut [Bitboard; 5],
square: Square,
) {
debug_assert!(!(*p_b_q | *n_b_k | *r_q_k).contains(square));
let x = square.bitboard();
if ROLE == Role::Pawn as u8 {
*p_b_q |= x;
}
if ROLE == Role::Knight as u8 {
*n_b_k |= x;
}
if ROLE == Role::Bishop as u8 {
*p_b_q |= x;
*n_b_k |= x;
}
if ROLE == Role::Rook as u8 {
*r_q_k |= x;
}
if ROLE == Role::Queen as u8 {
*p_b_q |= x;
*r_q_k |= x;
}
if ROLE == Role::King as u8 {
*n_b_k |= x;
*r_q_k |= x;
}
if COLOR != 0 {
*black |= x;
}
}
#[inline]
fn remove<const ROLE: u8, const COLOR: u8>(
[p_b_q, n_b_k, r_q_k, black, _]: &mut [Bitboard; 5],
square: Square,
) {
let x = square.bitboard();
if ROLE == Role::Pawn as u8 {
debug_assert!(p_b_q.contains(square));
debug_assert!(!n_b_k.contains(square));
debug_assert!(!r_q_k.contains(square));
*p_b_q ^= x;
}
if ROLE == Role::Knight as u8 {
debug_assert!(!p_b_q.contains(square));
debug_assert!(n_b_k.contains(square));
debug_assert!(!r_q_k.contains(square));
*n_b_k ^= x;
}
if ROLE == Role::Bishop as u8 {
debug_assert!(p_b_q.contains(square));
debug_assert!(n_b_k.contains(square));
debug_assert!(!r_q_k.contains(square));
*p_b_q ^= x;
*n_b_k ^= x;
}
if ROLE == Role::Rook as u8 {
debug_assert!(!p_b_q.contains(square));
debug_assert!(!n_b_k.contains(square));
debug_assert!(r_q_k.contains(square));
*r_q_k ^= x;
}
if ROLE == Role::Queen as u8 {
debug_assert!(p_b_q.contains(square));
debug_assert!(!n_b_k.contains(square));
debug_assert!(r_q_k.contains(square));
*p_b_q ^= x;
*r_q_k ^= x;
}
if ROLE == Role::King as u8 {
debug_assert!(!p_b_q.contains(square));
debug_assert!(n_b_k.contains(square));
debug_assert!(r_q_k.contains(square));
*n_b_k ^= x;
*r_q_k ^= x;
}
if COLOR != 0 {
debug_assert!(black.contains(square));
*black ^= x;
}
}
#[inline]
fn set<const ROLE: u8, const COLOR: u8>(
[p_b_q, n_b_k, r_q_k, black, _]: &mut [Bitboard; 5],
square: Square,
) {
let x = square.bitboard();
let y = !x;
if ROLE == Role::Pawn as u8 {
*p_b_q |= x;
*n_b_k &= y;
*r_q_k &= y;
}
if ROLE == Role::Knight as u8 {
*p_b_q &= y;
*n_b_k |= x;
*r_q_k &= y;
}
if ROLE == Role::Bishop as u8 {
*p_b_q |= x;
*n_b_k |= x;
*r_q_k &= y;
}
if ROLE == Role::Rook as u8 {
*p_b_q &= y;
*n_b_k &= y;
*r_q_k |= x;
}
if ROLE == Role::Queen as u8 {
*p_b_q |= x;
*n_b_k &= y;
*r_q_k |= x;
}
if ROLE == Role::King as u8 {
*p_b_q &= y;
*n_b_k |= x;
*r_q_k |= x;
}
if COLOR != 0 {
*black |= x;
} else {
*black &= y;
}
}
#[inline]
fn aux_play_castle<const COLOR: u8, const LONG: bool>(
[p_b_q, n_b_k, r_q_k, black, x]: &mut [Bitboard; 5],
) {
let (a, b, c, d) = if COLOR != 0 {
if LONG {
(Square::E8, Square::C8, Square::A8, Square::D8)
} else {
(Square::E8, Square::G8, Square::H8, Square::F8)
}
} else {
if LONG {
(Square::E1, Square::C1, Square::A1, Square::D1)
} else {
(Square::E1, Square::G1, Square::H1, Square::F1)
}
};
debug_assert!(!p_b_q.contains(a));
debug_assert!(n_b_k.contains(a));
debug_assert!(r_q_k.contains(a));
debug_assert!(COLOR == 0 || black.contains(a));
debug_assert!(!(*p_b_q | *n_b_k | *r_q_k).contains(b));
debug_assert!(!p_b_q.contains(c));
debug_assert!(!n_b_k.contains(c));
debug_assert!(r_q_k.contains(c));
debug_assert!(COLOR == 0 || black.contains(c));
debug_assert!(!(*p_b_q | *n_b_k | *r_q_k).contains(d));
let u = a.bitboard() | b.bitboard();
let v = u | c.bitboard() | d.bitboard();
*n_b_k ^= u;
*r_q_k ^= v;
if COLOR != 0 {
*black ^= v;
*x &= !(Square::A8.bitboard() | Square::H8.bitboard());
} else {
*x &= !(Square::A1.bitboard() | Square::H1.bitboard());
}
}
impl AsSetup for Position {
#[inline]
fn as_setup(&self) -> &Setup {
&self.0
}
}