[position] Implement a SliderRayToSquare trait

This trait declares ray_to_square() which should return a BitBoard representing
a ray from a Square to another Square. The ray should include the target Square.

PlacedPiece implements this trait.
This commit is contained in:
Eryn Wells 2024-02-01 08:42:19 -08:00
parent cac13b4bc7
commit 722a90b860
3 changed files with 141 additions and 7 deletions

View file

@ -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<Item = Square> {
LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) })
}
pub fn first_occupied_square(&self) -> Option<Square> {
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<Square> {
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));
}
}

View file

@ -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 {

View file

@ -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<BitBoard>;
}
impl SightExt for PlacedPiece {
fn sight(&self, pieces: &PieceBitBoards, en_passant_square: Option<Square>) -> BitBoard {
match self.shape() {
@ -183,8 +187,80 @@ impl SightExt for PlacedPiece {
}
}
impl SliderRayToSquareExt for PlacedPiece {
fn ray_to_square(&self, target: Square) -> Option<BitBoard> {
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 {