chessfriend/position/src/move_generator/king.rs
2025-05-08 17:37:51 -07:00

228 lines
7.4 KiB
Rust

// Eryn Wells <eryn@erynwells.me>
//! Declares the KingMoveGenerator type. This struct is responsible for
//! generating the possible moves for the king in the given position.
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
use chessfriend_bitboard::BitBoard;
use chessfriend_board::{castle::Castle, Board};
use chessfriend_core::{PlacedPiece, Shape};
move_generator_declaration!(KingMoveGenerator, struct);
move_generator_declaration!(KingMoveGenerator, new);
move_generator_declaration!(KingMoveGenerator, getters);
impl MoveGeneratorInternal for KingMoveGenerator {
fn shape() -> Shape {
Shape::King
}
fn move_set_for_piece(
board: &Board,
placed_piece: &PlacedPiece,
_capture_mask: BitBoard,
_push_mask: BitBoard,
) -> MoveSet {
let piece = placed_piece.piece();
let color = piece.color();
let square = placed_piece.square();
let safe_squares = BitBoard::FULL;
let all_king_moves = BitBoard::king_moves(square);
let empty_squares = board.empty_squares();
let safe_empty_squares = empty_squares & safe_squares;
let opposing_pieces = board.bitboard_for_color(color.other());
let opposing_pieces_on_safe_squares = opposing_pieces & safe_squares;
let quiet_moves = all_king_moves & safe_empty_squares;
let capture_moves = all_king_moves & opposing_pieces_on_safe_squares;
let mut move_set = MoveSet::new(*placed_piece)
.quiet_moves(quiet_moves)
.capture_moves(capture_moves);
if board.player_can_castle(color, Castle::KingSide) {
move_set.kingside_castle();
}
if board.player_can_castle(color, Castle::QueenSide) {
move_set.queenside_castle();
}
move_set
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{assert_move_list, test_position, testing::*};
use chessfriend_bitboard::bitboard;
use chessfriend_board::castle::Castle;
use chessfriend_core::{piece, Color, Square};
use chessfriend_moves::{Builder as MoveBuilder, Move};
use std::collections::HashSet;
#[test]
fn one_king() -> TestResult {
let pos = test_position![White King on E4];
let generator =
KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
assert_eq!(
generator._test_bitboard(),
bitboard![E5 F5 F4 F3 E3 D3 D4 D5]
);
let builder = MoveBuilder::push(&piece!(White King on E4));
let expected_moves: HashSet<Move> = HashSet::from_iter([
builder.clone().to(Square::D5).build()?,
builder.clone().to(Square::E5).build()?,
builder.clone().to(Square::F5).build()?,
builder.clone().to(Square::F4).build()?,
builder.clone().to(Square::F3).build()?,
builder.clone().to(Square::E3).build()?,
builder.clone().to(Square::D3).build()?,
builder.clone().to(Square::D4).build()?,
]);
let generated_moves: HashSet<Move> = generator.iter().collect();
assert_move_list!(generated_moves, expected_moves, pos);
Ok(())
}
#[test]
fn one_king_corner() -> TestResult {
let pos = test_position![White King on A1];
let generator =
KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
let generated_bitboard = generator._test_bitboard();
let expected_bitboard = bitboard![A2 B2 B1];
assert_eq!(
generator._test_bitboard(),
bitboard![A2 B2 B1],
"Generated:\n{generated_bitboard}\nExpected:\n{expected_bitboard}"
);
let builder = MoveBuilder::push(&piece!(White King on A1));
let expected_moves = [
builder.clone().to(Square::A2).build()?,
builder.clone().to(Square::B1).build()?,
builder.clone().to(Square::B2).build()?,
];
let mut generated_moves: HashSet<_> = generator.iter().collect();
for ex_move in expected_moves {
assert!(
generated_moves.remove(&ex_move),
"{:#?} was not generated",
&ex_move
);
}
assert!(
generated_moves.is_empty(),
"Moves unexpectedly present: {:#?}",
generated_moves
);
Ok(())
}
#[test]
fn black_king_in_check_by_rook() {
let pos = test_position!(Black, [
White King on E1,
White Rook on E4,
Black King on E7,
]);
assert!(pos.is_king_in_check());
let generator =
KingMoveGenerator::new(&pos.board, Color::Black, BitBoard::FULL, BitBoard::FULL);
let generated_moves = generator._test_bitboard();
let expected_moves = bitboard![F8 F7 F6 D6 D7 D8];
assert_eq!(generated_moves, expected_moves);
}
#[test]
fn white_king_unobstructed_castles() -> TestResult {
let pos = test_position!(
White King on E1,
White Rook on A1,
White Rook on H1,
);
assert!(pos.player_can_castle(Color::White, Castle::KingSide));
assert!(pos.player_can_castle(Color::White, Castle::QueenSide));
let generator =
KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
let generated_moves: HashSet<Move> = generator.iter().collect();
assert!(generated_moves
.contains(&MoveBuilder::castling(Color::White, Castle::KingSide).build()?));
assert!(generated_moves
.contains(&MoveBuilder::castling(Color::White, Castle::QueenSide).build()?));
Ok(())
}
#[test]
fn white_king_obstructed_queenside_castle() -> TestResult {
let pos = test_position!(
White King on E1,
White Knight on B1,
White Rook on A1,
White Rook on H1,
);
assert!(pos.player_can_castle(Color::White, Castle::KingSide));
assert!(!pos.player_can_castle(Color::White, Castle::QueenSide));
let generator =
KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
let generated_moves: HashSet<Move> = generator.iter().collect();
assert!(generated_moves
.contains(&MoveBuilder::castling(Color::White, Castle::KingSide).build()?));
assert!(!generated_moves
.contains(&MoveBuilder::castling(Color::White, Castle::QueenSide).build()?));
Ok(())
}
#[test]
fn white_king_obstructed_kingside_castle() -> TestResult {
let pos = test_position!(
White King on E1,
White Rook on A1,
White Knight on G1,
White Rook on H1,
);
assert!(!pos.player_can_castle(Color::White, Castle::KingSide));
assert!(pos.player_can_castle(Color::White, Castle::QueenSide));
let generator =
KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
let generated_moves: HashSet<Move> = generator.iter().collect();
assert!(!generated_moves
.contains(&MoveBuilder::castling(Color::White, Castle::KingSide).build()?));
assert!(generated_moves
.contains(&MoveBuilder::castling(Color::White, Castle::QueenSide).build()?));
Ok(())
}
}