Move castling move generation logic to movegen.rs and refactor related repeated code
This commit is contained in:
79
src/board.rs
79
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
src/fen.rs
43
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<Game, FenError> {
|
||||
@@ -78,17 +78,15 @@ fn side_to_move(side: &str) -> Result<Color, FenError> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<u8, FenError> {
|
||||
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<u8, FenError> {
|
||||
}
|
||||
};
|
||||
}
|
||||
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;
|
||||
|
||||
18
src/game.rs
18
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<u8>,
|
||||
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<u8>,
|
||||
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,
|
||||
|
||||
100
src/movegen.rs
100
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<Move> {
|
||||
let mut moves: Vec<Move> = 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<Move> {
|
||||
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)]
|
||||
|
||||
Reference in New Issue
Block a user