// Eryn Wells use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet}; use crate::{ piece::{Color, Piece, PlacedPiece}, square::Direction, BitBoard, Move, Position, }; 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| Move::new(piece, square, sq); 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::{Color, Piece}, position::DiagramFormatter, BitBoard, Position, Square, }; #[test] fn classical_single_queen_bitboard() { let mut pos = Position::empty(); [(Piece::queen(Color::White), Square::A1)] .into_iter() .for_each(|(p, sq)| { pos.place_piece(p, sq) .expect(&format!("Unable to place {:?} on {}", &p, &sq)) }); let generator = ClassicalMoveGenerator::new(&pos, Color::White); let bitboard = generator.bitboard(); let expected = BitBoard::new( 0b10000001_01000001_00100001_00010001_00001001_00000101_00000011_11111110, ); 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 mut pos = Position::empty(); [ (Piece::queen(Color::White), Square::A1), (Piece::knight(Color::White), Square::E1), ] .into_iter() .for_each(|(p, sq)| { pos.place_piece(p, sq) .expect(&format!("Unable to place {} on {}", p, sq)) }); 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 mut pos = Position::empty(); [ (Piece::queen(Color::White), Square::A1), (Piece::knight(Color::Black), Square::E5), ] .into_iter() .for_each(|(p, sq)| { pos.place_piece(p, sq) .expect(&format!("Unable to place {} on {}", p, sq)) }); let generator = ClassicalMoveGenerator::new(&pos, Color::White); assert_eq!( generator.bitboard(), BitBoard::new( 0b00000001_00000001_00000001_00010001_00001001_00000101_00000011_11111110, ) ); } #[test] fn classical_single_queen_in_center() { let mut pos = Position::empty(); [(Piece::queen(Color::White), Square::E4)] .into_iter() .for_each(|(p, sq)| { pos.place_piece(p, sq) .expect(&format!("Unable to place {} on {}", p, sq)) }); let generator = ClassicalMoveGenerator::new(&pos, Color::White); assert_eq!( generator.bitboard(), BitBoard::new( 0b00010001_10010010_01010100_00111000_11101111_00111000_01010100_10010010 ) ); } }