// Eryn Wells use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet}; use crate::{ piece::{Color, Piece, PlacedPiece}, MoveBuilder, Position, }; use chessfriend_bitboard::BitBoard; use chessfriend_core::Direction; move_generator_declaration!(ClassicalMoveGenerator); impl<'pos> MoveGeneratorInternal for ClassicalMoveGenerator<'pos> { fn piece(color: Color) -> Piece { Piece::queen(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 blockers = position.occupied_squares(); let empty_squares = !blockers; let friendly_pieces = position.bitboard_for_color(color); let opposing_pieces = position.bitboard_for_color(color.other()); let mut all_moves = BitBoard::empty(); macro_rules! update_moves_with_ray { ($direction:ident, $occupied_squares:tt) => { let ray = BitBoard::ray(square, Direction::$direction); if let Some(first_occupied_square) = BitBoard::$occupied_squares(&(ray & blockers)).next() { let remainder = BitBoard::ray(first_occupied_square, Direction::$direction); let attack_ray = ray & !remainder; all_moves |= attack_ray; } else { all_moves |= ray; } }; } update_moves_with_ray!(NorthWest, occupied_squares_trailing); update_moves_with_ray!(North, occupied_squares_trailing); update_moves_with_ray!(NorthEast, occupied_squares_trailing); update_moves_with_ray!(East, occupied_squares_trailing); update_moves_with_ray!(SouthEast, occupied_squares); update_moves_with_ray!(South, occupied_squares); update_moves_with_ray!(SouthWest, occupied_squares); update_moves_with_ray!(West, occupied_squares); let quiet_moves_bb = all_moves & (empty_squares | !friendly_pieces); let capture_moves_bb = all_moves & opposing_pieces; let map_to_move = |sq| MoveBuilder::new(*piece, square, sq).build(); let quiet_moves = quiet_moves_bb.occupied_squares().map(map_to_move); 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::{piece, position, position::DiagramFormatter, Color}; use chessfriend_bitboard::{bitboard, BitBoard}; #[test] fn classical_single_queen_bitboard() { let pos = position![White Queen on B2]; let generator = ClassicalMoveGenerator::new(&pos, Color::White); let bitboard = generator.bitboard(); let expected = bitboard![ A2, C2, D2, E2, F2, G2, H2, // Rank B1, B3, B4, B5, B6, B7, B8, // File A1, C3, D4, E5, F6, G7, H8, // Diagonal C1, A3 // Anti-diagonal ]; assert_eq!( bitboard, expected, "actual:\n{}\nexpected:\n{}", bitboard, expected ); } /// Test that a rook can move up to, but not onto, a friendly piece. #[test] fn classical_single_queen_with_same_color_blocker_bitboard() { let pos = position![ White Queen on A1, White Knight on E1, ]; println!("{}", DiagramFormatter::new(&pos)); let generator = ClassicalMoveGenerator::new(&pos, Color::White); let bitboard = generator.bitboard(); let expected = BitBoard::new( 0b10000001_01000001_00100001_00010001_00001001_00000101_00000011_00001110, ); assert_eq!( bitboard, expected, "actual:\n{}\nexpected:\n{}", bitboard, expected ); } /// Test that a rook can move up to, and then capture, an enemy piece. #[test] fn classical_single_queen_with_opposing_color_blocker_bitboard() { let pos = position![ White Queen on B2, Black Knight on E5, ]; println!("{}", DiagramFormatter::new(&pos)); let generator = ClassicalMoveGenerator::new(&pos, Color::White); assert_eq!( generator.bitboard(), bitboard![ A2, C2, D2, E2, F2, G2, H2, // Rank B1, B3, B4, B5, B6, B7, B8, // File A1, C3, D4, E5, // Diagonal C1, A3 // Anti-diagonal ] ); } #[test] fn classical_single_queen_in_center() { let pos = position![White Queen on D3]; println!("{}", DiagramFormatter::new(&pos)); let generator = ClassicalMoveGenerator::new(&pos, Color::White); assert_eq!( generator.bitboard(), bitboard![ A3, B3, C3, E3, F3, G3, H3, // Rank D1, D2, D4, D5, D6, D7, D8, // File B1, C2, E4, F5, G6, H7, // Diagonal F1, E2, C4, B5, A6, // Anti-diagonal ] ); } }