1
0
Fork 0

meta (wip)

This commit is contained in:
Paul-Nicolas Madelaine 2025-09-06 15:36:08 +02:00
parent 112978c7fb
commit d17d099960
5 changed files with 274 additions and 142 deletions

View file

@ -8,5 +8,5 @@ description = "computing chess moves"
repository = "https://git.pnm.tf/pnm/eschac"
keywords = ["chess"]
[profile.dev]
opt-level = 3
# [profile.dev]
# opt-level = 3

View file

@ -66,6 +66,11 @@ impl Color {
Self::Black => Direction::South,
}
}
#[inline]
pub(crate) unsafe fn transmute(value: u8) -> Self {
std::mem::transmute(value)
}
}
impl std::ops::Not for Color {
@ -406,13 +411,13 @@ pub(crate) enum OptionSquare {
}
impl OptionSquare {
#[inline]
pub(crate) fn new(square: Option<Square>) -> OptionSquare {
match square {
Some(square) => Self::from_square(square),
None => Self::None,
}
}
// #[inline]
// pub(crate) fn new(square: Option<Square>) -> OptionSquare {
// match square {
// Some(square) => Self::from_square(square),
// None => Self::None,
// }
// }
#[inline]
pub(crate) fn try_into_square(self) -> Option<Square> {
@ -428,6 +433,12 @@ impl OptionSquare {
pub(crate) fn from_square(square: Square) -> Self {
unsafe { std::mem::transmute(square) }
}
#[inline]
pub(crate) unsafe fn into_square_unchecked(self) -> Square {
debug_assert!(self != Self::None);
Square::transmute(self as u8)
}
}
/// A type of piece.
@ -630,48 +641,3 @@ impl CastlingSide {
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct CastlingRights(u8);
impl CastlingRights {
#[inline]
pub(crate) fn new() -> Self {
Self(0)
}
#[inline]
pub(crate) const fn full() -> Self {
Self(15)
}
#[inline]
pub(crate) fn get(&self, color: Color, side: CastlingSide) -> bool {
(self.0 & Self::mask(color, side)) != 0
}
#[inline]
pub(crate) fn set(&mut self, color: Color, side: CastlingSide) {
self.0 |= Self::mask(color, side);
}
#[inline]
pub(crate) fn unset(&mut self, color: Color, side: CastlingSide) {
self.0 &= !Self::mask(color, side);
}
#[inline]
pub(crate) fn mirror(&self) -> Self {
Self(((self.0 & 3) << 2) | (self.0 >> 2))
}
#[inline]
const fn mask(color: Color, side: CastlingSide) -> u8 {
match (color, side) {
(Color::White, CastlingSide::Short) => 1,
(Color::White, CastlingSide::Long) => 2,
(Color::Black, CastlingSide::Short) => 4,
(Color::Black, CastlingSide::Long) => 8,
}
}
}

View file

@ -82,8 +82,7 @@ impl Position {
p_b_q: Bitboard(0x2CFF00000000FF2C),
n_b_k: Bitboard(0x7600000000000076),
r_q_k: Bitboard(0x9900000000000099),
turn: Color::White,
castling_rights: CastlingRights::full(),
meta: Meta::W_KQkq,
en_passant: OptionSquare::None,
},
lookup: InitialisedLookup::init(),
@ -108,7 +107,10 @@ impl Position {
#[inline]
pub fn legal_moves<'l>(&'l self) -> Moves<'l> {
fn aux(position: &Position, visitor: &mut Moves) {
position.generate_moves(visitor);
match_meta!(
position.setup.meta,
position.generate_moves::<_, META>(visitor),
);
}
let mut visitor = Moves {
position: self,
@ -153,7 +155,10 @@ impl Position {
}
}
fn aux(position: &Position, visitor: &mut VisitorImpl) {
position.generate_moves(visitor);
match_meta!(
position.setup.meta,
position.generate_moves::<_, META>(visitor),
);
}
let mut visitor = VisitorImpl::new();
aux(self, &mut visitor);
@ -168,7 +173,7 @@ impl Position {
/// responsibility to rule out the legality of en passant before calling this function.
#[inline]
pub fn remove_en_passant_target_square(&mut self) {
self.setup.en_passant = OptionSquare::None;
self.setup.set_en_passant_target_square(None);
}
/// Returns the occupancy of a square.
@ -231,23 +236,21 @@ impl Position {
let n = setup.n_b_k ^ b ^ k;
let r = setup.r_q_k ^ q ^ k;
let p = setup.p_b_q ^ b ^ q;
let (us, them) = match setup.turn {
let (us, them) = match setup.turn() {
Color::White => (setup.w, blockers ^ setup.w),
Color::Black => (blockers ^ setup.w, setup.w),
};
let king_square = (us & k).next().unwrap();
let checkers = them
& (d.pawn_attack(setup.turn, king_square) & p
& (d.pawn_attack(setup.turn(), king_square) & p
| d.knight(king_square) & n
| d.bishop(king_square, blockers) & (q | b)
| d.rook(king_square, blockers) & (q | r));
checkers.is_empty().then(|| Self {
setup: Setup {
turn: !setup.turn,
en_passant: OptionSquare::None,
..setup.clone()
},
lookup: d,
checkers.is_empty().then(|| {
let mut setup = setup.clone();
setup.set_turn(!setup.turn());
setup.set_en_passant_target_square(None);
Self { setup, lookup: d }
})
}
@ -346,7 +349,10 @@ impl Position {
to: Square,
) -> Result<Move<'l>, InvalidUciMove> {
let mut visitor = VisitorImpl::<ROLE>::new(role, from.bitboard(), to.bitboard());
position.generate_moves(&mut visitor);
match_meta!(
position.setup.meta,
position.generate_moves::<_, META>(&mut visitor),
);
let raw = visitor.found.ok_or(InvalidUciMove::Illegal)?;
Ok(Move { position, raw })
}
@ -464,7 +470,10 @@ impl Position {
to: Bitboard,
) -> Result<Move<'l>, InvalidSan> {
let mut visitor = VisitorImpl::<ROLE>::new(role, from, to);
position.generate_moves(&mut visitor);
match_meta!(
position.setup.meta,
position.generate_moves::<_, META>(&mut visitor),
);
match visitor.found {
None => Err(InvalidSan::Illegal),
Some(raw) => match visitor.found_other {
@ -498,7 +507,9 @@ impl<'l> Move<'l> {
/// capture or when capturing is not legal.
pub fn make(self) -> Position {
let mut position = self.position.clone();
unsafe { position.play_unchecked(self.raw) };
match_meta!(position.setup.meta, unsafe {
position.play_unchecked::<META>(self.raw)
},);
position
}
@ -625,7 +636,10 @@ impl<'l> Move<'l> {
_ => {
fn aux<const ROLE: u8>(m: &Move) -> SanInner {
let mut visitor = VisitorImpl::<ROLE>::new(m.to());
m.position().generate_moves(&mut visitor);
match_meta!(
m.position().setup.meta,
m.position().generate_moves::<_, META>(&mut visitor),
);
let candidates = visitor.candidates;
let (file, rank) = if candidates == m.from().bitboard() {
(None, None)
@ -658,7 +672,7 @@ impl<'l> Move<'l> {
suffix: {
let pos = self.make();
let mut visitor = MateCollector::new();
pos.generate_moves(&mut visitor);
match_meta!(pos.setup.meta, pos.generate_moves::<_, META>(&mut visitor),);
visitor.is_check.then(|| match visitor.is_mate {
true => SanSuffix::Checkmate,
false => SanSuffix::Check,
@ -924,21 +938,25 @@ impl Position {
}
}
fn generate_moves<T>(&self, visitor: &mut T)
fn generate_moves<T, const META: u8>(&self, visitor: &mut T)
where
T: Visitor,
{
let global_mask_from = visitor.from();
let global_mask_to = visitor.to();
let turn = match META & META_TURN_WHITE == 0 {
true => Color::White,
false => Color::Black,
};
let Setup {
w,
p_b_q,
n_b_k,
r_q_k,
turn,
meta: _,
en_passant,
castling_rights,
} = self.setup;
let d = self.lookup;
@ -1008,8 +1026,10 @@ impl Position {
role: Role::King,
}),
);
// castling
if castling_rights.get(turn, CastlingSide::Short) {
// castling short
if (META & META_TURN_WHITE == 0) & (META & META_WHITE_CASTLE_SHORT != 0)
| (META & META_TURN_WHITE != 0) & (META & META_BLACK_CASTLE_SHORT != 0)
{
let (x, y) = match turn {
Color::White => (Bitboard(0x0000000000000070), Bitboard(0x0000000000000060)),
Color::Black => (Bitboard(0x7000000000000000), Bitboard(0x6000000000000000)),
@ -1030,7 +1050,10 @@ impl Position {
}
}
}
if castling_rights.get(turn, CastlingSide::Long) {
// castling long
if (META & META_TURN_WHITE == 0) & (META & META_WHITE_CASTLE_LONG != 0)
| (META & META_TURN_WHITE != 0) & (META & META_BLACK_CASTLE_LONG != 0)
{
let (x, y) = match turn {
Color::White => (Bitboard(0x000000000000001C), Bitboard(0x000000000000000E)),
Color::Black => (Bitboard(0x1C00000000000000), Bitboard(0x0E00000000000000)),
@ -1156,7 +1179,8 @@ impl Position {
),
);
// en passant
if let Some(to) = en_passant.try_into_square() {
if META & META_EN_PASSANT != 0 {
let to = unsafe { en_passant.into_square_unchecked() };
if global_mask_to.contains(to) {
let capture_square = unsafe {
// SAFETY: the position is legal
@ -1251,10 +1275,14 @@ impl Position {
}
#[inline]
unsafe fn play_unchecked(&mut self, m: RawMove) {
unsafe fn play_unchecked<const META: u8>(&mut self, m: RawMove) {
let Self { setup, .. } = self;
let turn = match META & META_TURN_WHITE == 0 {
true => Color::White,
false => Color::Black,
};
setup.en_passant = OptionSquare::None;
setup.set_en_passant_target_square(None);
let RawMove {
kind,
@ -1264,39 +1292,43 @@ impl Position {
} = m;
match kind {
MoveType::CastleShort => aux_play_castle(setup, CastlingSide::Short),
MoveType::CastleLong => aux_play_castle(setup, CastlingSide::Long),
MoveType::KingMove => aux_play_normal(setup, Role::King, from, to),
MoveType::PieceMove => aux_play_normal(setup, role, from, to),
MoveType::PawnAdvance => aux_play_pawn_advance(setup, Role::Pawn, from, to),
MoveType::PawnAttack => aux_play_normal(setup, Role::Pawn, from, to),
MoveType::PawnAdvancePromotion => aux_play_pawn_advance(setup, role, from, to),
MoveType::PawnAttackPromotion => aux_play_normal(setup, role, from, to),
MoveType::CastleShort => aux_play_castle::<META>(setup, CastlingSide::Short),
MoveType::CastleLong => aux_play_castle::<META>(setup, CastlingSide::Long),
MoveType::KingMove => aux_play_normal::<META>(setup, Role::King, from, to),
MoveType::PieceMove => aux_play_normal::<META>(setup, role, from, to),
MoveType::PawnAdvance => aux_play_pawn_advance::<META>(setup, Role::Pawn, from, to),
MoveType::PawnAttack => aux_play_normal::<META>(setup, Role::Pawn, from, to),
MoveType::PawnAdvancePromotion => aux_play_pawn_advance::<META>(setup, role, from, to),
MoveType::PawnAttackPromotion => aux_play_normal::<META>(setup, role, from, to),
MoveType::PawnDoubleAdvance => {
aux_play_pawn_advance(setup, Role::Pawn, from, to);
setup.en_passant = OptionSquare::new(Some(Square::new(
aux_play_pawn_advance::<META>(setup, Role::Pawn, from, to);
setup.set_en_passant_target_square(Some(Square::new(
from.file(),
match setup.turn {
match turn {
Color::White => Rank::Third,
Color::Black => Rank::Sixth,
},
)));
}
MoveType::EnPassant => {
let direction = !setup.turn.forward();
let direction = !turn.forward();
let x = (unsafe { to.trans_unchecked(direction) }).bitboard();
setup.p_b_q ^= x;
setup.w &= !x;
aux_play_pawn_advance(setup, Role::Pawn, from, to);
aux_play_pawn_advance::<META>(setup, Role::Pawn, from, to);
}
}
setup.turn = !setup.turn;
setup.set_turn(!turn);
}
}
#[inline]
fn aux_play_normal(setup: &mut Setup, role: Role, from: Square, target: Square) {
fn aux_play_normal<const META: u8>(setup: &mut Setup, role: Role, from: Square, target: Square) {
let turn = match META & META_TURN_WHITE == 0 {
true => Color::White,
false => Color::Black,
};
let from = from.bitboard();
let to = target.bitboard();
let mask = !(from | to);
@ -1304,20 +1336,18 @@ fn aux_play_normal(setup: &mut Setup, role: Role, from: Square, target: Square)
setup.p_b_q &= mask;
setup.n_b_k &= mask;
setup.r_q_k &= mask;
if target == Square::new(File::H, setup.turn.promotion_rank()) {
setup
.castling_rights
.unset(!setup.turn, CastlingSide::Short);
if target == Square::new(File::H, turn.promotion_rank()) {
setup.set_castling_rights(!turn, CastlingSide::Short, false);
}
if target == Square::new(File::A, setup.turn.promotion_rank()) {
setup.castling_rights.unset(!setup.turn, CastlingSide::Long);
if target == Square::new(File::A, turn.promotion_rank()) {
setup.set_castling_rights(!turn, CastlingSide::Long, false);
}
match role {
Role::King => {
setup.n_b_k |= to;
setup.r_q_k |= to;
setup.castling_rights.unset(setup.turn, CastlingSide::Short);
setup.castling_rights.unset(setup.turn, CastlingSide::Long);
setup.set_castling_rights(turn, CastlingSide::Short, false);
setup.set_castling_rights(turn, CastlingSide::Long, false);
}
Role::Queen => {
setup.p_b_q |= to;
@ -1332,24 +1362,28 @@ fn aux_play_normal(setup: &mut Setup, role: Role, from: Square, target: Square)
}
Role::Rook => {
setup.r_q_k |= to;
if from == Square::new(File::H, setup.turn.home_rank()).bitboard() {
setup.castling_rights.unset(setup.turn, CastlingSide::Short);
if from == Square::new(File::H, turn.home_rank()).bitboard() {
setup.set_castling_rights(turn, CastlingSide::Short, false);
}
if from == Square::new(File::A, setup.turn.home_rank()).bitboard() {
setup.castling_rights.unset(setup.turn, CastlingSide::Long);
if from == Square::new(File::A, turn.home_rank()).bitboard() {
setup.set_castling_rights(turn, CastlingSide::Long, false);
}
}
Role::Pawn => {
setup.p_b_q |= to;
}
}
if setup.turn == Color::White {
if turn == Color::White {
setup.w |= to;
}
}
#[inline]
fn aux_play_pawn_advance(setup: &mut Setup, role: Role, from: Square, to: Square) {
fn aux_play_pawn_advance<const META: u8>(setup: &mut Setup, role: Role, from: Square, to: Square) {
let turn = match META & META_TURN_WHITE == 0 {
true => Color::White,
false => Color::Black,
};
let from = from.bitboard();
let to = to.bitboard();
match role {
@ -1372,14 +1406,18 @@ fn aux_play_pawn_advance(setup: &mut Setup, role: Role, from: Square, to: Square
}
Role::Pawn => setup.p_b_q ^= from | to,
}
if setup.turn == Color::White {
if turn == Color::White {
setup.w ^= from | to;
}
}
#[inline]
fn aux_play_castle(setup: &mut Setup, side: CastlingSide) {
let rank = setup.turn.home_rank();
fn aux_play_castle<const META: u8>(setup: &mut Setup, side: CastlingSide) {
let turn = match META & META_TURN_WHITE == 0 {
true => Color::White,
false => Color::Black,
};
let rank = turn.home_rank();
let (king_flip, rook_flip) = match side {
CastlingSide::Short => (
Square::new(File::E, rank).bitboard() | Square::new(File::G, rank).bitboard(),
@ -1391,14 +1429,14 @@ fn aux_play_castle(setup: &mut Setup, side: CastlingSide) {
),
};
if setup.turn == Color::White {
if turn == Color::White {
setup.w ^= king_flip | rook_flip;
}
setup.n_b_k ^= king_flip;
setup.r_q_k ^= king_flip | rook_flip;
setup.castling_rights.unset(setup.turn, CastlingSide::Short);
setup.castling_rights.unset(setup.turn, CastlingSide::Long);
setup.set_castling_rights(turn, CastlingSide::Short, false);
setup.set_castling_rights(turn, CastlingSide::Long, false);
}
struct MateCollector {

View file

@ -23,16 +23,134 @@ use crate::position::*;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Setup {
pub(crate) w: Bitboard,
pub(crate) p_b_q: Bitboard,
pub(crate) n_b_k: Bitboard,
pub(crate) r_q_k: Bitboard,
pub(crate) turn: Color,
pub(crate) meta: Meta,
pub(crate) en_passant: OptionSquare,
pub(crate) castling_rights: CastlingRights,
}
#[allow(unused, non_camel_case_types)]
#[rustfmt::skip]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub(crate) enum Meta {
W, B, W_K, B_K, W_Q, B_Q, W_KQ, B_KQ,
W_k, B_k, W_Kk, B_Kk, W_Qk, B_Qk, W_KQk, B_KQk,
Wq, Bq, W_Kq, B_Kq, W_Qq, B_Qq, W_KQq, B_KQq,
W_kq, B_kq, W_Kkq, B_Kkq, W_Qkq, B_Qkq, W_KQkq, B_KQkq,
W_ep, B_ep, W_K_ep, B_K_ep, W_Q_ep, B_Q_ep, W_KQ_ep, B_KQ_ep,
W_k_ep, B_k_ep, W_Kk_ep, B_Kk_ep, W_Qk_ep, B_Qk_ep, W_KQk_ep, B_KQk_ep,
Wq_ep, Bq_ep, W_Kq_ep, B_Kq_ep, W_Qq_ep, B_Qq_ep, W_KQq_ep, B_KQq_ep,
W_kq_ep, B_kq_ep, W_Kkq_ep, B_Kkq_ep, W_Qkq_ep, B_Qkq_ep, W_KQkq_ep, B_KQkq_ep,
}
impl Meta {
#[inline]
unsafe fn transmute(value: u8) -> Self {
debug_assert!(value < 64);
std::mem::transmute(value)
}
#[inline]
fn turn(self) -> Color {
unsafe { Color::transmute(self as u8 & 1) }
}
#[inline]
fn castling_rights(self, color: Color, side: CastlingSide) -> bool {
let mask = (2 << side as u8) << ((color as u8) << 1);
(self as u8) & mask != 0
}
// #[inline]
// pub(crate) fn en_passant(self) -> bool {
// (self as u8) & 32 != 0
// }
#[inline]
fn set_turn(&mut self, turn: Color) {
let value = ((*self as u8) & !1) | turn as u8;
*self = unsafe { Meta::transmute(value) }
}
#[inline]
fn set_castling_rights(&mut self, color: Color, side: CastlingSide) {
let mask = (2 << side as u8) << ((color as u8) << 1);
let value = (*self as u8) | mask;
*self = unsafe { Meta::transmute(value) };
}
#[inline]
fn unset_castling_rights(&mut self, color: Color, side: CastlingSide) {
let mask = (2 << side as u8) << ((color as u8) << 1);
let value = (*self as u8) & !mask;
*self = unsafe { Meta::transmute(value) };
}
#[inline]
fn set_en_passant(&mut self) {
let mask = 0b100000;
let value = (*self as u8) | mask;
*self = unsafe { Meta::transmute(value) };
}
#[inline]
fn unset_en_passant(&mut self) {
let mask = 0b100000;
let value = (*self as u8) & !mask;
*self = unsafe { Meta::transmute(value) };
}
#[inline]
fn mirror(self) -> Self {
let x = self as u8;
let value = ((x & 0b000001) ^ 0b000001)
| ((x & 0b000110) << 2)
| ((x & 0b011000) >> 2)
| x & 0b100000;
unsafe { Meta::transmute(value) }
}
}
pub(crate) const META_TURN_WHITE: u8 = 1;
pub(crate) const META_WHITE_CASTLE_SHORT: u8 = 2;
pub(crate) const META_WHITE_CASTLE_LONG: u8 = 4;
pub(crate) const META_BLACK_CASTLE_SHORT: u8 = 8;
pub(crate) const META_BLACK_CASTLE_LONG: u8 = 16;
pub(crate) const META_EN_PASSANT: u8 = 32;
#[rustfmt::skip]
macro_rules! match_meta {
($meta:expr, $expr:expr,) => {
match $meta {
crate::setup::Meta::W => { const META: u8 = 0b000000; $expr }, crate::setup::Meta::B => { const META: u8 = 0b000001; $expr },
crate::setup::Meta::W_K => { const META: u8 = 0b000010; $expr }, crate::setup::Meta::B_K => { const META: u8 = 0b000011; $expr },
crate::setup::Meta::W_Q => { const META: u8 = 0b000100; $expr }, crate::setup::Meta::B_Q => { const META: u8 = 0b000101; $expr },
crate::setup::Meta::W_KQ => { const META: u8 = 0b000110; $expr }, crate::setup::Meta::B_KQ => { const META: u8 = 0b000111; $expr },
crate::setup::Meta::W_k => { const META: u8 = 0b001000; $expr }, crate::setup::Meta::B_k => { const META: u8 = 0b001001; $expr },
crate::setup::Meta::W_Kk => { const META: u8 = 0b001010; $expr }, crate::setup::Meta::B_Kk => { const META: u8 = 0b001011; $expr },
crate::setup::Meta::W_Qk => { const META: u8 = 0b001100; $expr }, crate::setup::Meta::B_Qk => { const META: u8 = 0b001101; $expr },
crate::setup::Meta::W_KQk => { const META: u8 = 0b011110; $expr }, crate::setup::Meta::B_KQk => { const META: u8 = 0b011111; $expr },
crate::setup::Meta::Wq => { const META: u8 = 0b010000; $expr }, crate::setup::Meta::Bq => { const META: u8 = 0b010001; $expr },
crate::setup::Meta::W_Kq => { const META: u8 = 0b010010; $expr }, crate::setup::Meta::B_Kq => { const META: u8 = 0b010011; $expr },
crate::setup::Meta::W_Qq => { const META: u8 = 0b010100; $expr }, crate::setup::Meta::B_Qq => { const META: u8 = 0b010101; $expr },
crate::setup::Meta::W_KQq => { const META: u8 = 0b010110; $expr }, crate::setup::Meta::B_KQq => { const META: u8 = 0b010111; $expr },
crate::setup::Meta::W_kq => { const META: u8 = 0b011000; $expr }, crate::setup::Meta::B_kq => { const META: u8 = 0b011001; $expr },
crate::setup::Meta::W_Kkq => { const META: u8 = 0b011010; $expr }, crate::setup::Meta::B_Kkq => { const META: u8 = 0b011011; $expr },
crate::setup::Meta::W_Qkq => { const META: u8 = 0b011100; $expr }, crate::setup::Meta::B_Qkq => { const META: u8 = 0b011101; $expr },
crate::setup::Meta::W_KQkq => { const META: u8 = 0b011110; $expr }, crate::setup::Meta::B_KQkq => { const META: u8 = 0b011111; $expr },
crate::setup::Meta::W_ep => { const META: u8 = 0b100000; $expr }, crate::setup::Meta::B_ep => { const META: u8 = 0b100001; $expr },
crate::setup::Meta::W_K_ep => { const META: u8 = 0b100010; $expr }, crate::setup::Meta::B_K_ep => { const META: u8 = 0b100011; $expr },
crate::setup::Meta::W_Q_ep => { const META: u8 = 0b100100; $expr }, crate::setup::Meta::B_Q_ep => { const META: u8 = 0b100101; $expr },
crate::setup::Meta::W_KQ_ep => { const META: u8 = 0b100110; $expr }, crate::setup::Meta::B_KQ_ep => { const META: u8 = 0b100111; $expr },
crate::setup::Meta::W_k_ep => { const META: u8 = 0b101000; $expr }, crate::setup::Meta::B_k_ep => { const META: u8 = 0b101001; $expr },
crate::setup::Meta::W_Kk_ep => { const META: u8 = 0b101010; $expr }, crate::setup::Meta::B_Kk_ep => { const META: u8 = 0b101011; $expr },
crate::setup::Meta::W_Qk_ep => { const META: u8 = 0b101100; $expr }, crate::setup::Meta::B_Qk_ep => { const META: u8 = 0b101101; $expr },
crate::setup::Meta::W_KQk_ep => { const META: u8 = 0b101110; $expr }, crate::setup::Meta::B_KQk_ep => { const META: u8 = 0b101111; $expr },
crate::setup::Meta::Wq_ep => { const META: u8 = 0b110000; $expr }, crate::setup::Meta::Bq_ep => { const META: u8 = 0b110001; $expr },
crate::setup::Meta::W_Kq_ep => { const META: u8 = 0b110010; $expr }, crate::setup::Meta::B_Kq_ep => { const META: u8 = 0b110011; $expr },
crate::setup::Meta::W_Qq_ep => { const META: u8 = 0b110100; $expr }, crate::setup::Meta::B_Qq_ep => { const META: u8 = 0b110101; $expr },
crate::setup::Meta::W_KQq_ep => { const META: u8 = 0b110110; $expr }, crate::setup::Meta::B_KQq_ep => { const META: u8 = 0b110111; $expr },
crate::setup::Meta::W_kq_ep => { const META: u8 = 0b111000; $expr }, crate::setup::Meta::B_kq_ep => { const META: u8 = 0b111001; $expr },
crate::setup::Meta::W_Kkq_ep => { const META: u8 = 0b111010; $expr }, crate::setup::Meta::B_Kkq_ep => { const META: u8 = 0b111011; $expr },
crate::setup::Meta::W_Qkq_ep => { const META: u8 = 0b111100; $expr }, crate::setup::Meta::B_Qkq_ep => { const META: u8 = 0b111101; $expr },
crate::setup::Meta::W_KQkq_ep => { const META: u8 = 0b111110; $expr }, crate::setup::Meta::B_KQkq_ep => { const META: u8 = 0b111111; $expr },
}
};
}
pub(crate) use match_meta;
impl Setup {
/// Creates an empty board, i.e. `8/8/8/8/8/8/8/8 w - -`.
#[inline]
@ -42,9 +160,8 @@ impl Setup {
p_b_q: Bitboard(0),
n_b_k: Bitboard(0),
r_q_k: Bitboard(0),
turn: Color::White,
meta: Meta::W,
en_passant: OptionSquare::None,
castling_rights: CastlingRights::new(),
}
}
@ -144,13 +261,13 @@ impl Setup {
/// Returns the color to play.
#[inline]
pub fn turn(&self) -> Color {
self.turn
self.meta.turn()
}
/// Returns `true` if castling is available for the given color and side.
#[inline]
pub fn castling_rights(&self, color: Color, side: CastlingSide) -> bool {
self.castling_rights.get(color, side)
self.meta.castling_rights(color, side)
}
/// Returns the optional en passant target square.
@ -202,22 +319,31 @@ impl Setup {
/// Sets the color to play.
#[inline]
pub fn set_turn(&mut self, color: Color) {
self.turn = color;
self.meta.set_turn(color);
}
/// Sets the castling rights for the given color and side.
#[inline]
pub fn set_castling_rights(&mut self, color: Color, side: CastlingSide, value: bool) {
match value {
true => self.castling_rights.set(color, side),
false => self.castling_rights.unset(color, side),
false => self.meta.unset_castling_rights(color, side),
true => self.meta.set_castling_rights(color, side),
}
}
/// Sets the en passant target square.
#[inline]
pub fn set_en_passant_target_square(&mut self, square: Option<Square>) {
self.en_passant = OptionSquare::new(square);
match square {
None => {
self.meta.unset_en_passant();
self.en_passant = OptionSquare::None;
}
Some(square) => {
self.meta.set_en_passant();
self.en_passant = OptionSquare::from_square(square);
}
}
}
/// Returns the mirror image of the position.
@ -235,13 +361,12 @@ impl Setup {
p_b_q: self.p_b_q.mirror(),
n_b_k: self.n_b_k.mirror(),
r_q_k: self.r_q_k.mirror(),
turn: !self.turn,
meta: self.meta.mirror(),
en_passant: self
.en_passant
.try_into_square()
.map(|square| OptionSquare::from_square(square.mirror()))
.unwrap_or(OptionSquare::None),
castling_rights: self.castling_rights.mirror(),
}
}
@ -272,15 +397,15 @@ impl Setup {
reasons.add(IllegalPositionReason::TooManyKings);
}
if pieces.get(!self.turn).king().any(|enemy_king| {
let pieces = pieces.get(self.turn);
if pieces.get(!self.meta.turn()).king().any(|enemy_king| {
let pieces = pieces.get(self.turn());
!(d.king(enemy_king) & *pieces.get(Role::King)
| d.bishop(enemy_king, blockers)
& (*pieces.get(Role::Queen) | *pieces.get(Role::Bishop))
| d.rook(enemy_king, blockers)
& (*pieces.get(Role::Queen) | *pieces.get(Role::Rook))
| d.knight(enemy_king) & *pieces.get(Role::Knight)
| d.pawn_attack(!self.turn, enemy_king) & *pieces.get(Role::Pawn))
| d.pawn_attack(!self.turn(), enemy_king) & *pieces.get(Role::Pawn))
.is_empty()
}) {
reasons.add(IllegalPositionReason::HangingKing);
@ -315,7 +440,7 @@ impl Setup {
if Color::all().into_iter().any(|color| {
CastlingSide::all().into_iter().any(|side| {
self.castling_rights.get(color, side)
self.castling_rights(color, side)
&& !(pieces
.get(color)
.get(Role::King)
@ -330,31 +455,34 @@ impl Setup {
}
if self.en_passant.try_into_square().is_some_and(|en_passant| {
let (target_rank, pawn_rank) = match self.turn {
let (target_rank, pawn_rank) = match self.turn() {
Color::White => (Rank::Sixth, Rank::Fifth),
Color::Black => (Rank::Third, Rank::Fourth),
};
let pawn_square = Square::new(en_passant.file(), pawn_rank);
en_passant.rank() != target_rank
|| blockers.contains(en_passant)
|| !pieces.get(!self.turn).get(Role::Pawn).contains(pawn_square)
|| !pieces
.get(!self.turn())
.get(Role::Pawn)
.contains(pawn_square)
}) {
reasons.add(IllegalPositionReason::InvalidEnPassant);
}
if self.en_passant.try_into_square().is_some_and(|en_passant| {
let blockers = blockers
& !en_passant.bitboard().trans(match self.turn {
& !en_passant.bitboard().trans(match self.turn() {
Color::White => Direction::South,
Color::Black => Direction::North,
});
pieces
.get(self.turn)
.get(self.turn())
.king()
.first()
.is_some_and(|king_square| {
!(d.bishop(king_square, blockers)
& (pieces.get(!self.turn).queen() | pieces.get(!self.turn).bishop()))
& (pieces.get(!self.turn()).queen() | pieces.get(!self.turn()).bishop()))
.is_empty()
})
}) {
@ -456,7 +584,7 @@ impl std::fmt::Display for Setup {
f.write_char(' ')?;
f.write_char(match self.turn {
f.write_char(match self.turn() {
Color::White => 'w',
Color::Black => 'b',
})?;

View file

@ -229,7 +229,7 @@ fn mirror() {
.unwrap()
.validate()
.unwrap();
assert_eq!(mirror, position.mirror());
assert_eq!(position.mirror(), mirror);
}
fn perft_aux(record: &str, tests: &[u128]) {