added (failing) perft, debugging required

This commit is contained in:
Moritz 2025-11-14 13:56:49 +01:00
parent 2aca5ed841
commit 196e5ba265
4 changed files with 45 additions and 78 deletions

View file

@ -1,3 +1,4 @@
// FILENAME: legal_check.rs
use crate::board::{Board, Color, PieceType};
use crate::movegen::tables::{get_bishop_attacks, get_rook_attacks, ATTACKING_PAWNS, KING_ATTACKS, KNIGHT_ATTACKS, MAGICS_BISHOP, MAGICS_ROOK, PREMASKS_BISHOP, PREMASKS_ROOK, RELEVANT_BITS_BISHOP, RELEVANT_BITS_ROOK};
use crate::square::{Square, SQUARES};
@ -33,6 +34,7 @@ pub fn is_square_attacked(board: &Board, square: Square, color: Color) -> bool {
// 2. Sliding
let blockers = board.all_occupied;
let queens = board.pieces[PieceType::Queen as usize][color as usize];
// 2.1 Bishop (and part of Queen)
let premask_bishop = PREMASKS_BISHOP[square as usize];
@ -43,8 +45,8 @@ pub fn is_square_attacked(board: &Board, square: Square, color: Color) -> bool {
let magic_index_bishop = ((blockers & premask_bishop).wrapping_mul(magic_bishop)) >> shift_bishop;
let bishop_attackable_squares = attack_table_bishop[square as usize][magic_index_bishop as usize];
let bishops = board.pieces[PieceType::Bishop as usize][color as usize];
if (bishop_attackable_squares & bishops) != 0 {
let bishops_and_queens = board.pieces[PieceType::Bishop as usize][color as usize] | queens;
if (bishop_attackable_squares & bishops_and_queens) != 0 {
return true;
}
@ -57,15 +59,8 @@ pub fn is_square_attacked(board: &Board, square: Square, color: Color) -> bool {
let magic_index_rook = ((blockers & premask_rook).wrapping_mul(magic_rook)) >> shift_rook;
let rook_attackable_squares = attack_table_rook[square as usize][magic_index_rook as usize];
let rooks = board.pieces[PieceType::Rook as usize][color as usize];
if (rook_attackable_squares & rooks) != 0 {
return true;
}
// 2.3 Queens
let queen_attackable_squares = bishop_attackable_squares | rook_attackable_squares;
let queens = board.pieces[PieceType::Queen as usize][color as usize];
if (queen_attackable_squares & queens) != 0 {
let rooks_and_queens = board.pieces[PieceType::Rook as usize][color as usize] | queens;
if (rook_attackable_squares & rooks_and_queens) != 0 {
return true;
}

View file

@ -1,3 +1,4 @@
// FILENAME: non_sliders.rs
use crate::board::*;
use crate::movegen::legal_check::is_square_attacked;
use crate::r#move::*;
@ -68,9 +69,8 @@ pub fn generate_king_moves(board: &Board, list: &mut MoveList) {
// Kingside (OO)
if (board.castling_rights & CASTLING_WK_FLAG) != 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) {
// Check F1 (path). G1 (landing) is checked by perft function.
if !is_square_attacked(board, Square::F1, Color::Black) {
list.push(Move::new(Square::E1, Square::G1, MOVE_FLAG_WK_CASTLE));
}
}
@ -78,9 +78,8 @@ pub fn generate_king_moves(board: &Board, list: &mut MoveList) {
// Queenside (OOO)
if (board.castling_rights & CASTLING_WQ_FLAG) != 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) {
// Check D1 (path). C1 (landing) is checked by perft function. B1 is irrelevant.
if !is_square_attacked(board, Square::D1, Color::Black) {
list.push(Move::new(Square::E1, Square::C1, MOVE_FLAG_WQ_CASTLE));
}
}
@ -94,9 +93,8 @@ pub fn generate_king_moves(board: &Board, list: &mut MoveList) {
// Kingside (OO)
if (board.castling_rights & CASTLING_BK_FLAG) != 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) {
// Check F8 (path). G8 (landing) is checked by perft function.
if !is_square_attacked(board, Square::F8, Color::White) {
list.push(Move::new(Square::E8, Square::G8, MOVE_FLAG_BK_CASTLE));
}
}
@ -104,9 +102,8 @@ pub fn generate_king_moves(board: &Board, list: &mut MoveList) {
// Queenside (OOO)
if (board.castling_rights & CASTLING_BQ_FLAG) != 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) {
// Check D8 (path). C8 (landing) is checked by perft function. B8 is irrelevant.
if !is_square_attacked(board, Square::D8, Color::White) {
list.push(Move::new(Square::E8, Square::C8, MOVE_FLAG_BQ_CASTLE));
}
}

View file

@ -1,55 +0,0 @@
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);
}

View file

@ -1,7 +1,37 @@
use chess_engine::board::Board;
use chess_engine::movegen::generate_pseudo_legal_moves;
use chess_engine::movegen::legal_check::is_king_attacked;
use chess_engine::r#move::MoveList;
fn count_legal_moves_recursive(board: &mut Board, depth: u8) -> u64 {
if depth == 0 {
return 1_u64;
}
let mut list = MoveList::new();
generate_pseudo_legal_moves(&board, &mut list);
let mut leaf_nodes = 0_u64;
for mv in list.iter() {
// Store the undo info when making the move
let undo_info = board.make_move(*mv);
if !is_king_attacked(board) {
leaf_nodes += count_legal_moves_recursive(board, depth - 1);
}
// Undo the move to restore the board state for the next iteration
board.undo_move(undo_info);
}
leaf_nodes
}
#[test]
fn perft() {
let mut board = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
println!("{}", count_legal_moves_recursive(&mut board, 4));
}
// "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" 7 3195901860 "false"
// "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" 5 193690690 "false"