[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] 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) library::library().ray(sq, dir)
} }
@ -107,6 +107,24 @@ impl BitBoard {
pub fn occupied_squares_trailing(&self) -> impl Iterator<Item = Square> { pub fn occupied_squares_trailing(&self) -> impl Iterator<Item = Square> {
LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) }) 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 { impl Default for BitBoard {
@ -377,4 +395,14 @@ mod tests {
assert_eq!(BitBoard::from(Square::A1), BitBoard(0b1)); assert_eq!(BitBoard::from(Square::A1), BitBoard(0b1));
assert_eq!(BitBoard::from(Square::H8), BitBoard(1 << 63)); 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 ray
} }
pub(super) fn ray(&self, sq: Square, dir: Direction) -> BitBoard { pub(super) fn ray(&self, sq: Square, dir: Direction) -> &BitBoard {
self.rays[sq as usize][dir as usize] &self.rays[sq as usize][dir as usize]
} }
pub(super) fn pawn_pushes(&self, sq: Square, color: Color) -> BitBoard { 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; fn king_sight(&self, pieces: &PieceBitBoards) -> BitBoard;
} }
pub(crate) trait SliderRayToSquareExt {
fn ray_to_square(&self, square: Square) -> Option<BitBoard>;
}
impl SightExt for PlacedPiece { impl SightExt for PlacedPiece {
fn sight(&self, pieces: &PieceBitBoards, en_passant_square: Option<Square>) -> BitBoard { fn sight(&self, pieces: &PieceBitBoards, en_passant_square: Option<Square>) -> BitBoard {
match self.shape() { 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)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use chessfriend_bitboard::bitboard;
use chessfriend_core::{piece, Square};
macro_rules! sight_test { macro_rules! sight_test {
($test_name:ident, $position:expr, $piece:expr, $bitboard:expr) => { ($test_name:ident, $position:expr, $piece:expr, $bitboard:expr) => {
#[test] #[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 { mod pawn {
use crate::test_position; use crate::test_position;
use chessfriend_bitboard::{bitboard, BitBoard}; use chessfriend_bitboard::{bitboard, BitBoard};
@ -273,20 +355,25 @@ mod tests {
} }
mod bishop { mod bishop {
use chessfriend_bitboard::bitboard; use super::*;
use chessfriend_core::piece;
sight_test!( sight_test!(
c2_bishop, c2_bishop,
piece!(Black Bishop on C2), piece!(Black Bishop on C2),
bitboard!(D1, B3, A4, B1, D3, E4, F5, G6, H7) 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 { mod rook {
use super::*;
use crate::test_position; use crate::test_position;
use chessfriend_bitboard::bitboard;
use chessfriend_core::piece;
sight_test!( sight_test!(
g3_rook, g3_rook,
@ -304,6 +391,25 @@ mod tests {
piece!(White Rook on E4), piece!(White Rook on E4),
bitboard!(A4, B4, C4, D4, F4, G4, H4, E2, E3, E5, E6, E7) 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 { mod king {