From 07af7294fa856e6efa183f6c52a5aba05200f869 Mon Sep 17 00:00:00 2001 From: stefiosif Date: Fri, 14 Jun 2024 21:46:37 +0300 Subject: [PATCH] Move castling move generation logic to movegen.rs and refactor related repeated code --- src/board.rs | 79 +------------------------------------- src/fen.rs | 43 +++++++++++++++------ src/game.rs | 18 +++++---- src/movegen.rs | 100 +++++++++++++++++++++++++++---------------------- 4 files changed, 100 insertions(+), 140 deletions(-) diff --git a/src/board.rs b/src/board.rs index b7d70e0..19c95e7 100644 --- a/src/board.rs +++ b/src/board.rs @@ -5,7 +5,7 @@ use crate::attack::{ get_bishop_attacks, get_king_attacks, get_knight_attacks, get_pawn_attacks, get_queen_attacks, get_rook_attacks, }; -use crate::game::{CastlingRights, State}; +use crate::game::State; #[derive(Debug, PartialEq, Eq)] pub struct Board { @@ -128,82 +128,7 @@ impl Board { Kind::Bishop => bishop_pseudo_moves(pieces, all_occupancies, own_occupancies), Kind::Rook => rook_pseudo_moves(pieces, all_occupancies, own_occupancies), Kind::Queen => queen_pseudo_moves(pieces, all_occupancies, own_occupancies), - Kind::King => king_pseudo_moves( - pieces, - all_occupancies, - own_occupancies, - self.castling_ability(color), - color, - ), - } - } - - fn castling_ability(&self, color: Color) -> CastlingRights { - match ( - self.castling_from_fen(color), - self.castling_pseudo_safety(color), - ) { - (CastlingRights::Both, _) => self.castling_pseudo_safety(color), - (CastlingRights::Short, CastlingRights::Short) => CastlingRights::Short, - (CastlingRights::Long, CastlingRights::Long) => CastlingRights::Long, - _ => CastlingRights::None, - } - } - - const fn castling_from_fen(&self, color: Color) -> CastlingRights { - let castling_fen = self.state.get_castling_ability(); - - let black_king_and_queen = (castling_fen >> 2) & 0b11 == 0b11; - let black_king = (castling_fen >> 3) & 1 == 1; - let black_queen = (castling_fen >> 2) & 1 == 1; - let white_king_and_queen = castling_fen & 0b11 == 0b11; - let white_king = (castling_fen >> 1) & 1 == 1; - let white_queen = castling_fen & 1 == 1; - - match color { - Color::White => match (white_king_and_queen, white_king, white_queen) { - (true, _, _) => CastlingRights::Both, - (_, true, _) => CastlingRights::Short, - (_, _, true) => CastlingRights::Long, - _ => CastlingRights::None, - }, - Color::Black => match (black_king_and_queen, black_king, black_queen) { - (true, _, _) => CastlingRights::Both, - (_, true, _) => CastlingRights::Short, - (_, _, true) => CastlingRights::Long, - _ => CastlingRights::None, - }, - } - } - - fn castling_pseudo_safety(&self, color: Color) -> CastlingRights { - match color { - Color::White => { - if self.is_attacked(4, Color::Black) { - return CastlingRights::None; - } - let d1_attacked = self.is_attacked(3, Color::Black); - let f1_attacked = self.is_attacked(5, Color::Black); - match (d1_attacked, f1_attacked) { - (true, true) => CastlingRights::None, - (true, false) => CastlingRights::Short, - (false, true) => CastlingRights::Long, - (false, false) => CastlingRights::Both, - } - } - Color::Black => { - if self.is_attacked(60, Color::White) { - return CastlingRights::None; - } - let d8_attacked = self.is_attacked(59, Color::White); - let f8_attacked = self.is_attacked(61, Color::White); - match (d8_attacked, f8_attacked) { - (true, true) => CastlingRights::None, - (true, false) => CastlingRights::Short, - (false, true) => CastlingRights::Long, - (false, false) => CastlingRights::Both, - } - } + Kind::King => king_pseudo_moves(pieces, all_occupancies, own_occupancies, self, color), } } } diff --git a/src/fen.rs b/src/fen.rs index aa6f292..36dd48c 100644 --- a/src/fen.rs +++ b/src/fen.rs @@ -1,5 +1,5 @@ use crate::board::{Board, Color, Kind, Piece}; -use crate::game::{Game, State}; +use crate::game::{Castle, Game, State}; use String as FenError; pub fn from_fen(fen: &str) -> Result { @@ -78,17 +78,15 @@ fn side_to_move(side: &str) -> Result { } } -/// Return castling ability as a bitflag. LSb K, 2nd LSb Q, 3rd LSb k, 4th LSb q. Ignore 4 MSb's -fn castling_ability(castling: &str) -> Result { - let mut rights = 0b0; - +fn castling_ability(castling: &str) -> Result<[Castle; 2], FenError> { + let mut bitflag = 0b0; for c in castling.chars() { match c { - 'K' => rights |= 0b1, - 'Q' => rights |= 0b10, - 'k' => rights |= 0b100, - 'q' => rights |= 0b1000, - '-' => rights |= 0b0, + 'K' => bitflag |= 0b1000, + 'Q' => bitflag |= 0b100, + 'k' => bitflag |= 0b10, + 'q' => bitflag |= 0b1, + '-' => break, _ => { return Err(FenError::from( "Found invalid character while parsing in castling_ability", @@ -96,7 +94,30 @@ fn castling_ability(castling: &str) -> Result { } }; } - Ok(rights) + + let mut castling_ability: [Castle; 2] = [Castle::None, Castle::None]; + + let white_king_and_queen = bitflag & 0b11 == 0b11; + let white_king = (bitflag >> 1) & 1 == 1; + let white_queen = bitflag & 1 == 1; + castling_ability[0] = match (white_king_and_queen, white_king, white_queen) { + (true, _, _) => Castle::Both, + (_, true, _) => Castle::Short, + (_, _, true) => Castle::Long, + _ => Castle::None, + }; + + let black_king_and_queen = (bitflag >> 2) & 0b11 == 0b11; + let black_king = (bitflag >> 3) & 1 == 1; + let black_queen = (bitflag >> 2) & 1 == 1; + castling_ability[1] = match (black_king_and_queen, black_king, black_queen) { + (true, _, _) => Castle::Both, + (_, true, _) => Castle::Short, + (_, _, true) => Castle::Long, + _ => Castle::None, + }; + + Ok(castling_ability) } use std::collections::HashMap; diff --git a/src/game.rs b/src/game.rs index 5e3b235..5e9b43e 100644 --- a/src/game.rs +++ b/src/game.rs @@ -34,7 +34,7 @@ impl Default for Game { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct State { side_to_move: Color, - castling_ability: u8, + castling_ability: [Castle; 2], en_passant_target_square: Option, halfmove_clock: u8, fullmove_counter: u8, @@ -44,7 +44,7 @@ impl State { pub const fn new() -> Self { Self { side_to_move: Color::White, - castling_ability: 0b1111, + castling_ability: [Castle::Both, Castle::Both], en_passant_target_square: None, halfmove_clock: 0, fullmove_counter: 1, @@ -53,7 +53,7 @@ impl State { pub const fn load_state( side_to_move: Color, - castling_ability: u8, + castling_ability: [Castle; 2], en_passant_target_square: Option, halfmove_clock: u8, fullmove_counter: u8, @@ -71,8 +71,11 @@ impl State { self.en_passant_target_square } - pub const fn get_castling_ability(&self) -> u8 { - self.castling_ability + pub const fn get_castling_ability(&self, color: Color) -> Castle { + match color { + Color::White => self.castling_ability[0], + Color::Black => self.castling_ability[1], + } } } @@ -81,7 +84,7 @@ use std::fmt; impl fmt::Display for State { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, - "side_to_move: {:?}\ncastling_ability: {:b}\nen_passant_target_square: {:b}\nhalfmove_clock: {}\nfullmove_counter: {}\n", + "side_to_move: {:?}\ncastling_ability: {:?}\nen_passant_target_square: {:b}\nhalfmove_clock: {}\nfullmove_counter: {}\n", self.side_to_move, self.castling_ability, 1_u64 << self.en_passant_target_square.unwrap_or(0), self.halfmove_clock, self.fullmove_counter) } } @@ -92,7 +95,8 @@ impl Default for State { } } -pub enum CastlingRights { +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Castle { Short, Long, Both, diff --git a/src/movegen.rs b/src/movegen.rs index d6d1259..cd4d63e 100644 --- a/src/movegen.rs +++ b/src/movegen.rs @@ -2,8 +2,8 @@ use crate::attack::{ get_bishop_attacks, get_king_attacks, get_knight_attacks, get_pawn_attacks, get_queen_attacks, get_rook_attacks, }; -use crate::board::Color; -use crate::game::CastlingRights; +use crate::board::{Board, Color}; +use crate::game::Castle; use u64 as Bitboard; const NOT_A_FILE: Bitboard = 0xfefefefefefefefe; @@ -246,7 +246,7 @@ pub fn king_pseudo_moves( king: Bitboard, all_occupancies: Bitboard, own_occupancies: Bitboard, - castling_rights: CastlingRights, + board: &Board, color: Color, ) -> Vec { let mut moves: Vec = vec![]; @@ -260,50 +260,60 @@ pub fn king_pseudo_moves( attacks &= attacks - 1; } - match color { - Color::White => match castling_rights { - CastlingRights::Both => { - if all_occupancies & 0x60 == 0 { - moves.push(Move::new(4, 6)) - } else if all_occupancies & 0xe == 0 { - moves.push(Move::new(4, 2)) - } - } - CastlingRights::Short => { - if all_occupancies & 0x60 == 0 { - moves.push(Move::new(4, 6)) - } - } - CastlingRights::Long => { - if all_occupancies & 0xe == 0 { - moves.push(Move::new(4, 2)) - } - } - CastlingRights::None => (), - }, - Color::Black => match castling_rights { - CastlingRights::Both => { - if all_occupancies & 0x6000000000000000 == 0 { - moves.push(Move::new(60, 62)) - } else if all_occupancies & 0xe00000000000000 == 0 { - moves.push(Move::new(60, 58)) - } - } - CastlingRights::Short => { - if all_occupancies & 0x6000000000000000 == 0 { - moves.push(Move::new(60, 62)) - } - } - CastlingRights::Long => { - if all_occupancies & 0xe00000000000000 == 0 { - moves.push(Move::new(60, 58)) - } - } - CastlingRights::None => (), - }, + moves.extend(king_castling_moves(board, color, all_occupancies)); + moves +} + +fn king_castling_moves(board: &Board, color: Color, all_occupancies: Bitboard) -> Vec { + let mut moves = vec![]; + let (king_from, king_to_short, king_to_long, path_short, path_long) = match color { + Color::White => (4, 2, 6, 0x60_u64, 0xe_u64), + Color::Black => (60, 58, 62, 0x6000000000000000_u64, 0xe00000000000000_u64), + }; + + let mut add_move_if_empty_path = |path_mask, king_to| { + if all_occupancies & path_mask != 0 { + moves.push(Move::new(king_from, king_to)) + } + }; + + match ( + board.state.get_castling_ability(color), + king_and_adj_square_safety(board, color), + ) { + (Castle::Both, Castle::Both) => { + add_move_if_empty_path(path_short, king_to_short); + add_move_if_empty_path(path_long, king_to_long); + } + (Castle::Both, Castle::Short) | (Castle::Short, Castle::Short) => { + add_move_if_empty_path(path_short, king_to_short); + } + (Castle::Both, Castle::Long) | (Castle::Long, Castle::Long) => { + add_move_if_empty_path(path_long, king_to_long) + } + _ => (), + }; + moves +} + +fn king_and_adj_square_safety(board: &Board, color: Color) -> Castle { + let (king_at, opponent_color) = match color { + Color::White => (3, Color::Black), + Color::Black => (60, Color::White), + }; + + if board.is_attacked(king_at, opponent_color) { + return Castle::None; } - moves + let short_attacked = board.is_attacked(king_at - 1, opponent_color); + let long_attacked = board.is_attacked(king_at + 1, opponent_color); + match (short_attacked, long_attacked) { + (true, true) => Castle::None, + (true, false) => Castle::Short, + (false, true) => Castle::Long, + (false, false) => Castle::Both, + } } #[cfg(test)]