diff --git a/bitboard/src/bitboard.rs b/bitboard/src/bitboard.rs index 2f9740d..4904c3e 100644 --- a/bitboard/src/bitboard.rs +++ b/bitboard/src/bitboard.rs @@ -39,7 +39,7 @@ impl BitBoard { library::FILES[*file as usize] } - pub fn ray(sq: Square, dir: Direction) -> BitBoard { + pub fn ray(sq: Square, dir: Direction) -> &'static BitBoard { library::library().ray(sq, dir) } @@ -107,6 +107,24 @@ impl BitBoard { pub fn occupied_squares_trailing(&self) -> impl Iterator { LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) }) } + + pub fn first_occupied_square(&self) -> Option { + let leading_zeros = self.0.leading_zeros() as u8; + if leading_zeros < Square::NUM as u8 { + unsafe { Some(Square::from_index(Square::NUM as u8 - leading_zeros - 1)) } + } else { + None + } + } + + pub fn first_occupied_square_trailing(&self) -> Option { + let trailing_zeros = self.0.trailing_zeros() as u8; + if trailing_zeros < Square::NUM as u8 { + unsafe { Some(Square::from_index(trailing_zeros)) } + } else { + None + } + } } impl Default for BitBoard { @@ -377,4 +395,14 @@ mod tests { assert_eq!(BitBoard::from(Square::A1), BitBoard(0b1)); assert_eq!(BitBoard::from(Square::H8), BitBoard(1 << 63)); } + + #[test] + fn first_occupied_squares() { + let bb = bitboard![A8, E1]; + assert_eq!(bb.first_occupied_square(), Some(Square::A8)); + assert_eq!(bb.first_occupied_square_trailing(), Some(Square::E1)); + + let bb = bitboard![D6, E7, F8]; + assert_eq!(bb.first_occupied_square_trailing(), Some(Square::D6)); + } } diff --git a/bitboard/src/library.rs b/bitboard/src/library.rs index 41951c7..4615caa 100644 --- a/bitboard/src/library.rs +++ b/bitboard/src/library.rs @@ -220,8 +220,8 @@ impl MoveLibrary { ray } - pub(super) fn ray(&self, sq: Square, dir: Direction) -> BitBoard { - self.rays[sq as usize][dir as usize] + pub(super) fn ray(&self, sq: Square, dir: Direction) -> &BitBoard { + &self.rays[sq as usize][dir as usize] } pub(super) fn pawn_pushes(&self, sq: Square, color: Color) -> BitBoard { diff --git a/position/src/sight.rs b/position/src/sight.rs index 76005a2..6e87d95 100644 --- a/position/src/sight.rs +++ b/position/src/sight.rs @@ -26,6 +26,10 @@ pub(crate) trait SightExt { fn king_sight(&self, pieces: &PieceBitBoards) -> BitBoard; } +pub(crate) trait SliderRayToSquareExt { + fn ray_to_square(&self, square: Square) -> Option; +} + impl SightExt for PlacedPiece { fn sight(&self, pieces: &PieceBitBoards, en_passant_square: Option) -> BitBoard { match self.shape() { @@ -183,8 +187,80 @@ impl SightExt for PlacedPiece { } } +impl SliderRayToSquareExt for PlacedPiece { + fn ray_to_square(&self, target: Square) -> Option { + macro_rules! ray { + ($square:expr, $direction:ident) => { + ( + BitBoard::ray($square, Direction::$direction), + Direction::$direction, + ) + }; + } + + let square = self.square(); + let target_bitboard: BitBoard = target.into(); + + let ray_and_direction = match self.shape() { + Shape::Bishop => [ + ray!(square, NorthEast), + ray!(square, SouthEast), + ray!(square, SouthWest), + ray!(square, NorthWest), + ] + .into_iter() + .find(|(&ray, _)| !(target_bitboard & ray).is_empty()), + Shape::Rook => [ + ray!(square, North), + ray!(square, East), + ray!(square, South), + ray!(square, West), + ] + .into_iter() + .find(|(&ray, _)| !(target_bitboard & ray).is_empty()), + Shape::Queen => [ + ray!(square, North), + ray!(square, NorthEast), + ray!(square, East), + ray!(square, SouthEast), + ray!(square, South), + ray!(square, SouthWest), + ray!(square, West), + ray!(square, NorthWest), + ] + .into_iter() + .find(|(&ray, _)| !(target_bitboard & ray).is_empty()), + _ => None, + }; + + if let Some((ray, direction)) = ray_and_direction { + let first_occupied_square = match direction { + Direction::East + | Direction::NorthWest + | Direction::NorthEast + | Direction::North => ray.first_occupied_square_trailing(), + Direction::West + | Direction::SouthWest + | Direction::SouthEast + | Direction::South => ray.first_occupied_square(), + }; + + if let Some(occupied_square) = first_occupied_square { + let remainder = BitBoard::ray(target, direction); + return Some(ray & !remainder); + } + } + + None + } +} + #[cfg(test)] mod tests { + use super::*; + use chessfriend_bitboard::bitboard; + use chessfriend_core::{piece, Square}; + macro_rules! sight_test { ($test_name:ident, $position:expr, $piece:expr, $bitboard:expr) => { #[test] @@ -201,6 +277,12 @@ mod tests { }; } + #[test] + fn pawns_and_knights_cannot_make_rays() { + assert_eq!(piece!(White Pawn on F7).ray_to_square(Square::E8), None); + assert_eq!(piece!(White Knight on F6).ray_to_square(Square::E8), None); + } + mod pawn { use crate::test_position; use chessfriend_bitboard::{bitboard, BitBoard}; @@ -273,20 +355,25 @@ mod tests { } mod bishop { - use chessfriend_bitboard::bitboard; - use chessfriend_core::piece; + use super::*; sight_test!( c2_bishop, piece!(Black Bishop on C2), bitboard!(D1, B3, A4, B1, D3, E4, F5, G6, H7) ); + + #[test] + fn ray_to_square() { + let generated_ray = piece!(White Bishop on C5).ray_to_square(Square::E7); + let expected_ray = bitboard![D6, E7]; + assert_eq!(generated_ray, Some(expected_ray)); + } } mod rook { + use super::*; use crate::test_position; - use chessfriend_bitboard::bitboard; - use chessfriend_core::piece; sight_test!( g3_rook, @@ -304,6 +391,25 @@ mod tests { piece!(White Rook on E4), bitboard!(A4, B4, C4, D4, F4, G4, H4, E2, E3, E5, E6, E7) ); + + #[test] + fn ray_to_square() { + let generated_ray = piece!(White Rook on C2).ray_to_square(Square::C6); + let expected_ray = bitboard![C3, C4, C5, C6]; + assert_eq!(generated_ray, Some(expected_ray)); + + let generated_ray = piece!(White Rook on D2).ray_to_square(Square::H2); + let expected_ray = bitboard![E2, F2, G2, H2]; + assert_eq!(generated_ray, Some(expected_ray)); + + let generated_ray = piece!(White Rook on G6).ray_to_square(Square::B6); + let expected_ray = bitboard![B6, C6, D6, E6, F6]; + assert_eq!(generated_ray, Some(expected_ray)); + + let generated_ray = piece!(White Rook on A6).ray_to_square(Square::A3); + let expected_ray = bitboard![A5, A4, A3]; + assert_eq!(generated_ray, Some(expected_ray)); + } } mod king {