// Eryn Wells //! 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 crate::{r#move::Castle, Move, MoveBuilder, Position}; use chessfriend_bitboard::BitBoard; use chessfriend_core::{Color, Piece, PlacedPiece}; move_generator_declaration!(KingMoveGenerator, struct); move_generator_declaration!(KingMoveGenerator, new); move_generator_declaration!(KingMoveGenerator, getters); impl KingMoveGenerator { #[allow(unused_variables)] fn king_side_castle(position: &Position, color: Color) -> Option { if !position.player_has_right_to_castle(color, Castle::KingSide) { return None; } // TODO: Implement king side castle. None } #[allow(unused_variables)] fn queen_side_castle(position: &Position, color: Color) -> Option { if !position.player_has_right_to_castle(color, Castle::QueenSide) { return None; } // TODO: Implement queen side castle. None } } impl MoveGeneratorInternal for KingMoveGenerator { fn piece(color: Color) -> Piece { Piece::king(color) } fn move_set_for_piece(position: &Position, placed_piece: PlacedPiece) -> MoveSet { let piece = placed_piece.piece(); let color = piece.color(); let square = placed_piece.square(); let empty_squares = position.empty_squares(); let opposing_pieces = position.bitboard_for_color(color.other()); let all_moves = BitBoard::king_moves(square); let quiet_moves_bb = all_moves & empty_squares; let capture_moves_bb = all_moves & opposing_pieces; // TODO: Handle checks. Prevent moving a king to a square attacked by a // piece of the opposite color. let map_to_move = |sq| MoveBuilder::new(*piece, square, sq).build(); let king_side_castle = Self::king_side_castle(position, color); let queen_side_castle = Self::queen_side_castle(position, color); let quiet_moves = quiet_moves_bb .occupied_squares() .map(map_to_move) .chain(king_side_castle.iter().cloned()) .chain(queen_side_castle.iter().cloned()); let capture_moves = capture_moves_bb.occupied_squares().map(map_to_move); MoveSet::new(placed_piece) .quiet_moves(quiet_moves_bb, quiet_moves) .capture_moves(capture_moves_bb, capture_moves) } } #[cfg(test)] mod tests { use super::*; use crate::{position, r#move::AlgebraicMoveFormatter, PositionBuilder}; use chessfriend_bitboard::bitboard; use chessfriend_core::{piece, Square}; use std::collections::HashSet; #[test] fn one_king() { let pos = position![White King on E4]; let generator = KingMoveGenerator::new(&pos, Color::White); assert_eq!( generator.bitboard(), bitboard![E5, F5, F4, F3, E3, D3, D4, D5] ); let expected_moves = [ MoveBuilder::new(piece!(White King), Square::E4, Square::D5).build(), MoveBuilder::new(piece!(White King), Square::E4, Square::E5).build(), MoveBuilder::new(piece!(White King), Square::E4, Square::F5).build(), MoveBuilder::new(piece!(White King), Square::E4, Square::F4).build(), MoveBuilder::new(piece!(White King), Square::E4, Square::F3).build(), MoveBuilder::new(piece!(White King), Square::E4, Square::E3).build(), MoveBuilder::new(piece!(White King), Square::E4, Square::D3).build(), MoveBuilder::new(piece!(White King), Square::E4, Square::D4).build(), ]; let mut generated_moves: HashSet = generator.iter().cloned().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 ); } #[test] fn one_king_corner() { let pos = position![White King on A1]; let generator = KingMoveGenerator::new(&pos, Color::White); let generated_bitboard = generator.bitboard(); let expected_bitboard = bitboard![A2, B2, B1]; assert_eq!( generator.bitboard(), bitboard![A2, B2, B1], "Generated:\n{generated_bitboard}\nExpected:\n{expected_bitboard}" ); let expected_moves = [ MoveBuilder::new(piece!(White King), Square::A1, Square::A2).build(), MoveBuilder::new(piece!(White King), Square::A1, Square::B1).build(), MoveBuilder::new(piece!(White King), Square::A1, Square::B2).build(), ]; let mut generated_moves: HashSet = generator.iter().cloned().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 ); } #[test] fn black_king_in_check_by_rook() { let pos = PositionBuilder::new() .place_piece(piece!(White King on E1)) .place_piece(piece!(White Rook on E4)) .place_piece(piece!(Black King on E7)) .to_move(Color::Black) .build(); assert!(pos.is_king_in_check()); let generator = KingMoveGenerator::new(&pos, Color::Black); let generated_moves: HashSet = generator.iter().cloned().collect(); let king = piece!(Black King); let from_square = Square::E7; let expected_moves = HashSet::from_iter([ MoveBuilder::new(king, from_square, Square::D6).build(), MoveBuilder::new(king, from_square, Square::D7).build(), MoveBuilder::new(king, from_square, Square::D8).build(), MoveBuilder::new(king, from_square, Square::F6).build(), MoveBuilder::new(king, from_square, Square::F7).build(), MoveBuilder::new(king, from_square, Square::F8).build(), ]); assert_eq!( generated_moves, expected_moves, "Difference: {:?}", generated_moves .symmetric_difference(&expected_moves) .map(|mv| format!("{}", AlgebraicMoveFormatter::new(mv, &pos))) .collect::>() ); } }