From d17d099960154f6f5467fdcc6994e59411cdc6df Mon Sep 17 00:00:00 2001 From: Paul-Nicolas Madelaine Date: Sat, 6 Sep 2025 15:36:08 +0200 Subject: [PATCH] meta (wip) --- Cargo.toml | 4 +- src/board.rs | 70 +++++-------------- src/position.rs | 164 +++++++++++++++++++++++++++----------------- src/setup.rs | 176 +++++++++++++++++++++++++++++++++++++++++------- tests/tests.rs | 2 +- 5 files changed, 274 insertions(+), 142 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02f1792..429ea80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/src/board.rs b/src/board.rs index e90eda3..502da38 100644 --- a/src/board.rs +++ b/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) -> OptionSquare { - match square { - Some(square) => Self::from_square(square), - None => Self::None, - } - } + // #[inline] + // pub(crate) fn new(square: Option) -> OptionSquare { + // match square { + // Some(square) => Self::from_square(square), + // None => Self::None, + // } + // } #[inline] pub(crate) fn try_into_square(self) -> Option { @@ -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, - } - } -} diff --git a/src/position.rs b/src/position.rs index 38ea97f..2bf36d7 100644 --- a/src/position.rs +++ b/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, InvalidUciMove> { let mut visitor = VisitorImpl::::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, InvalidSan> { let mut visitor = VisitorImpl::::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::(self.raw) + },); position } @@ -625,7 +636,10 @@ impl<'l> Move<'l> { _ => { fn aux(m: &Move) -> SanInner { let mut visitor = VisitorImpl::::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(&self, visitor: &mut T) + fn generate_moves(&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(&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::(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::PawnDoubleAdvance => { - aux_play_pawn_advance(setup, Role::Pawn, from, to); - setup.en_passant = OptionSquare::new(Some(Square::new( + aux_play_pawn_advance::(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::(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(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(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(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 { diff --git a/src/setup.rs b/src/setup.rs index 83fe4f1..99d78f7 100644 --- a/src/setup.rs +++ b/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) { - 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', })?; diff --git a/tests/tests.rs b/tests/tests.rs index e010b81..279c94c 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -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]) {