castling: simplify castle updates

This commit is contained in:
dece 2020-06-20 21:05:54 +02:00
parent b62dd1c076
commit 8b0f4c9255
4 changed files with 45 additions and 124 deletions

View file

@ -1,19 +1,21 @@
//! Castling flags. //! Castling flags.
pub const CASTLING_WH_K: u8 = 0b00000001; pub type Castle = u8;
pub const CASTLING_WH_Q: u8 = 0b00000010;
pub const CASTLING_WH_MASK: u8 = 0b00000011; pub const CASTLING_WH_K: Castle = 0b00000001;
pub const CASTLING_BL_K: u8 = 0b00000100; pub const CASTLING_WH_Q: Castle = 0b00000010;
pub const CASTLING_BL_Q: u8 = 0b00001000; pub const CASTLING_WH_MASK: Castle = 0b00000011;
pub const CASTLING_BL_MASK: u8 = 0b00001100; pub const CASTLING_BL_K: Castle = 0b00000100;
pub const CASTLING_K_MASK: u8 = 0b00000101; pub const CASTLING_BL_Q: Castle = 0b00001000;
pub const CASTLING_Q_MASK: u8 = 0b00001010; pub const CASTLING_BL_MASK: Castle = 0b00001100;
pub const CASTLING_MASK: u8 = 0b00001111; pub const CASTLING_K_MASK: Castle = 0b00000101;
pub const CASTLING_Q_MASK: Castle = 0b00001010;
pub const CASTLING_MASK: Castle = 0b00001111;
/// Castling sides parameters. /// Castling sides parameters.
/// ///
/// For both sides, the 3-uple contains files that should be empty /// For both sides, the 3-uple contains files that should be empty
/// and not attacked, an optional file that should be empty for /// and not attacked, an optional file that should be empty for
/// queen-side, and the castling side-mask. /// queen-side, and the castling side-mask.
pub const CASTLING_SIDES: [([i8; 2], Option<i8>, u8); 2] = pub const CASTLING_SIDES: [([i8; 2], Option<i8>, Castle); 2] =
[([5i8, 6i8], None, CASTLING_K_MASK), ([3i8, 2i8], Some(1i8), CASTLING_Q_MASK)]; [([5i8, 6i8], None, CASTLING_K_MASK), ([3i8, 2i8], Some(1i8), CASTLING_Q_MASK)];

View file

@ -6,9 +6,6 @@ use crate::board::*;
use crate::castling::*; use crate::castling::*;
use crate::rules::GameState; use crate::rules::GameState;
const START_WH_K_POS: Square = E1;
const START_BL_K_POS: Square = E8;
/// A movement, with before/after positions and optional promotion. /// A movement, with before/after positions and optional promotion.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct Move { pub struct Move {
@ -39,119 +36,50 @@ impl Move {
/// Apply this move to `board` and `game_state`. /// Apply this move to `board` and `game_state`.
pub fn apply_to(&self, board: &mut Board, game_state: &mut GameState) { pub fn apply_to(&self, board: &mut Board, game_state: &mut GameState) {
// If a rook is taken, remove its castling option. Needs to be checked before we update // If a king moves, remove it from castling options.
// board. Note that we only check for a piece going to rook's initial position: it means if self.source == E1 { game_state.castling &= !CASTLING_WH_MASK; }
// the rook either moved previously, or it has been taken. else if self.source == E8 { game_state.castling &= !CASTLING_BL_MASK; }
match self.source { // Same for rooks.
A1 => { game_state.castling &= !CASTLING_WH_Q; } if self.source == A1 || self.dest == A1 { game_state.castling &= !CASTLING_WH_Q; }
H1 => { game_state.castling &= !CASTLING_WH_K; } else if self.source == H1 || self.dest == H1 { game_state.castling &= !CASTLING_WH_K; }
A8 => { game_state.castling &= !CASTLING_BL_Q; } else if self.source == A8 || self.dest == A8 { game_state.castling &= !CASTLING_BL_Q; }
H8 => { game_state.castling &= !CASTLING_BL_K; } else if self.source == H8 || self.dest == H8 { game_state.castling &= !CASTLING_BL_K; }
_ => {}
}
// Update board and game state. // Update board and game state.
self.apply_to_board(board); self.apply_to_board(board);
game_state.color = opposite(game_state.color); game_state.color = opposite(game_state.color);
// If the move is a castle, remove it from castling options.
if let Some(castle) = self.get_castle() {
match castle {
CASTLING_WH_K | CASTLING_WH_Q => game_state.castling &= !CASTLING_WH_MASK,
CASTLING_BL_K | CASTLING_BL_Q => game_state.castling &= !CASTLING_BL_MASK,
_ => {}
};
}
// Else, check if the king or a rook moved to update castling options.
else {
let color = board.get_color_on(self.dest);
if color == WHITE && game_state.castling & CASTLING_WH_MASK != 0 {
match board.get_piece_on(self.dest) {
KING => {
if self.source == E1 {
game_state.castling &= !CASTLING_WH_MASK;
}
}
ROOK => {
if self.source == A1 {
game_state.castling &= !CASTLING_WH_Q;
} else if self.source == H1 {
game_state.castling &= !CASTLING_WH_K;
}
}
_ => {}
}
} else if color == BLACK && game_state.castling & CASTLING_BL_MASK != 0 {
match board.get_piece_on(self.dest) {
KING => {
if self.source == E8 {
game_state.castling &= !CASTLING_BL_MASK;
}
}
ROOK => {
if self.source == A8 {
game_state.castling &= !CASTLING_BL_Q;
} else if self.source == H8 {
game_state.castling &= !CASTLING_BL_K;
}
}
_ => {}
}
}
}
} }
/// Apply the move into `board`. /// Apply the move into `board`.
pub fn apply_to_board(&self, board: &mut Board) { pub fn apply_to_board(&self, board: &mut Board) {
let piece = board.get_piece_on(self.source);
// If a king is castling, apply special move.
if piece == KING {
if let Some(castle) = self.get_castle() { if let Some(castle) = self.get_castle() {
match castle { match castle {
CASTLING_WH_K => { CASTLING_WH_K => { board.move_square(E1, G1); board.move_square(H1, F1); }
board.move_square(START_WH_K_POS, G1); CASTLING_WH_Q => { board.move_square(E1, C1); board.move_square(A1, D1); }
board.move_square(H1, F1); CASTLING_BL_K => { board.move_square(E8, G8); board.move_square(H8, F8); }
CASTLING_BL_Q => { board.move_square(E8, C8); board.move_square(A8, D8); }
_ => { panic!("Invalid castle.") }
} }
CASTLING_WH_Q => { return
board.move_square(START_WH_K_POS, C1);
board.move_square(A1, D1);
} }
CASTLING_BL_K => {
board.move_square(START_BL_K_POS, G8);
board.move_square(H8, F8);
} }
CASTLING_BL_Q => {
board.move_square(START_BL_K_POS, C8);
board.move_square(A8, D8);
}
_ => {}
}
} else {
board.move_square(self.source, self.dest); board.move_square(self.source, self.dest);
if let Some(piece) = self.promotion { if let Some(piece) = self.promotion {
let color = board.get_color_on(self.dest); let color = board.get_color_on(self.dest);
board.set_square(self.dest, color, piece); board.set_square(self.dest, color, piece);
} }
} }
}
/// Get the corresponding castling flag for this move. /// Get the corresponding castling flag for this move.
pub fn get_castle(&self) -> Option<u8> { pub fn get_castle(&self) -> Option<Castle> {
if self.source == E1 { match (self.source, self.dest) {
if self.dest == C1 { (E1, C1) => Some(CASTLING_WH_Q),
Some(CASTLING_WH_Q) (E1, G1) => Some(CASTLING_WH_K),
} else if self.dest == G1 { (E8, C8) => Some(CASTLING_BL_Q),
Some(CASTLING_WH_K) (E8, G8) => Some(CASTLING_BL_K),
} else { _ => None,
None
}
} else if self.source == E8 {
if self.dest == C8 {
Some(CASTLING_BL_Q)
} else if self.dest == G8 {
Some(CASTLING_BL_K)
} else {
None
}
} else {
None
} }
} }

View file

@ -54,11 +54,6 @@ impl fmt::Display for Node {
let mut s = vec!(); let mut s = vec!();
self.board.draw(&mut s); self.board.draw(&mut s);
let board_drawing = String::from_utf8_lossy(&s).to_string(); let board_drawing = String::from_utf8_lossy(&s).to_string();
write!( write!(f, "* Board:\n{}\nGame state: {}", board_drawing, self.game_state)
f,
"* Board:\n{}\n\
* Game state:\n{}",
board_drawing, self.game_state
)
} }
} }

View file

@ -43,11 +43,7 @@ impl std::fmt::Display for GameState {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!( write!(
f, f,
"- color: {}\n\ "[color: {}, castling: {:04b}, en_passant: {}, halfmove: {}, fullmove: {}]",
- castling: {:04b}\n\
- en_passant: {}\n\
- halfmove: {}\n\
- fullmove: {}",
color_to_string(self.color), self.castling, color_to_string(self.color), self.castling,
fen::en_passant_to_string(self.en_passant), fen::en_passant_to_string(self.en_passant),
self.halfmove, self.fullmove self.halfmove, self.fullmove