prevented illegal castling
This commit is contained in:
parent
2a100c8b50
commit
2aca5ed841
4 changed files with 99 additions and 11 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::board::{Board, Color, PieceType};
|
use crate::board::{Board, Color, PieceType};
|
||||||
use crate::r#move::MoveList;
|
use crate::r#move::{MoveList, Move};
|
||||||
use crate::square::Square;
|
use crate::square::Square;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
|
@ -213,3 +213,9 @@ impl fmt::Display for Square {
|
||||||
write!(f, "{}{}", file, rank)
|
write!(f, "{}{}", file, rank)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Move {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.to_algebraic())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,6 @@ impl MoveList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn push(&mut self, mv: Move) {
|
pub fn push(&mut self, mv: Move) {
|
||||||
debug_assert!(self.count < 256, "Move list overflow!");
|
debug_assert!(self.count < 256, "Move list overflow!");
|
||||||
|
|
||||||
|
|
@ -88,20 +87,21 @@ impl MoveList {
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.count
|
self.count
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.count == 0
|
self.count == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn iter(&self) -> slice::Iter<'_, Move> {
|
pub fn iter(&self) -> slice::Iter<'_, Move> {
|
||||||
self.moves[..self.count].iter()
|
self.moves[..self.count].iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, mv: &Move) -> bool {
|
||||||
|
self.moves.contains(mv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UndoMove {
|
pub struct UndoMove {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::board::*;
|
use crate::board::*;
|
||||||
|
use crate::movegen::legal_check::is_square_attacked;
|
||||||
use crate::r#move::*;
|
use crate::r#move::*;
|
||||||
use crate::square::*;
|
use crate::square::*;
|
||||||
use super::tables::{KING_ATTACKS, KNIGHT_ATTACKS};
|
use super::tables::{KING_ATTACKS, KNIGHT_ATTACKS};
|
||||||
|
|
@ -30,7 +31,6 @@ pub fn generate_knight_moves(board: &Board, list: &mut MoveList) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_king_moves(board: &Board, list: &mut MoveList) {
|
pub fn generate_king_moves(board: &Board, list: &mut MoveList) {
|
||||||
// TODO no castle when square or piece under attack
|
|
||||||
let enemy_occupied = board.occupied[!board.side_to_move as usize];
|
let enemy_occupied = board.occupied[!board.side_to_move as usize];
|
||||||
let friendly_king = board.pieces[PieceType::King as usize][board.side_to_move as usize];
|
let friendly_king = board.pieces[PieceType::King as usize][board.side_to_move as usize];
|
||||||
|
|
||||||
|
|
@ -57,32 +57,59 @@ pub fn generate_king_moves(board: &Board, list: &mut MoveList) {
|
||||||
attacks &= attacks - 1;
|
attacks &= attacks - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Optimize the is attacked testing on castling
|
||||||
// 2. Generate castling king moves
|
// 2. Generate castling king moves
|
||||||
if board.side_to_move == Color::White {
|
if board.side_to_move == Color::White {
|
||||||
|
// King must not be in check to castle
|
||||||
|
if is_square_attacked(board, Square::E1, Color::Black) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Kingside (OO)
|
// Kingside (OO)
|
||||||
if (board.castling_rights & CASTLING_WK_FLAG) != 0 {
|
if (board.castling_rights & CASTLING_WK_FLAG) != 0 {
|
||||||
if (board.all_occupied & CASTLING_WK_MASK) == 0 {
|
if (board.all_occupied & CASTLING_WK_MASK) == 0 {
|
||||||
|
// Check F1 (path) and G1 (landing)
|
||||||
|
if !is_square_attacked(board, Square::F1, Color::Black) &&
|
||||||
|
!is_square_attacked(board, Square::G1, Color::Black) {
|
||||||
list.push(Move::new(Square::E1, Square::G1, MOVE_FLAG_WK_CASTLE));
|
list.push(Move::new(Square::E1, Square::G1, MOVE_FLAG_WK_CASTLE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Queenside (OOO)
|
// Queenside (OOO)
|
||||||
if (board.castling_rights & CASTLING_WQ_FLAG) != 0 {
|
if (board.castling_rights & CASTLING_WQ_FLAG) != 0 {
|
||||||
if (board.all_occupied & CASTLING_WQ_MASK) == 0 {
|
if (board.all_occupied & CASTLING_WQ_MASK) == 0 {
|
||||||
|
// Check D1 (path) and C1 (landing). B1 is irrelevant.
|
||||||
|
if !is_square_attacked(board, Square::D1, Color::Black) &&
|
||||||
|
!is_square_attacked(board, Square::C1, Color::Black) {
|
||||||
list.push(Move::new(Square::E1, Square::C1, MOVE_FLAG_WQ_CASTLE));
|
list.push(Move::new(Square::E1, Square::C1, MOVE_FLAG_WQ_CASTLE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else { // Black
|
} else { // Black
|
||||||
|
// King must not be in check to castle
|
||||||
|
if is_square_attacked(board, Square::E8, Color::White) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Kingside (OO)
|
// Kingside (OO)
|
||||||
if (board.castling_rights & CASTLING_BK_FLAG) != 0 {
|
if (board.castling_rights & CASTLING_BK_FLAG) != 0 {
|
||||||
if (board.all_occupied & CASTLING_BK_MASK) == 0 {
|
if (board.all_occupied & CASTLING_BK_MASK) == 0 {
|
||||||
|
// Check F8 (path) and G8 (landing)
|
||||||
|
if !is_square_attacked(board, Square::F8, Color::White) &&
|
||||||
|
!is_square_attacked(board, Square::G8, Color::White) {
|
||||||
list.push(Move::new(Square::E8, Square::G8, MOVE_FLAG_BK_CASTLE));
|
list.push(Move::new(Square::E8, Square::G8, MOVE_FLAG_BK_CASTLE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Queenside (OOO)
|
// Queenside (OOO)
|
||||||
if (board.castling_rights & CASTLING_BQ_FLAG) != 0 {
|
if (board.castling_rights & CASTLING_BQ_FLAG) != 0 {
|
||||||
if (board.all_occupied & CASTLING_BQ_MASK) == 0 {
|
if (board.all_occupied & CASTLING_BQ_MASK) == 0 {
|
||||||
|
// Check D8 (path) and C8 (landing). B8 is irrelevant.
|
||||||
|
if !is_square_attacked(board, Square::D8, Color::White) &&
|
||||||
|
!is_square_attacked(board, Square::C8, Color::White) {
|
||||||
list.push(Move::new(Square::E8, Square::C8, MOVE_FLAG_BQ_CASTLE));
|
list.push(Move::new(Square::E8, Square::C8, MOVE_FLAG_BQ_CASTLE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
55
tests/castling_under_attack.rs
Normal file
55
tests/castling_under_attack.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
use chess_engine::board::Board;
|
||||||
|
use chess_engine::movegen::generate_pseudo_legal_moves;
|
||||||
|
use chess_engine::r#move::{Move, MoveList, MOVE_FLAG_WK_CASTLE, MOVE_FLAG_WQ_CASTLE, MOVE_FLAG_BK_CASTLE, MOVE_FLAG_BQ_CASTLE};
|
||||||
|
use chess_engine::square::Square;
|
||||||
|
|
||||||
|
fn assert_move_generated(fen:&str, mv: Move, contains: bool) {
|
||||||
|
let mut list = MoveList::new();
|
||||||
|
let mut board = Board::from_fen(fen);
|
||||||
|
generate_pseudo_legal_moves(&mut board, &mut list);
|
||||||
|
assert_eq!(list.contains(&mv), contains, "board '{fen}' contains move '{mv}': {contains}")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wk_castle_under_attacks() {
|
||||||
|
let kingside_castle = Move::new(Square::E1, Square::G1, MOVE_FLAG_WK_CASTLE);
|
||||||
|
assert_move_generated("4k3/8/3r4/8/8/8/8/4K2R w K - 0 1", kingside_castle, true);
|
||||||
|
assert_move_generated("4k3/8/4r3/8/8/8/8/4K2R w K - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("4k3/8/5r2/8/8/8/8/4K2R w K - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("4k3/8/6r1/8/8/8/8/4K2R w K - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("4k3/8/7r/8/8/8/8/4K2R w K - 0 1", kingside_castle, true);
|
||||||
|
assert_move_generated("4k3/8/8/8/8/8/8/r3K2R w K - 0 1", kingside_castle, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wq_castle_under_attacks() {
|
||||||
|
let kingside_castle = Move::new(Square::E1, Square::C1, MOVE_FLAG_WQ_CASTLE);
|
||||||
|
assert_move_generated("4k3/8/5r2/8/8/8/8/R3K3 w Q - 0 1", kingside_castle, true);
|
||||||
|
assert_move_generated("4k3/8/4r3/8/8/8/8/R3K3 w Q - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("4k3/8/3r4/8/8/8/8/R3K3 w Q - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("4k3/8/2r5/8/8/8/8/R3K3 w Q - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("4k3/8/1r6/8/8/8/8/R3K3 w Q - 0 1", kingside_castle, true);
|
||||||
|
assert_move_generated("4k3/8/r7/8/8/8/8/R3K3 w Q - 0 1", kingside_castle, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bk_castle_under_attacks() {
|
||||||
|
let kingside_castle = Move::new(Square::E8, Square::G8, MOVE_FLAG_BK_CASTLE);
|
||||||
|
assert_move_generated("4k2r/8/8/8/8/3R4/8/4K3 b k - 0 1", kingside_castle, true);
|
||||||
|
assert_move_generated("4k2r/8/8/8/8/4R3/8/4K3 b k - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("4k2r/8/8/8/8/5R2/8/4K3 b k - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("4k2r/8/8/8/8/6R1/8/4K3 b k - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("4k2r/8/8/8/8/7R/8/4K3 b k - 0 1", kingside_castle, true);
|
||||||
|
assert_move_generated("2R1k2r/8/8/8/8/8/8/4K3 b k - 0 1", kingside_castle, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bq_castle_under_attacks() {
|
||||||
|
let kingside_castle = Move::new(Square::E8, Square::C8, MOVE_FLAG_BQ_CASTLE);
|
||||||
|
assert_move_generated("r3k3/8/8/8/8/5R2/8/4K3 b q - 0 1", kingside_castle, true);
|
||||||
|
assert_move_generated("r3k3/8/8/8/8/4R3/8/4K3 b q - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("r3k3/8/8/8/8/3R4/8/4K3 b q - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("r3k3/8/8/8/8/2R5/8/4K3 b q - 0 1", kingside_castle, false);
|
||||||
|
assert_move_generated("r3k3/8/8/8/8/1R6/8/4K3 b q - 0 1", kingside_castle, true);
|
||||||
|
assert_move_generated("r3k3/8/8/8/8/R7/8/4K3 b q - 0 1", kingside_castle, true);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue