From 3ecc263701a48eaf1e67db1f49d28159f9e9f23d Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Mon, 15 Jan 2024 16:03:06 -0800 Subject: [PATCH] [board] Implement piece sight algorithms Add a new Sight trait, implemented by PlacedPiece. The implementation of this trait produces a BitBoard representing the squares visible to the placed piece. --- board/src/lib.rs | 1 + board/src/piece.rs | 10 +++ board/src/position/position.rs | 18 ++++ board/src/sight.rs | 155 +++++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 board/src/sight.rs diff --git a/board/src/lib.rs b/board/src/lib.rs index 354c4e1..03b5c96 100644 --- a/board/src/lib.rs +++ b/board/src/lib.rs @@ -7,6 +7,7 @@ mod moves; pub mod piece; #[macro_use] pub mod position; +mod sight; mod square; pub use moves::Move; diff --git a/board/src/piece.rs b/board/src/piece.rs index 7a20d19..e8a8cbb 100644 --- a/board/src/piece.rs +++ b/board/src/piece.rs @@ -238,6 +238,16 @@ impl PlacedPiece { pub fn square(&self) -> Square { self.square } + + #[inline] + pub fn color(&self) -> Color { + self.piece.color + } + + #[inline] + pub fn shape(&self) -> Shape { + self.piece.shape + } } #[cfg(test)] diff --git a/board/src/position/position.rs b/board/src/position/position.rs index 3a98c1e..cf91f0e 100644 --- a/board/src/position/position.rs +++ b/board/src/position/position.rs @@ -44,6 +44,8 @@ pub struct Position { /// Bitboards representing positions of particular piece types per color. pieces_per_type: [[BitBoard; 6]; 2], + + en_passant_square: Option, } impl Position { @@ -70,6 +72,7 @@ impl Position { BitBoard::empty(), ], ], + en_passant_square: None, } } @@ -101,6 +104,7 @@ impl Position { black_pieces.iter().fold(BitBoard::empty(), |a, b| a | *b), ], pieces_per_type: [white_pieces, black_pieces], + en_passant_square: None, } } @@ -155,6 +159,16 @@ impl Position { self.pieces_per_color[Color::White as usize] | self.pieces_per_color[Color::Black as usize] } + #[inline] + pub(crate) fn friendly_pieces(&self) -> BitBoard { + self.bitboard_for_color(self.color_to_move) + } + + #[inline] + pub(crate) fn opposing_pieces(&self) -> BitBoard { + self.bitboard_for_color(self.color_to_move.other()) + } + /// Return a BitBoard representing the set of squares containing a piece. /// This set is the inverse of `occupied_squares`. #[inline] @@ -195,6 +209,10 @@ impl Position { pub fn pieces(&self, color: Color) -> Pieces { Pieces::new(&self, color) } + + pub fn en_passant_square(&self) -> Option { + self.en_passant_square + } } impl Default for Position { diff --git a/board/src/sight.rs b/board/src/sight.rs new file mode 100644 index 0000000..33e716e --- /dev/null +++ b/board/src/sight.rs @@ -0,0 +1,155 @@ +// Eryn Wells + +use crate::{ + piece::{Color, PlacedPiece, Shape}, + square::Direction, + BitBoard, Position, +}; + +pub(crate) trait Sight { + fn sight_in_position(&self, position: &Position) -> BitBoard; +} + +impl Sight for PlacedPiece { + fn sight_in_position(&self, position: &Position) -> BitBoard { + match self.shape() { + Shape::Pawn => match self.color() { + Color::White => self.white_pawn_sight_in_position(position), + Color::Black => self.black_pawn_sight_in_position(position), + }, + Shape::Knight => self.knight_sight_in_position(position), + Shape::Bishop => self.bishop_sight_in_position(position), + Shape::Rook => self.rook_sight_in_position(position), + Shape::Queen => self.queen_sight_in_position(position), + Shape::King => self.king_sight_in_position(position), + } + } +} + +impl PlacedPiece { + fn white_pawn_sight_in_position(&self, position: &Position) -> BitBoard { + let pawn: BitBoard = self.square().into(); + let pawn = pawn.shift_north_west_one() | pawn.shift_north_east_one(); + + let mut possible_squares = position.empty_squares() | position.opposing_pieces(); + if let Some(en_passant) = position.en_passant_square() { + possible_squares |= en_passant.into(); + } + + pawn & possible_squares + } + + fn black_pawn_sight_in_position(&self, position: &Position) -> BitBoard { + let pawn: BitBoard = self.square().into(); + let pawn = pawn.shift_south_west_one() | pawn.shift_south_east_one(); + + let mut possible_squares = position.empty_squares() | position.opposing_pieces(); + if let Some(en_passant) = position.en_passant_square() { + possible_squares |= en_passant.into(); + } + + pawn & possible_squares + } + + fn knight_sight_in_position(&self, position: &Position) -> BitBoard { + BitBoard::knight_moves(self.square()) & !position.friendly_pieces() + } + + fn bishop_sight_in_position(&self, position: &Position) -> BitBoard { + let square = self.square(); + + let mut sight = BitBoard::empty(); + + let blockers = position.occupied_squares(); + + 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; + sight |= attack_ray; + } else { + sight |= ray; + } + }; + } + + update_moves_with_ray!(NorthEast, occupied_squares_trailing); + update_moves_with_ray!(NorthWest, occupied_squares_trailing); + update_moves_with_ray!(SouthEast, occupied_squares); + update_moves_with_ray!(SouthWest, occupied_squares); + + sight + } + + fn rook_sight_in_position(&self, position: &Position) -> BitBoard { + let square = self.square(); + + let mut sight = BitBoard::empty(); + + let blockers = position.occupied_squares(); + + 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; + sight |= attack_ray; + } else { + sight |= ray; + } + }; + } + + update_moves_with_ray!(North, occupied_squares_trailing); + update_moves_with_ray!(East, occupied_squares_trailing); + update_moves_with_ray!(South, occupied_squares); + update_moves_with_ray!(West, occupied_squares); + + sight + } + + fn queen_sight_in_position(&self, position: &Position) -> BitBoard { + let square = self.square(); + + let mut sight = BitBoard::empty(); + + let blockers = position.occupied_squares(); + + 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; + sight |= attack_ray; + } else { + sight |= 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); + + sight + } + + fn king_sight_in_position(&self, position: &Position) -> BitBoard { + BitBoard::king_moves(self.square()) & !position.friendly_pieces() + } +}