meta (wip)
This commit is contained in:
parent
112978c7fb
commit
d17d099960
5 changed files with 274 additions and 142 deletions
|
@ -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
|
||||
|
|
70
src/board.rs
70
src/board.rs
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
164
src/position.rs
164
src/position.rs
|
@ -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 {
|
||||
|
|
176
src/setup.rs
176
src/setup.rs
|
@ -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',
|
||||
})?;
|
||||
|
|
|
@ -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]) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue