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_bishop_attacks, get_king_attacks, get_knight_attacks, get_pawn_attacks, get_queen_attacks,
|
||||||
get_rook_attacks,
|
get_rook_attacks,
|
||||||
};
|
};
|
||||||
use crate::game::{CastlingRights, State};
|
use crate::game::State;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
@@ -128,82 +128,7 @@ impl Board {
|
|||||||
Kind::Bishop => bishop_pseudo_moves(pieces, all_occupancies, own_occupancies),
|
Kind::Bishop => bishop_pseudo_moves(pieces, all_occupancies, own_occupancies),
|
||||||
Kind::Rook => rook_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::Queen => queen_pseudo_moves(pieces, all_occupancies, own_occupancies),
|
||||||
Kind::King => king_pseudo_moves(
|
Kind::King => king_pseudo_moves(pieces, all_occupancies, own_occupancies, self, color),
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/fen.rs
43
src/fen.rs
@@ -1,5 +1,5 @@
|
|||||||
use crate::board::{Board, Color, Kind, Piece};
|
use crate::board::{Board, Color, Kind, Piece};
|
||||||
use crate::game::{Game, State};
|
use crate::game::{Castle, Game, State};
|
||||||
use String as FenError;
|
use String as FenError;
|
||||||
|
|
||||||
pub fn from_fen(fen: &str) -> Result<Game, 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<[Castle; 2], FenError> {
|
||||||
fn castling_ability(castling: &str) -> Result<u8, FenError> {
|
let mut bitflag = 0b0;
|
||||||
let mut rights = 0b0;
|
|
||||||
|
|
||||||
for c in castling.chars() {
|
for c in castling.chars() {
|
||||||
match c {
|
match c {
|
||||||
'K' => rights |= 0b1,
|
'K' => bitflag |= 0b1000,
|
||||||
'Q' => rights |= 0b10,
|
'Q' => bitflag |= 0b100,
|
||||||
'k' => rights |= 0b100,
|
'k' => bitflag |= 0b10,
|
||||||
'q' => rights |= 0b1000,
|
'q' => bitflag |= 0b1,
|
||||||
'-' => rights |= 0b0,
|
'-' => break,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(FenError::from(
|
return Err(FenError::from(
|
||||||
"Found invalid character while parsing in castling_ability",
|
"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;
|
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)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
side_to_move: Color,
|
side_to_move: Color,
|
||||||
castling_ability: u8,
|
castling_ability: [Castle; 2],
|
||||||
en_passant_target_square: Option<u8>,
|
en_passant_target_square: Option<u8>,
|
||||||
halfmove_clock: u8,
|
halfmove_clock: u8,
|
||||||
fullmove_counter: u8,
|
fullmove_counter: u8,
|
||||||
@@ -44,7 +44,7 @@ impl State {
|
|||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
side_to_move: Color::White,
|
side_to_move: Color::White,
|
||||||
castling_ability: 0b1111,
|
castling_ability: [Castle::Both, Castle::Both],
|
||||||
en_passant_target_square: None,
|
en_passant_target_square: None,
|
||||||
halfmove_clock: 0,
|
halfmove_clock: 0,
|
||||||
fullmove_counter: 1,
|
fullmove_counter: 1,
|
||||||
@@ -53,7 +53,7 @@ impl State {
|
|||||||
|
|
||||||
pub const fn load_state(
|
pub const fn load_state(
|
||||||
side_to_move: Color,
|
side_to_move: Color,
|
||||||
castling_ability: u8,
|
castling_ability: [Castle; 2],
|
||||||
en_passant_target_square: Option<u8>,
|
en_passant_target_square: Option<u8>,
|
||||||
halfmove_clock: u8,
|
halfmove_clock: u8,
|
||||||
fullmove_counter: u8,
|
fullmove_counter: u8,
|
||||||
@@ -71,8 +71,11 @@ impl State {
|
|||||||
self.en_passant_target_square
|
self.en_passant_target_square
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn get_castling_ability(&self) -> u8 {
|
pub const fn get_castling_ability(&self, color: Color) -> Castle {
|
||||||
self.castling_ability
|
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 {
|
impl fmt::Display for State {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
writeln!(f,
|
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)
|
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,
|
Short,
|
||||||
Long,
|
Long,
|
||||||
Both,
|
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_bishop_attacks, get_king_attacks, get_knight_attacks, get_pawn_attacks, get_queen_attacks,
|
||||||
get_rook_attacks,
|
get_rook_attacks,
|
||||||
};
|
};
|
||||||
use crate::board::Color;
|
use crate::board::{Board, Color};
|
||||||
use crate::game::CastlingRights;
|
use crate::game::Castle;
|
||||||
use u64 as Bitboard;
|
use u64 as Bitboard;
|
||||||
|
|
||||||
const NOT_A_FILE: Bitboard = 0xfefefefefefefefe;
|
const NOT_A_FILE: Bitboard = 0xfefefefefefefefe;
|
||||||
@@ -246,7 +246,7 @@ pub fn king_pseudo_moves(
|
|||||||
king: Bitboard,
|
king: Bitboard,
|
||||||
all_occupancies: Bitboard,
|
all_occupancies: Bitboard,
|
||||||
own_occupancies: Bitboard,
|
own_occupancies: Bitboard,
|
||||||
castling_rights: CastlingRights,
|
board: &Board,
|
||||||
color: Color,
|
color: Color,
|
||||||
) -> Vec<Move> {
|
) -> Vec<Move> {
|
||||||
let mut moves: Vec<Move> = vec![];
|
let mut moves: Vec<Move> = vec![];
|
||||||
@@ -260,50 +260,60 @@ pub fn king_pseudo_moves(
|
|||||||
attacks &= attacks - 1;
|
attacks &= attacks - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
match color {
|
moves.extend(king_castling_moves(board, color, all_occupancies));
|
||||||
Color::White => match castling_rights {
|
moves
|
||||||
CastlingRights::Both => {
|
}
|
||||||
if all_occupancies & 0x60 == 0 {
|
|
||||||
moves.push(Move::new(4, 6))
|
fn king_castling_moves(board: &Board, color: Color, all_occupancies: Bitboard) -> Vec<Move> {
|
||||||
} else if all_occupancies & 0xe == 0 {
|
let mut moves = vec![];
|
||||||
moves.push(Move::new(4, 2))
|
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),
|
||||||
CastlingRights::Short => {
|
};
|
||||||
if all_occupancies & 0x60 == 0 {
|
|
||||||
moves.push(Move::new(4, 6))
|
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))
|
||||||
CastlingRights::Long => {
|
}
|
||||||
if all_occupancies & 0xe == 0 {
|
};
|
||||||
moves.push(Move::new(4, 2))
|
|
||||||
}
|
match (
|
||||||
}
|
board.state.get_castling_ability(color),
|
||||||
CastlingRights::None => (),
|
king_and_adj_square_safety(board, color),
|
||||||
},
|
) {
|
||||||
Color::Black => match castling_rights {
|
(Castle::Both, Castle::Both) => {
|
||||||
CastlingRights::Both => {
|
add_move_if_empty_path(path_short, king_to_short);
|
||||||
if all_occupancies & 0x6000000000000000 == 0 {
|
add_move_if_empty_path(path_long, king_to_long);
|
||||||
moves.push(Move::new(60, 62))
|
}
|
||||||
} else if all_occupancies & 0xe00000000000000 == 0 {
|
(Castle::Both, Castle::Short) | (Castle::Short, Castle::Short) => {
|
||||||
moves.push(Move::new(60, 58))
|
add_move_if_empty_path(path_short, king_to_short);
|
||||||
}
|
}
|
||||||
}
|
(Castle::Both, Castle::Long) | (Castle::Long, Castle::Long) => {
|
||||||
CastlingRights::Short => {
|
add_move_if_empty_path(path_long, king_to_long)
|
||||||
if all_occupancies & 0x6000000000000000 == 0 {
|
}
|
||||||
moves.push(Move::new(60, 62))
|
_ => (),
|
||||||
}
|
};
|
||||||
}
|
moves
|
||||||
CastlingRights::Long => {
|
}
|
||||||
if all_occupancies & 0xe00000000000000 == 0 {
|
|
||||||
moves.push(Move::new(60, 58))
|
fn king_and_adj_square_safety(board: &Board, color: Color) -> Castle {
|
||||||
}
|
let (king_at, opponent_color) = match color {
|
||||||
}
|
Color::White => (3, Color::Black),
|
||||||
CastlingRights::None => (),
|
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)]
|
#[cfg(test)]
|
||||||
|
|||||||
Reference in New Issue
Block a user