// 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; 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 ); } }