[position] Move make_move to its own module: position::make_move
Rework sight.rs and add a new module, movement.rs, to calculate piece sight and movement. I discovered during this process that "sight" and "movement" are different because pawns (in particular) can move in ways that don't follow their sight lines. The routines in the movement module account for this, but also pass through to the sight routines for other pieces.
This commit is contained in:
parent
669a7c00ec
commit
a78526befa
6 changed files with 459 additions and 283 deletions
|
@ -3,6 +3,7 @@
|
|||
mod check;
|
||||
mod display;
|
||||
mod move_generator;
|
||||
mod movement;
|
||||
mod position;
|
||||
mod sight;
|
||||
|
||||
|
|
121
position/src/movement.rs
Normal file
121
position/src/movement.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
//! Defines routines for computing the movement of a piece. Movement is the set
|
||||
//! of squares a piece can move to. For all pieces except pawns, the Movement
|
||||
//! set is equal to the Sight set.
|
||||
|
||||
use crate::sight::Sight;
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_board::Board;
|
||||
use chessfriend_core::{Color, Piece, Rank, Shape, Square};
|
||||
|
||||
pub trait Movement {
|
||||
fn movement(&self, square: Square, board: &Board) -> BitBoard;
|
||||
}
|
||||
|
||||
impl Movement for Piece {
|
||||
fn movement(&self, square: Square, board: &Board) -> BitBoard {
|
||||
match self.shape {
|
||||
Shape::Pawn => {
|
||||
// Pawns can only move to squares they can see to capture.
|
||||
let opposing_occupancy = board.opposing_occupancy(self.color);
|
||||
let sight = self.sight(square, board) & opposing_occupancy;
|
||||
let pushes = pawn_pushes(square.into(), self.color, board.occupancy());
|
||||
sight | pushes
|
||||
}
|
||||
_ => self.sight(square, board),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pawn_pushes(pawn: BitBoard, color: Color, occupancy: BitBoard) -> BitBoard {
|
||||
let vacancy = !occupancy;
|
||||
|
||||
match color {
|
||||
Color::White => {
|
||||
let second_rank = BitBoard::rank(&Rank::TWO.into());
|
||||
|
||||
let mut pushes = pawn.shift_north_one() & vacancy;
|
||||
if !(pawn & second_rank).is_empty() {
|
||||
// Double push
|
||||
pushes = pushes | (pushes.shift_north_one() & vacancy);
|
||||
}
|
||||
|
||||
pushes
|
||||
}
|
||||
Color::Black => {
|
||||
let seventh_rank = BitBoard::rank(&Rank::SEVEN.into());
|
||||
|
||||
let mut pushes = pawn.shift_south_one() & vacancy;
|
||||
if !(pawn & seventh_rank).is_empty() {
|
||||
// Double push
|
||||
pushes = pushes | (pushes.shift_south_one() & vacancy);
|
||||
}
|
||||
|
||||
pushes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::pawn_pushes;
|
||||
use chessfriend_bitboard::{bitboard, BitBoard};
|
||||
use chessfriend_core::{Color, Square};
|
||||
|
||||
#[test]
|
||||
fn white_pushes_empty_board() {
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::E4.into(), Color::White, BitBoard::empty()),
|
||||
bitboard![E5]
|
||||
);
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::E2.into(), Color::White, BitBoard::empty()),
|
||||
bitboard![E3 E4]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn black_pawn_empty_board() {
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::A4.into(), Color::Black, BitBoard::empty()),
|
||||
bitboard![A3]
|
||||
);
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::B7.into(), Color::Black, BitBoard::empty()),
|
||||
bitboard![B6 B5]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn white_pushes_blocker() {
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::C5.into(), Color::White, bitboard![C6]),
|
||||
BitBoard::empty()
|
||||
);
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::D2.into(), Color::White, bitboard![D4]),
|
||||
bitboard![D3]
|
||||
);
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::D2.into(), Color::White, bitboard![D3]),
|
||||
BitBoard::empty()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn black_pushes_blocker() {
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::C5.into(), Color::Black, bitboard![C4]),
|
||||
BitBoard::empty()
|
||||
);
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::D7.into(), Color::Black, bitboard![D5]),
|
||||
bitboard![D6]
|
||||
);
|
||||
assert_eq!(
|
||||
pawn_pushes(Square::D7.into(), Color::Black, bitboard![D6]),
|
||||
BitBoard::empty()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
mod builders;
|
||||
mod make_move;
|
||||
mod position;
|
||||
|
||||
pub use {
|
||||
builders::{MakeMoveError, MoveBuilder},
|
||||
make_move::{MakeMoveError, ValidateMove},
|
||||
position::Position,
|
||||
};
|
||||
|
|
182
position/src/position/make_move.rs
Normal file
182
position/src/position/make_move.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::{movement::Movement, Position};
|
||||
use chessfriend_board::{PlacePieceError, PlacePieceStrategy};
|
||||
use chessfriend_core::{Color, Piece, Square};
|
||||
use chessfriend_moves::Move;
|
||||
use thiserror::Error;
|
||||
|
||||
type MakeMoveResult = Result<(), MakeMoveError>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub enum ValidateMove {
|
||||
#[default]
|
||||
No,
|
||||
Yes,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Eq, PartialEq)]
|
||||
pub enum MakeMoveError {
|
||||
#[error("no piece on {0}")]
|
||||
NoPiece(Square),
|
||||
|
||||
#[error("{piece} on {square} is not of active color")]
|
||||
NonActiveColor { piece: Piece, square: Square },
|
||||
|
||||
#[error("cannot capture piece on {0}")]
|
||||
InvalidCapture(Square),
|
||||
|
||||
#[error("no capture square")]
|
||||
NoCaptureSquare,
|
||||
|
||||
#[error("{piece} on {origin} cannot move to {target}")]
|
||||
NoMove {
|
||||
piece: Piece,
|
||||
origin: Square,
|
||||
target: Square,
|
||||
},
|
||||
|
||||
#[error("{0}")]
|
||||
PlacePieceError(#[from] PlacePieceError),
|
||||
}
|
||||
|
||||
pub enum UnmakeMoveError {}
|
||||
|
||||
impl Position {
|
||||
/// Make a move in the position.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// If `validate` is [`ValidateMove::Yes`], perform validation of move correctness prior to
|
||||
/// applying the move. See [`Position::validate_move`].
|
||||
pub fn make_move(&mut self, ply: Move, validate: ValidateMove) -> MakeMoveResult {
|
||||
self.validate_move(ply, validate)?;
|
||||
|
||||
if ply.is_quiet() {
|
||||
return self.make_quiet_move(ply.origin_square(), ply.target_square());
|
||||
}
|
||||
|
||||
if ply.is_capture() {
|
||||
return self.make_capture_move(ply);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unmake_move(&mut self, ply: &Move) -> Result<(), UnmakeMoveError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
fn make_quiet_move(&mut self, origin: Square, target: Square) -> MakeMoveResult {
|
||||
let piece = self
|
||||
.board
|
||||
.remove_piece(origin)
|
||||
.ok_or(MakeMoveError::NoPiece(origin))?;
|
||||
|
||||
self.place_active_piece(piece, target)?;
|
||||
|
||||
self.advance_clocks(HalfMoveClock::Advance);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_capture_move(&mut self, ply: Move) -> MakeMoveResult {
|
||||
let origin_square = ply.origin_square();
|
||||
let piece = self.get_piece_for_move(origin_square)?;
|
||||
|
||||
let capture_square = ply.capture_square().ok_or(MakeMoveError::NoCaptureSquare)?;
|
||||
let captured_piece = self.get_piece_for_move(capture_square)?;
|
||||
|
||||
// Register the capture
|
||||
self.captures[piece.color as usize].push(captured_piece);
|
||||
|
||||
self.remove_piece(origin_square).unwrap();
|
||||
let target_square = ply.target_square();
|
||||
self.place_piece(piece, target_square, PlacePieceStrategy::Replace)?;
|
||||
|
||||
self.advance_clocks(HalfMoveClock::Reset);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
fn get_piece_for_move(&mut self, square: Square) -> Result<Piece, MakeMoveError> {
|
||||
self.get_piece(square).ok_or(MakeMoveError::NoPiece(square))
|
||||
}
|
||||
|
||||
fn place_active_piece(&mut self, piece: Piece, square: Square) -> MakeMoveResult {
|
||||
self.board
|
||||
.place_piece(piece, square, PlacePieceStrategy::PreserveExisting)
|
||||
.map_err(MakeMoveError::PlacePieceError)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
enum HalfMoveClock {
|
||||
Reset,
|
||||
#[default]
|
||||
Advance,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
fn advance_clocks(&mut self, half_move_clock: HalfMoveClock) {
|
||||
match half_move_clock {
|
||||
HalfMoveClock::Reset => self.board.half_move_clock = 0,
|
||||
HalfMoveClock::Advance => self.board.half_move_clock += 1,
|
||||
}
|
||||
|
||||
self.board.active_color = self.board.active_color.next();
|
||||
|
||||
if self.board.active_color == Color::White {
|
||||
self.board.full_move_number += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
fn validate_move(&self, ply: Move, validate: ValidateMove) -> Result<(), MakeMoveError> {
|
||||
if validate == ValidateMove::No {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let origin_square = ply.origin_square();
|
||||
let active_piece = self
|
||||
.board
|
||||
.get_piece(origin_square)
|
||||
.ok_or(MakeMoveError::NoPiece(origin_square))?;
|
||||
|
||||
if active_piece.color != self.board.active_color {
|
||||
return Err(MakeMoveError::NonActiveColor {
|
||||
piece: active_piece,
|
||||
square: origin_square,
|
||||
});
|
||||
}
|
||||
|
||||
let target_square = ply.target_square();
|
||||
|
||||
let movement = active_piece.movement(origin_square, &self.board);
|
||||
if !movement.contains(target_square) {
|
||||
return Err(MakeMoveError::NoMove {
|
||||
piece: active_piece,
|
||||
origin: origin_square,
|
||||
target: target_square,
|
||||
});
|
||||
}
|
||||
|
||||
if ply.is_capture() {
|
||||
let target = ply.target_square();
|
||||
if let Some(captured_piece) = self.board.get_piece(target) {
|
||||
if captured_piece.color == active_piece.color {
|
||||
return Err(MakeMoveError::InvalidCapture(target));
|
||||
}
|
||||
} else {
|
||||
return Err(MakeMoveError::NoPiece(target));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -189,16 +189,6 @@ impl Position {
|
|||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn make_move(&mut self, ply: &Move) -> Result<(), MakeMoveError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unmake_move(&mut self, ply: &Move) -> Result<(), UnmakeMoveError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
|
|
@ -3,10 +3,58 @@
|
|||
//! Defines routines for computing sight of a piece. Sight is the set of squares
|
||||
//! that a piece can see. In other words, it's the set of squares attacked or
|
||||
//! controled by a piece.
|
||||
//!
|
||||
//! These functions use some common terms to describe arguments.
|
||||
//!
|
||||
//! occupancy
|
||||
//! : The set of occupied squares on the board.
|
||||
//!
|
||||
//! vacancy
|
||||
//! : The set of empty squares on the board, `!occpuancy`.
|
||||
//!
|
||||
//! blockers
|
||||
//! : The set of squares occupied by friendly pieces that block moves to that
|
||||
//! square and beyond.
|
||||
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_board::Board;
|
||||
use chessfriend_core::{Color, Direction, PlacedPiece, Shape, Square};
|
||||
use chessfriend_core::{Color, Direction, Piece, Shape, Square};
|
||||
|
||||
pub trait Sight {
|
||||
fn sight(&self, square: Square, board: &Board) -> BitBoard;
|
||||
}
|
||||
|
||||
impl Sight for Piece {
|
||||
fn sight(&self, square: Square, board: &Board) -> BitBoard {
|
||||
let occupancy = board.occupancy();
|
||||
let info = SightInfo {
|
||||
square,
|
||||
occupancy,
|
||||
friendly_occupancy: board.friendly_occupancy(self.color),
|
||||
};
|
||||
|
||||
match self.shape {
|
||||
Shape::Pawn => {
|
||||
let en_passant_square: BitBoard = board.en_passant_target.into();
|
||||
match self.color {
|
||||
Color::White => white_pawn_sight(&info, en_passant_square),
|
||||
Color::Black => black_pawn_sight(&info, en_passant_square),
|
||||
}
|
||||
}
|
||||
Shape::Knight => knight_sight(&info),
|
||||
Shape::Bishop => bishop_sight(&info),
|
||||
Shape::Rook => rook_sight(&info),
|
||||
Shape::Queen => queen_sight(&info),
|
||||
Shape::King => king_sight(&info),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SightInfo {
|
||||
square: Square,
|
||||
occupancy: BitBoard,
|
||||
friendly_occupancy: BitBoard,
|
||||
}
|
||||
|
||||
macro_rules! ray_in_direction {
|
||||
($square:expr, $blockers:expr, $direction:ident, $first_occupied_square:tt) => {{
|
||||
|
@ -23,287 +71,111 @@ macro_rules! ray_in_direction {
|
|||
}
|
||||
|
||||
/// Compute sight of a white pawn.
|
||||
fn _white_pawn_sight(
|
||||
pawn: BitBoard,
|
||||
occupancy: BitBoard,
|
||||
blockers: BitBoard,
|
||||
en_passant_square: BitBoard,
|
||||
) -> BitBoard {
|
||||
let possible_squares = !occupancy | blockers | en_passant_square;
|
||||
fn white_pawn_sight(info: &SightInfo, en_passant_square: BitBoard) -> BitBoard {
|
||||
let possible_squares = !info.friendly_occupancy | en_passant_square;
|
||||
|
||||
let pawn: BitBoard = info.square.into();
|
||||
let pawn = pawn.shift_north_west_one() | pawn.shift_north_east_one();
|
||||
|
||||
pawn & possible_squares
|
||||
}
|
||||
|
||||
fn _black_pawn_sight(
|
||||
pawn: BitBoard,
|
||||
occupancy: BitBoard,
|
||||
blockers: BitBoard,
|
||||
en_passant_square: BitBoard,
|
||||
) -> BitBoard {
|
||||
let possible_squares = !occupancy | blockers | en_passant_square;
|
||||
fn black_pawn_sight(info: &SightInfo, en_passant_square: BitBoard) -> BitBoard {
|
||||
let possible_squares = !info.friendly_occupancy | en_passant_square;
|
||||
|
||||
let pawn: BitBoard = info.square.into();
|
||||
let pawn = pawn.shift_south_west_one() | pawn.shift_south_east_one();
|
||||
|
||||
pawn & possible_squares
|
||||
}
|
||||
|
||||
fn _knight_sight(knight_square: Square, blockers: BitBoard) -> BitBoard {
|
||||
BitBoard::knight_moves(knight_square) & !blockers
|
||||
fn knight_sight(info: &SightInfo) -> BitBoard {
|
||||
BitBoard::knight_moves(info.square)
|
||||
}
|
||||
|
||||
fn _bishop_sight(bishop_square: Square, occupancy: BitBoard) -> BitBoard {
|
||||
fn bishop_sight(info: &SightInfo) -> BitBoard {
|
||||
let bishop = info.square;
|
||||
let occupancy = info.occupancy;
|
||||
|
||||
#[rustfmt::skip]
|
||||
let sight = ray_in_direction!(bishop_square, occupancy, NorthEast, first_occupied_square_trailing)
|
||||
| ray_in_direction!(bishop_square, occupancy, NorthWest, first_occupied_square_trailing)
|
||||
| ray_in_direction!(bishop_square, occupancy, SouthEast, first_occupied_square_leading)
|
||||
| ray_in_direction!(bishop_square, occupancy, SouthWest, first_occupied_square_leading);
|
||||
let sight = ray_in_direction!(bishop, occupancy, NorthEast, first_occupied_square_trailing)
|
||||
| ray_in_direction!(bishop, occupancy, SouthEast, first_occupied_square_leading)
|
||||
| ray_in_direction!(bishop, occupancy, SouthWest, first_occupied_square_leading)
|
||||
| ray_in_direction!(bishop, occupancy, NorthWest, first_occupied_square_trailing);
|
||||
|
||||
sight
|
||||
}
|
||||
|
||||
fn _rook_sight(rook_square: Square, occupancy: BitBoard) -> BitBoard {
|
||||
fn rook_sight(info: &SightInfo) -> BitBoard {
|
||||
let rook = info.square;
|
||||
let occupancy = info.occupancy;
|
||||
|
||||
#[rustfmt::skip]
|
||||
let sight = ray_in_direction!(rook_square, occupancy, North, first_occupied_square_trailing)
|
||||
| ray_in_direction!(rook_square, occupancy, East, first_occupied_square_trailing)
|
||||
| ray_in_direction!(rook_square, occupancy, South, first_occupied_square_leading)
|
||||
| ray_in_direction!(rook_square, occupancy, West, first_occupied_square_leading);
|
||||
let sight = ray_in_direction!(rook, occupancy, North, first_occupied_square_trailing)
|
||||
| ray_in_direction!(rook, occupancy, East, first_occupied_square_trailing)
|
||||
| ray_in_direction!(rook, occupancy, South, first_occupied_square_leading)
|
||||
| ray_in_direction!(rook, occupancy, West, first_occupied_square_leading);
|
||||
|
||||
sight
|
||||
}
|
||||
|
||||
fn _queen_sight(queen_square: Square, occupancy: BitBoard) -> BitBoard {
|
||||
fn queen_sight(info: &SightInfo) -> BitBoard {
|
||||
let queen = info.square;
|
||||
let occupancy = info.occupancy;
|
||||
|
||||
#[rustfmt::skip]
|
||||
let sight = ray_in_direction!(queen_square, occupancy, NorthWest, first_occupied_square_trailing)
|
||||
| ray_in_direction!(queen_square, occupancy, North, first_occupied_square_trailing)
|
||||
| ray_in_direction!(queen_square, occupancy, NorthEast, first_occupied_square_trailing)
|
||||
| ray_in_direction!(queen_square, occupancy, East, first_occupied_square_trailing)
|
||||
| ray_in_direction!(queen_square, occupancy, SouthEast, first_occupied_square_leading)
|
||||
| ray_in_direction!(queen_square, occupancy, South, first_occupied_square_leading)
|
||||
| ray_in_direction!(queen_square, occupancy, SouthWest, first_occupied_square_leading)
|
||||
| ray_in_direction!(queen_square, occupancy, West, first_occupied_square_leading);
|
||||
let sight = ray_in_direction!(queen, occupancy, NorthWest, first_occupied_square_trailing)
|
||||
| ray_in_direction!(queen, occupancy, North, first_occupied_square_trailing)
|
||||
| ray_in_direction!(queen, occupancy, NorthEast, first_occupied_square_trailing)
|
||||
| ray_in_direction!(queen, occupancy, East, first_occupied_square_trailing)
|
||||
| ray_in_direction!(queen, occupancy, SouthEast, first_occupied_square_leading)
|
||||
| ray_in_direction!(queen, occupancy, South, first_occupied_square_leading)
|
||||
| ray_in_direction!(queen, occupancy, SouthWest, first_occupied_square_leading)
|
||||
| ray_in_direction!(queen, occupancy, West, first_occupied_square_leading);
|
||||
|
||||
sight
|
||||
}
|
||||
|
||||
fn _king_sight(king_square: Square, blockers: BitBoard) -> BitBoard {
|
||||
BitBoard::king_moves(king_square) & !blockers
|
||||
}
|
||||
pub(crate) trait BishopSightExt {
|
||||
fn bishop_sight(&self, occupancy: BitBoard) -> BitBoard;
|
||||
}
|
||||
|
||||
pub(crate) trait KingSightExt {
|
||||
fn king_sight(&self, board: &Board) -> BitBoard;
|
||||
}
|
||||
|
||||
pub(crate) trait KnightSightExt {
|
||||
fn knight_sight(&self, board: &Board) -> BitBoard;
|
||||
}
|
||||
|
||||
pub(crate) trait PawnSightExt {
|
||||
fn pawn_sight(&self, board: &Board, en_passant_square: Option<Square>) -> BitBoard;
|
||||
fn white_pawn_sight(&self, board: &Board, en_passant_square: Option<Square>) -> BitBoard;
|
||||
fn black_pawn_sight(&self, board: &Board, en_passant_square: Option<Square>) -> BitBoard;
|
||||
}
|
||||
pub(crate) trait QueenSightExt {
|
||||
fn queen_sight(&self, occupancy: BitBoard) -> BitBoard;
|
||||
}
|
||||
|
||||
pub(crate) trait RookSightExt {
|
||||
fn rook_sight(&self, occupancy: BitBoard) -> BitBoard;
|
||||
}
|
||||
|
||||
pub(crate) trait SliderSightExt: BishopSightExt + QueenSightExt + RookSightExt {}
|
||||
|
||||
pub(crate) trait SightExt {
|
||||
fn sight(&self, board: &Board, en_passant_square: Option<Square>) -> BitBoard;
|
||||
}
|
||||
|
||||
pub(crate) trait SliderRayToSquareExt {
|
||||
fn ray_to_square(&self, origin: Square, target: Square) -> Option<BitBoard>;
|
||||
}
|
||||
|
||||
impl SightExt for PlacedPiece {
|
||||
fn sight(&self, board: &Board, en_passant_square: Option<Square>) -> BitBoard {
|
||||
match self.shape() {
|
||||
Shape::Pawn => match self.color() {
|
||||
Color::White => self.white_pawn_sight(board, en_passant_square),
|
||||
Color::Black => self.black_pawn_sight(board, en_passant_square),
|
||||
},
|
||||
Shape::Knight => self.knight_sight(board),
|
||||
Shape::Bishop => self.bishop_sight(board.pieces.all_pieces()),
|
||||
Shape::Rook => self.rook_sight(board.pieces.all_pieces()),
|
||||
Shape::Queen => self.queen_sight(board.pieces.all_pieces()),
|
||||
Shape::King => self.king_sight(board),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KingSightExt for PlacedPiece {
|
||||
fn king_sight(&self, board: &Board) -> BitBoard {
|
||||
_king_sight(self.square, board.pieces.all_pieces_of_color(self.color))
|
||||
}
|
||||
}
|
||||
|
||||
impl KnightSightExt for PlacedPiece {
|
||||
fn knight_sight(&self, board: &Board) -> BitBoard {
|
||||
_knight_sight(self.square, board.pieces.all_pieces_of_color(self.color))
|
||||
}
|
||||
}
|
||||
|
||||
impl PawnSightExt for PlacedPiece {
|
||||
fn pawn_sight(&self, board: &Board, en_passant_square: Option<Square>) -> BitBoard {
|
||||
match self.color {
|
||||
Color::White => self.white_pawn_sight(board, en_passant_square),
|
||||
Color::Black => self.black_pawn_sight(board, en_passant_square),
|
||||
}
|
||||
}
|
||||
|
||||
fn white_pawn_sight(&self, board: &Board, en_passant_square: Option<Square>) -> BitBoard {
|
||||
let opponent = self.color.other();
|
||||
_white_pawn_sight(
|
||||
self.square.into(),
|
||||
board.pieces.all_pieces(),
|
||||
board.pieces.all_pieces_of_color(opponent),
|
||||
en_passant_square.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn black_pawn_sight(&self, board: &Board, en_passant_square: Option<Square>) -> BitBoard {
|
||||
let opponent = self.piece.color.other();
|
||||
_black_pawn_sight(
|
||||
self.square.into(),
|
||||
board.pieces.all_pieces(),
|
||||
board.pieces.all_pieces_of_color(opponent),
|
||||
en_passant_square.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl BishopSightExt for PlacedPiece {
|
||||
fn bishop_sight(&self, occupancy: BitBoard) -> BitBoard {
|
||||
_bishop_sight(self.square, occupancy)
|
||||
}
|
||||
}
|
||||
|
||||
impl RookSightExt for PlacedPiece {
|
||||
fn rook_sight(&self, occupancy: BitBoard) -> BitBoard {
|
||||
_rook_sight(self.square, occupancy)
|
||||
}
|
||||
}
|
||||
|
||||
impl QueenSightExt for PlacedPiece {
|
||||
fn queen_sight(&self, occupancy: BitBoard) -> BitBoard {
|
||||
_queen_sight(self.square, occupancy)
|
||||
}
|
||||
}
|
||||
|
||||
impl SliderSightExt for PlacedPiece {}
|
||||
|
||||
impl SliderRayToSquareExt for Shape {
|
||||
fn ray_to_square(&self, origin: Square, target: Square) -> Option<BitBoard> {
|
||||
macro_rules! ray {
|
||||
($square:expr, $direction:ident) => {
|
||||
(
|
||||
BitBoard::ray($square, Direction::$direction),
|
||||
Direction::$direction,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
let target_bitboard: BitBoard = target.into();
|
||||
|
||||
let ray_and_direction = match self {
|
||||
Shape::Bishop => [
|
||||
ray!(origin, NorthEast),
|
||||
ray!(origin, SouthEast),
|
||||
ray!(origin, SouthWest),
|
||||
ray!(origin, NorthWest),
|
||||
]
|
||||
.into_iter()
|
||||
.find(|(ray, _)| !(target_bitboard & ray).is_empty()),
|
||||
Shape::Rook => [
|
||||
ray!(origin, North),
|
||||
ray!(origin, East),
|
||||
ray!(origin, South),
|
||||
ray!(origin, West),
|
||||
]
|
||||
.into_iter()
|
||||
.find(|(ray, _)| !(target_bitboard & ray).is_empty()),
|
||||
Shape::Queen => [
|
||||
ray!(origin, North),
|
||||
ray!(origin, NorthEast),
|
||||
ray!(origin, East),
|
||||
ray!(origin, SouthEast),
|
||||
ray!(origin, South),
|
||||
ray!(origin, SouthWest),
|
||||
ray!(origin, West),
|
||||
ray!(origin, NorthWest),
|
||||
]
|
||||
.into_iter()
|
||||
.find(|(ray, _)| !(target_bitboard & ray).is_empty()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some((ray, direction)) = ray_and_direction {
|
||||
let remainder = BitBoard::ray(target, direction);
|
||||
return Some(ray & !remainder);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl BishopSightExt for Square {
|
||||
fn bishop_sight(&self, occupancy: BitBoard) -> BitBoard {
|
||||
_bishop_sight(*self, occupancy)
|
||||
}
|
||||
}
|
||||
|
||||
impl QueenSightExt for Square {
|
||||
fn queen_sight(&self, occupancy: BitBoard) -> BitBoard {
|
||||
_queen_sight(*self, occupancy)
|
||||
}
|
||||
}
|
||||
|
||||
impl RookSightExt for Square {
|
||||
fn rook_sight(&self, occupancy: BitBoard) -> BitBoard {
|
||||
_rook_sight(*self, occupancy)
|
||||
}
|
||||
fn king_sight(info: &SightInfo) -> BitBoard {
|
||||
BitBoard::king_moves(info.square)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use chessfriend_bitboard::bitboard;
|
||||
use chessfriend_core::{piece, Square};
|
||||
use chessfriend_core::piece;
|
||||
|
||||
macro_rules! sight_test {
|
||||
($test_name:ident, $position:expr, $piece:expr, $bitboard:expr) => {
|
||||
($test_name:ident, $position:expr, $piece:expr, $square:expr, $bitboard:expr) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
use chessfriend_core::Square;
|
||||
use $crate::sight::Sight;
|
||||
|
||||
let pos = $position;
|
||||
let piece = $piece;
|
||||
let sight = pos.sight_of_piece(&piece);
|
||||
let sight = piece.sight($square, &pos.board);
|
||||
|
||||
assert_eq!(sight, $bitboard);
|
||||
}
|
||||
};
|
||||
($test_name:ident, $piece:expr, $bitboard:expr) => {
|
||||
sight_test! {$test_name, $crate::Position::empty(), $piece, $bitboard}
|
||||
($test_name:ident, $piece:expr, $square:expr, $bitboard:expr) => {
|
||||
sight_test! {$test_name, $crate::Position::empty(), $piece, $square, $bitboard}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pawns_and_knights_cannot_make_rays() {
|
||||
assert_eq!(Shape::Pawn.ray_to_square(Square::F7, Square::E8), None);
|
||||
assert_eq!(Shape::Knight.ray_to_square(Square::F6, Square::E8), None);
|
||||
}
|
||||
// #[test]
|
||||
// fn pawns_and_knights_cannot_make_rays() {
|
||||
// assert_eq!(Shape::Pawn.ray_to_square(Square::F7, Square::E8), None);
|
||||
// assert_eq!(Shape::Knight.ray_to_square(Square::F6, Square::E8), None);
|
||||
// }
|
||||
|
||||
mod pawn {
|
||||
use crate::test_position;
|
||||
use crate::{sight::Sight, test_position};
|
||||
use chessfriend_bitboard::{bitboard, BitBoard};
|
||||
use chessfriend_core::piece;
|
||||
use chessfriend_core::{piece, Square};
|
||||
|
||||
sight_test!(e4_pawn, piece!(White Pawn on E4), bitboard![D5 F5]);
|
||||
sight_test!(e4_pawn, piece!(White Pawn), Square::E4, bitboard![D5 F5]);
|
||||
|
||||
sight_test!(
|
||||
e4_pawn_one_blocker,
|
||||
|
@ -311,7 +183,8 @@ mod tests {
|
|||
White Bishop on D5,
|
||||
White Pawn on E4,
|
||||
],
|
||||
piece!(White Pawn on E4),
|
||||
piece!(White Pawn),
|
||||
Square::E4,
|
||||
bitboard!(F5)
|
||||
);
|
||||
|
||||
|
@ -323,8 +196,8 @@ mod tests {
|
|||
White Pawn on E4,
|
||||
);
|
||||
|
||||
let piece = piece!(White Pawn on E4);
|
||||
let sight = pos.sight_of_piece(&piece);
|
||||
let piece = piece!(White Pawn);
|
||||
let sight = piece.sight(Square::E4, &pos.board);
|
||||
|
||||
assert_eq!(sight, BitBoard::empty());
|
||||
}
|
||||
|
@ -337,10 +210,10 @@ mod tests {
|
|||
White Pawn on E4,
|
||||
);
|
||||
|
||||
let piece = piece!(White Pawn on E4);
|
||||
let sight = pos.sight_of_piece(&piece);
|
||||
let piece = piece!(White Pawn);
|
||||
let sight = piece.sight(Square::E4, &pos.board);
|
||||
|
||||
assert_eq!(sight, bitboard!(D5));
|
||||
assert_eq!(sight, bitboard![D5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -349,8 +222,8 @@ mod tests {
|
|||
White Pawn on E5,
|
||||
Black Pawn on D5,
|
||||
], D6);
|
||||
let piece = piece!(White Pawn on E5);
|
||||
let sight = pos.sight_of_piece(&piece);
|
||||
let piece = piece!(White Pawn);
|
||||
let sight = piece.sight(Square::E5, &pos.board);
|
||||
|
||||
assert_eq!(sight, bitboard!(D6 F6));
|
||||
}
|
||||
|
@ -363,7 +236,8 @@ mod tests {
|
|||
|
||||
sight_test!(
|
||||
f6_knight,
|
||||
piece!(Black Knight on F6),
|
||||
piece!(Black Knight),
|
||||
Square::F6,
|
||||
bitboard![H7 G8 E8 D7 D5 E4 G4 H5]
|
||||
);
|
||||
}
|
||||
|
@ -373,16 +247,17 @@ mod tests {
|
|||
|
||||
sight_test!(
|
||||
c2_bishop,
|
||||
piece!(Black Bishop on C2),
|
||||
piece!(Black Bishop),
|
||||
Square::C2,
|
||||
bitboard![D1 B3 A4 B1 D3 E4 F5 G6 H7]
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn ray_to_square() {
|
||||
let generated_ray = Shape::Bishop.ray_to_square(Square::C5, Square::E7);
|
||||
let expected_ray = bitboard![D6 E7];
|
||||
assert_eq!(generated_ray, Some(expected_ray));
|
||||
}
|
||||
// #[test]
|
||||
// fn ray_to_square() {
|
||||
// let generated_ray = Shape::Bishop.ray_to_square(Square::C5, Square::E7);
|
||||
// let expected_ray = bitboard![D6 E7];
|
||||
// assert_eq!(generated_ray, Some(expected_ray));
|
||||
// }
|
||||
}
|
||||
|
||||
mod rook {
|
||||
|
@ -391,7 +266,8 @@ mod tests {
|
|||
|
||||
sight_test!(
|
||||
g3_rook,
|
||||
piece!(White Rook on G3),
|
||||
piece!(White Rook),
|
||||
Square::G3,
|
||||
bitboard![G1 G2 G4 G5 G6 G7 G8 A3 B3 C3 D3 E3 F3 H3]
|
||||
);
|
||||
|
||||
|
@ -399,37 +275,43 @@ mod tests {
|
|||
e4_rook_with_e1_white_king_e7_black_king,
|
||||
test_position![
|
||||
White Rook on E4,
|
||||
White King on E1,
|
||||
White King on E2,
|
||||
Black King on E7,
|
||||
],
|
||||
piece!(White Rook on E4),
|
||||
bitboard![A4 B4 C4 D4 F4 G4 H4 E2 E3 E5 E6 E7 E1]
|
||||
piece!(White Rook),
|
||||
Square::E4,
|
||||
bitboard![A4 B4 C4 D4 F4 G4 H4 E2 E3 E5 E6 E7]
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn ray_to_square() {
|
||||
let generated_ray = Shape::Rook.ray_to_square(Square::C2, Square::C6);
|
||||
let expected_ray = bitboard![C3 C4 C5 C6];
|
||||
assert_eq!(generated_ray, Some(expected_ray));
|
||||
// #[test]
|
||||
// fn ray_to_square() {
|
||||
// let generated_ray = Shape::Rook.ray_to_square(Square::C2, Square::C6);
|
||||
// let expected_ray = bitboard![C3 C4 C5 C6];
|
||||
// assert_eq!(generated_ray, Some(expected_ray));
|
||||
|
||||
let generated_ray = Shape::Rook.ray_to_square(Square::D2, Square::H2);
|
||||
let expected_ray = bitboard![E2 F2 G2 H2];
|
||||
assert_eq!(generated_ray, Some(expected_ray));
|
||||
// let generated_ray = Shape::Rook.ray_to_square(Square::D2, Square::H2);
|
||||
// let expected_ray = bitboard![E2 F2 G2 H2];
|
||||
// assert_eq!(generated_ray, Some(expected_ray));
|
||||
|
||||
let generated_ray = Shape::Rook.ray_to_square(Square::G6, Square::B6);
|
||||
let expected_ray = bitboard![B6 C6 D6 E6 F6];
|
||||
assert_eq!(generated_ray, Some(expected_ray));
|
||||
// let generated_ray = Shape::Rook.ray_to_square(Square::G6, Square::B6);
|
||||
// let expected_ray = bitboard![B6 C6 D6 E6 F6];
|
||||
// assert_eq!(generated_ray, Some(expected_ray));
|
||||
|
||||
let generated_ray = Shape::Rook.ray_to_square(Square::A6, Square::A3);
|
||||
let expected_ray = bitboard![A5 A4 A3];
|
||||
assert_eq!(generated_ray, Some(expected_ray));
|
||||
}
|
||||
// let generated_ray = Shape::Rook.ray_to_square(Square::A6, Square::A3);
|
||||
// let expected_ray = bitboard![A5 A4 A3];
|
||||
// assert_eq!(generated_ray, Some(expected_ray));
|
||||
// }
|
||||
}
|
||||
|
||||
mod king {
|
||||
use chessfriend_bitboard::bitboard;
|
||||
use chessfriend_core::piece;
|
||||
|
||||
sight_test!(e1_king, piece!(White King on E1), bitboard![D1 D2 E2 F2 F1]);
|
||||
sight_test!(
|
||||
e1_king,
|
||||
piece!(White King),
|
||||
Square::E1,
|
||||
bitboard![D1 D2 E2 F2 F1]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue