[position, board] Move castle, movement, and sight modules to the board crate
Nothing about this code depends on Position, so push it down to a lower layer.
This commit is contained in:
		
							parent
							
								
									9a4fa827f9
								
							
						
					
					
						commit
						dbca7b4f88
					
				
					 7 changed files with 334 additions and 80 deletions
				
			
		| 
						 | 
					@ -5,3 +5,232 @@ mod rights;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use parameters::Parameters;
 | 
					pub use parameters::Parameters;
 | 
				
			||||||
pub use rights::Rights;
 | 
					pub use rights::Rights;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::Board;
 | 
				
			||||||
 | 
					use chessfriend_core::{Color, Piece, Square, Wing};
 | 
				
			||||||
 | 
					use thiserror::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
 | 
				
			||||||
 | 
					pub enum CastleEvaluationError {
 | 
				
			||||||
 | 
					    #[error("{color} does not have the right to castle {wing}")]
 | 
				
			||||||
 | 
					    NoRights { color: Color, wing: Wing },
 | 
				
			||||||
 | 
					    #[error("no king")]
 | 
				
			||||||
 | 
					    NoKing,
 | 
				
			||||||
 | 
					    #[error("no rook")]
 | 
				
			||||||
 | 
					    NoRook,
 | 
				
			||||||
 | 
					    #[error("castling path is not clear")]
 | 
				
			||||||
 | 
					    ObstructingPieces,
 | 
				
			||||||
 | 
					    #[error("opposing pieces check castling path")]
 | 
				
			||||||
 | 
					    CheckingPieces,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Board {
 | 
				
			||||||
 | 
					    /// Evaluates whether the active color can castle toward the given wing of the board in the
 | 
				
			||||||
 | 
					    /// current position.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// ## Errors
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns an error indicating why the active color cannot castle.
 | 
				
			||||||
 | 
					    pub fn active_color_can_castle(&self, wing: Wing) -> Result<(), CastleEvaluationError> {
 | 
				
			||||||
 | 
					        // TODO: Cache this result. It's expensive!
 | 
				
			||||||
 | 
					        // TODO: Does this actually need to rely on internal state, i.e. active_color?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let active_color = self.active_color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if !self.castling_rights.color_has_right(active_color, wing) {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::NoRights {
 | 
				
			||||||
 | 
					                color: active_color,
 | 
				
			||||||
 | 
					                wing,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let parameters = self.castling_parameters(wing);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.castling_king(parameters.origin.king).is_none() {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::NoKing);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.castling_rook(parameters.origin.rook).is_none() {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::NoRook);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // All squares must be clear.
 | 
				
			||||||
 | 
					        let has_obstructing_pieces = (self.occupancy() & parameters.clear).is_populated();
 | 
				
			||||||
 | 
					        if has_obstructing_pieces {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::ObstructingPieces);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // King cannot pass through check.
 | 
				
			||||||
 | 
					        let opposing_sight = self.opposing_sight();
 | 
				
			||||||
 | 
					        let opposing_pieces_can_see_castling_path =
 | 
				
			||||||
 | 
					            (parameters.check & opposing_sight).is_populated();
 | 
				
			||||||
 | 
					        if opposing_pieces_can_see_castling_path {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::CheckingPieces);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(crate) fn castling_king(&self, square: Square) -> Option<Piece> {
 | 
				
			||||||
 | 
					        let active_color = self.active_color;
 | 
				
			||||||
 | 
					        self.get_piece(square)
 | 
				
			||||||
 | 
					            .filter(|piece| piece.color == active_color && piece.is_king())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(crate) fn castling_rook(&self, square: Square) -> Option<Piece> {
 | 
				
			||||||
 | 
					        let active_color = self.active_color;
 | 
				
			||||||
 | 
					        self.get_piece(square)
 | 
				
			||||||
 | 
					            .filter(|piece| piece.color == active_color && piece.is_rook())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    use crate::test_board;
 | 
				
			||||||
 | 
					    use chessfriend_core::{piece, Color, Wing};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn king_on_starting_square_can_castle() {
 | 
				
			||||||
 | 
					        let pos = test_board!(
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					            White Rook on H1
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let rights = pos.castling_rights;
 | 
				
			||||||
 | 
					        assert!(rights.color_has_right(Color::White, Wing::KingSide));
 | 
				
			||||||
 | 
					        assert!(rights.color_has_right(Color::White, Wing::QueenSide));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn king_for_castle() {
 | 
				
			||||||
 | 
					        let pos = test_board![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let kingside_parameters = pos.castling_parameters(Wing::KingSide);
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.castling_king(kingside_parameters.origin.king),
 | 
				
			||||||
 | 
					            Some(piece!(White King))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let queenside_parameters = pos.castling_parameters(Wing::QueenSide);
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.castling_king(queenside_parameters.origin.king),
 | 
				
			||||||
 | 
					            Some(piece!(White King))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn rook_for_castle() {
 | 
				
			||||||
 | 
					        let pos = test_board![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let kingside_parameters = pos.castling_parameters(Wing::KingSide);
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.castling_rook(kingside_parameters.origin.rook),
 | 
				
			||||||
 | 
					            Some(piece!(White Rook))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pos = test_board![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let queenside_parameters = pos.castling_parameters(Wing::QueenSide);
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.castling_rook(queenside_parameters.origin.rook),
 | 
				
			||||||
 | 
					            Some(piece!(White Rook))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_can_castle() {
 | 
				
			||||||
 | 
					        let pos = test_board![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(pos.active_color_can_castle(Wing::KingSide), Ok(()));
 | 
				
			||||||
 | 
					        assert_eq!(pos.active_color_can_castle(Wing::QueenSide), Ok(()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_cannot_castle_missing_king() {
 | 
				
			||||||
 | 
					        let pos = test_board![
 | 
				
			||||||
 | 
					            White King on E2,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::KingSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::NoKing)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::QueenSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::NoKing)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_cannot_castle_missing_rook() {
 | 
				
			||||||
 | 
					        let pos = test_board![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::KingSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::NoRook)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pos = test_board![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::QueenSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::NoRook)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_cannot_castle_obstructing_piece() {
 | 
				
			||||||
 | 
					        let pos = test_board![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Bishop on F1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::KingSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::ObstructingPieces)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(pos.active_color_can_castle(Wing::QueenSide), Ok(()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_cannot_castle_checking_pieces() {
 | 
				
			||||||
 | 
					        let pos = test_board![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					            Black Queen on C6,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(pos.active_color_can_castle(Wing::KingSide), Ok(()));
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::QueenSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::CheckingPieces)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,8 @@ pub mod display;
 | 
				
			||||||
pub mod en_passant;
 | 
					pub mod en_passant;
 | 
				
			||||||
pub mod fen;
 | 
					pub mod fen;
 | 
				
			||||||
pub mod macros;
 | 
					pub mod macros;
 | 
				
			||||||
 | 
					pub mod movement;
 | 
				
			||||||
 | 
					pub mod sight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod board;
 | 
					mod board;
 | 
				
			||||||
mod piece_sets;
 | 
					mod piece_sets;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,17 +4,26 @@
 | 
				
			||||||
//! of squares a piece can move to. For all pieces except pawns, the Movement
 | 
					//! of squares a piece can move to. For all pieces except pawns, the Movement
 | 
				
			||||||
//! set is equal to the Sight set.
 | 
					//! set is equal to the Sight set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{sight::Sight, Position};
 | 
					use crate::{sight::Sight, Board};
 | 
				
			||||||
use chessfriend_bitboard::BitBoard;
 | 
					use chessfriend_bitboard::BitBoard;
 | 
				
			||||||
use chessfriend_core::{Color, Piece, Rank, Shape, Square, Wing};
 | 
					use chessfriend_core::{Color, Piece, Rank, Shape, Square, Wing};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Board {
 | 
				
			||||||
 | 
					    pub fn movement(&self, square: Square) -> BitBoard {
 | 
				
			||||||
 | 
					        if let Some(piece) = self.get_piece(square) {
 | 
				
			||||||
 | 
					            piece.movement(square, self)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            BitBoard::empty()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait Movement {
 | 
					pub trait Movement {
 | 
				
			||||||
    fn movement(&self, square: Square, position: &Position) -> BitBoard;
 | 
					    fn movement(&self, square: Square, board: &Board) -> BitBoard;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Movement for Piece {
 | 
					impl Movement for Piece {
 | 
				
			||||||
    fn movement(&self, square: Square, position: &Position) -> BitBoard {
 | 
					    fn movement(&self, square: Square, board: &Board) -> BitBoard {
 | 
				
			||||||
        let board = &position.board;
 | 
					 | 
				
			||||||
        let opposing_occupancy = board.opposing_occupancy(self.color);
 | 
					        let opposing_occupancy = board.opposing_occupancy(self.color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match self.shape {
 | 
					        match self.shape {
 | 
				
			||||||
| 
						 | 
					@ -27,7 +36,7 @@ impl Movement for Piece {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Shape::King => {
 | 
					            Shape::King => {
 | 
				
			||||||
                let kingside_target_square =
 | 
					                let kingside_target_square =
 | 
				
			||||||
                    if position.active_color_can_castle(Wing::KingSide).is_ok() {
 | 
					                    if board.active_color_can_castle(Wing::KingSide).is_ok() {
 | 
				
			||||||
                        let parameters = board.castling_parameters(Wing::KingSide);
 | 
					                        let parameters = board.castling_parameters(Wing::KingSide);
 | 
				
			||||||
                        parameters.target.king.into()
 | 
					                        parameters.target.king.into()
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
| 
						 | 
					@ -35,7 +44,7 @@ impl Movement for Piece {
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let queenside_target_square =
 | 
					                let queenside_target_square =
 | 
				
			||||||
                    if position.active_color_can_castle(Wing::QueenSide).is_ok() {
 | 
					                    if board.active_color_can_castle(Wing::QueenSide).is_ok() {
 | 
				
			||||||
                        let parameters = board.castling_parameters(Wing::QueenSide);
 | 
					                        let parameters = board.castling_parameters(Wing::QueenSide);
 | 
				
			||||||
                        parameters.target.king.into()
 | 
					                        parameters.target.king.into()
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
| 
						 | 
					@ -16,9 +16,50 @@
 | 
				
			||||||
//! : The set of squares occupied by friendly pieces that block moves to that
 | 
					//! : The set of squares occupied by friendly pieces that block moves to that
 | 
				
			||||||
//! square and beyond.
 | 
					//! square and beyond.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use chessfriend_bitboard::BitBoard;
 | 
					use crate::Board;
 | 
				
			||||||
use chessfriend_board::Board;
 | 
					use chessfriend_bitboard::{BitBoard, IterationDirection};
 | 
				
			||||||
use chessfriend_core::{Color, Direction, Piece, Shape, Square};
 | 
					use chessfriend_core::{Color, Direction, Piece, Shape, Square};
 | 
				
			||||||
 | 
					use std::ops::BitOr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Board {
 | 
				
			||||||
 | 
					    /// Compute sight of the piece on the given square.
 | 
				
			||||||
 | 
					    pub fn sight(&self, square: Square) -> BitBoard {
 | 
				
			||||||
 | 
					        if let Some(piece) = self.get_piece(square) {
 | 
				
			||||||
 | 
					            piece.sight(square, self)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            BitBoard::empty()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn active_sight(&self) -> BitBoard {
 | 
				
			||||||
 | 
					        self.friendly_sight(self.active_color)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// A [`BitBoard`] of all squares the given color can see.
 | 
				
			||||||
 | 
					    pub fn friendly_sight(&self, color: Color) -> BitBoard {
 | 
				
			||||||
 | 
					        // TODO: Probably want to implement a caching layer here.
 | 
				
			||||||
 | 
					        self.friendly_occupancy(color)
 | 
				
			||||||
 | 
					            .occupied_squares(&IterationDirection::default())
 | 
				
			||||||
 | 
					            .map(|square| self.sight(square))
 | 
				
			||||||
 | 
					            .fold(BitBoard::empty(), BitOr::bitor)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// A [`BitBoard`] of all squares visible by colors that oppose the given color.
 | 
				
			||||||
 | 
					    pub fn opposing_sight(&self) -> BitBoard {
 | 
				
			||||||
 | 
					        // TODO: Probably want to implement a caching layer here.
 | 
				
			||||||
 | 
					        let active_color = self.active_color;
 | 
				
			||||||
 | 
					        Color::ALL
 | 
				
			||||||
 | 
					            .into_iter()
 | 
				
			||||||
 | 
					            .filter_map(|c| {
 | 
				
			||||||
 | 
					                if c == active_color {
 | 
				
			||||||
 | 
					                    None
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Some(self.friendly_sight(c))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .fold(BitBoard::empty(), BitOr::bitor)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait Sight {
 | 
					pub trait Sight {
 | 
				
			||||||
    fn sight(&self, square: Square, board: &Board) -> BitBoard;
 | 
					    fn sight(&self, square: Square, board: &Board) -> BitBoard;
 | 
				
			||||||
| 
						 | 
					@ -142,6 +183,7 @@ fn king_sight(info: &SightInfo) -> BitBoard {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use crate::test_board;
 | 
				
			||||||
    use chessfriend_bitboard::bitboard;
 | 
					    use chessfriend_bitboard::bitboard;
 | 
				
			||||||
    use chessfriend_core::piece;
 | 
					    use chessfriend_core::piece;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -154,16 +196,37 @@ mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let pos = $position;
 | 
					                let pos = $position;
 | 
				
			||||||
                let piece = $piece;
 | 
					                let piece = $piece;
 | 
				
			||||||
                let sight = piece.sight($square, &pos.board);
 | 
					                let sight = piece.sight($square, &pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                assert_eq!(sight, $bitboard);
 | 
					                assert_eq!(sight, $bitboard);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        ($test_name:ident, $piece:expr, $square:expr, $bitboard:expr) => {
 | 
					        ($test_name:ident, $piece:expr, $square:expr, $bitboard:expr) => {
 | 
				
			||||||
            sight_test! {$test_name, $crate::Position::empty(), $piece, $square, $bitboard}
 | 
					            sight_test! {$test_name, $crate::Board::empty(), $piece, $square, $bitboard}
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn friendly_sight() {
 | 
				
			||||||
 | 
					        let pos = test_board!(
 | 
				
			||||||
 | 
					            White King on E4,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let sight = pos.active_sight();
 | 
				
			||||||
 | 
					        assert_eq!(sight, bitboard![E5 F5 F4 F3 E3 D3 D4 D5]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn opposing_sight() {
 | 
				
			||||||
 | 
					        let pos = test_board!(
 | 
				
			||||||
 | 
					            White King on E4,
 | 
				
			||||||
 | 
					            Black Rook on E7,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let sight = pos.opposing_sight();
 | 
				
			||||||
 | 
					        assert_eq!(sight, bitboard![A7 B7 C7 D7 F7 G7 H7 E8 E6 E5 E4]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // #[test]
 | 
					    // #[test]
 | 
				
			||||||
    // fn pawns_and_knights_cannot_make_rays() {
 | 
					    // fn pawns_and_knights_cannot_make_rays() {
 | 
				
			||||||
    //     assert_eq!(Shape::Pawn.ray_to_square(Square::F7, Square::E8), None);
 | 
					    //     assert_eq!(Shape::Pawn.ray_to_square(Square::F7, Square::E8), None);
 | 
				
			||||||
| 
						 | 
					@ -171,7 +234,7 @@ mod tests {
 | 
				
			||||||
    // }
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mod pawn {
 | 
					    mod pawn {
 | 
				
			||||||
        use crate::{sight::Sight, test_position};
 | 
					        use crate::{sight::Sight, test_board};
 | 
				
			||||||
        use chessfriend_bitboard::{bitboard, BitBoard};
 | 
					        use chessfriend_bitboard::{bitboard, BitBoard};
 | 
				
			||||||
        use chessfriend_core::{piece, Square};
 | 
					        use chessfriend_core::{piece, Square};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -179,7 +242,7 @@ mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sight_test!(
 | 
					        sight_test!(
 | 
				
			||||||
            e4_pawn_one_blocker,
 | 
					            e4_pawn_one_blocker,
 | 
				
			||||||
            test_position![
 | 
					            test_board![
 | 
				
			||||||
                White Bishop on D5,
 | 
					                White Bishop on D5,
 | 
				
			||||||
                White Pawn on E4,
 | 
					                White Pawn on E4,
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
| 
						 | 
					@ -190,40 +253,40 @@ mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[test]
 | 
					        #[test]
 | 
				
			||||||
        fn e4_pawn_two_blocker() {
 | 
					        fn e4_pawn_two_blocker() {
 | 
				
			||||||
            let pos = test_position!(
 | 
					            let pos = test_board!(
 | 
				
			||||||
                White Bishop on D5,
 | 
					                White Bishop on D5,
 | 
				
			||||||
                White Queen on F5,
 | 
					                White Queen on F5,
 | 
				
			||||||
                White Pawn on E4,
 | 
					                White Pawn on E4,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let piece = piece!(White Pawn);
 | 
					            let piece = piece!(White Pawn);
 | 
				
			||||||
            let sight = piece.sight(Square::E4, &pos.board);
 | 
					            let sight = piece.sight(Square::E4, &pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert_eq!(sight, BitBoard::empty());
 | 
					            assert_eq!(sight, BitBoard::empty());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[test]
 | 
					        #[test]
 | 
				
			||||||
        fn e4_pawn_capturable() {
 | 
					        fn e4_pawn_capturable() {
 | 
				
			||||||
            let pos = test_position!(
 | 
					            let pos = test_board!(
 | 
				
			||||||
                Black Bishop on D5,
 | 
					                Black Bishop on D5,
 | 
				
			||||||
                White Queen on F5,
 | 
					                White Queen on F5,
 | 
				
			||||||
                White Pawn on E4,
 | 
					                White Pawn on E4,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let piece = piece!(White Pawn);
 | 
					            let piece = piece!(White Pawn);
 | 
				
			||||||
            let sight = piece.sight(Square::E4, &pos.board);
 | 
					            let sight = piece.sight(Square::E4, &pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert_eq!(sight, bitboard![D5]);
 | 
					            assert_eq!(sight, bitboard![D5]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[test]
 | 
					        #[test]
 | 
				
			||||||
        fn e5_en_passant() {
 | 
					        fn e5_en_passant() {
 | 
				
			||||||
            let pos = test_position!(White, [
 | 
					            let pos = test_board!(White, [
 | 
				
			||||||
                White Pawn on E5,
 | 
					                White Pawn on E5,
 | 
				
			||||||
                Black Pawn on D5,
 | 
					                Black Pawn on D5,
 | 
				
			||||||
            ], D6);
 | 
					            ], D6);
 | 
				
			||||||
            let piece = piece!(White Pawn);
 | 
					            let piece = piece!(White Pawn);
 | 
				
			||||||
            let sight = piece.sight(Square::E5, &pos.board);
 | 
					            let sight = piece.sight(Square::E5, &pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert_eq!(sight, bitboard!(D6 F6));
 | 
					            assert_eq!(sight, bitboard!(D6 F6));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -262,7 +325,7 @@ mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mod rook {
 | 
					    mod rook {
 | 
				
			||||||
        use super::*;
 | 
					        use super::*;
 | 
				
			||||||
        use crate::test_position;
 | 
					        use crate::test_board;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sight_test!(
 | 
					        sight_test!(
 | 
				
			||||||
            g3_rook,
 | 
					            g3_rook,
 | 
				
			||||||
| 
						 | 
					@ -273,7 +336,7 @@ mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sight_test!(
 | 
					        sight_test!(
 | 
				
			||||||
            e4_rook_with_e1_white_king_e7_black_king,
 | 
					            e4_rook_with_e1_white_king_e7_black_king,
 | 
				
			||||||
            test_position![
 | 
					            test_board![
 | 
				
			||||||
                White Rook on E4,
 | 
					                White Rook on E4,
 | 
				
			||||||
                White King on E2,
 | 
					                White King on E2,
 | 
				
			||||||
                Black King on E7,
 | 
					                Black King on E7,
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,6 @@
 | 
				
			||||||
// Eryn Wells <eryn@erynwells.me>
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod movement;
 | 
					 | 
				
			||||||
mod position;
 | 
					mod position;
 | 
				
			||||||
mod sight;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[macro_use]
 | 
					#[macro_use]
 | 
				
			||||||
mod macros;
 | 
					mod macros;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
// Eryn Wells <eryn@erynwells.me>
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{movement::Movement, Position};
 | 
					use crate::Position;
 | 
				
			||||||
use chessfriend_board::{PlacePieceError, PlacePieceStrategy};
 | 
					use chessfriend_board::{movement::Movement, PlacePieceError, PlacePieceStrategy};
 | 
				
			||||||
use chessfriend_core::{Color, Piece, Rank, Square, Wing};
 | 
					use chessfriend_core::{Color, Piece, Rank, Square, Wing};
 | 
				
			||||||
use chessfriend_moves::Move;
 | 
					use chessfriend_moves::Move;
 | 
				
			||||||
use thiserror::Error;
 | 
					use thiserror::Error;
 | 
				
			||||||
| 
						 | 
					@ -276,7 +276,7 @@ impl Position {
 | 
				
			||||||
        // Pawns can see squares they can't move to. So, calculating valid
 | 
					        // Pawns can see squares they can't move to. So, calculating valid
 | 
				
			||||||
        // squares requires a concept that includes Sight, but adds pawn pushes.
 | 
					        // squares requires a concept that includes Sight, but adds pawn pushes.
 | 
				
			||||||
        // In ChessFriend, that concept is Movement.
 | 
					        // In ChessFriend, that concept is Movement.
 | 
				
			||||||
        let movement = active_piece.movement(origin_square, self);
 | 
					        let movement = active_piece.movement(origin_square, &self.board);
 | 
				
			||||||
        if !movement.contains(target_square) {
 | 
					        if !movement.contains(target_square) {
 | 
				
			||||||
            return Err(MakeMoveError::NoMove {
 | 
					            return Err(MakeMoveError::NoMove {
 | 
				
			||||||
                piece: active_piece,
 | 
					                piece: active_piece,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,11 @@
 | 
				
			||||||
// Eryn Wells <eryn@erynwells.me>
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{movement::Movement, sight::Sight};
 | 
					use chessfriend_bitboard::BitBoard;
 | 
				
			||||||
use chessfriend_bitboard::{BitBoard, IterationDirection};
 | 
					 | 
				
			||||||
use chessfriend_board::{
 | 
					use chessfriend_board::{
 | 
				
			||||||
    display::DiagramFormatter, en_passant::EnPassant, fen::ToFenStr, Board, PlacePieceError,
 | 
					    display::DiagramFormatter, fen::ToFenStr, Board, PlacePieceError, PlacePieceStrategy,
 | 
				
			||||||
    PlacePieceStrategy,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use chessfriend_core::{Color, Piece, Square};
 | 
					use chessfriend_core::{Color, Piece, Square};
 | 
				
			||||||
use std::{cell::OnceCell, fmt, ops::BitOr};
 | 
					use std::{cell::OnceCell, fmt};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[must_use]
 | 
					#[must_use]
 | 
				
			||||||
#[derive(Clone, Debug, Eq)]
 | 
					#[derive(Clone, Debug, Eq)]
 | 
				
			||||||
| 
						 | 
					@ -64,51 +62,27 @@ impl Position {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Position {
 | 
					impl Position {
 | 
				
			||||||
    pub fn sight(&self, square: Square) -> BitBoard {
 | 
					    pub fn sight(&self, square: Square) -> BitBoard {
 | 
				
			||||||
        if let Some(piece) = self.get_piece(square) {
 | 
					        self.board.sight(square)
 | 
				
			||||||
            piece.sight(square, &self.board)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            BitBoard::empty()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn movement(&self, square: Square) -> BitBoard {
 | 
					    pub fn movement(&self, square: Square) -> BitBoard {
 | 
				
			||||||
        if let Some(piece) = self.get_piece(square) {
 | 
					        self.board.movement(square)
 | 
				
			||||||
            piece.movement(square, self)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            BitBoard::empty()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Position {
 | 
					impl Position {
 | 
				
			||||||
    pub fn active_sight(&self) -> BitBoard {
 | 
					    pub fn active_sight(&self) -> BitBoard {
 | 
				
			||||||
        self.friendly_sight(self.board.active_color)
 | 
					        self.board.active_sight()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// A [`BitBoard`] of all squares the given color can see.
 | 
					    /// A [`BitBoard`] of all squares the given color can see.
 | 
				
			||||||
    pub fn friendly_sight(&self, color: Color) -> BitBoard {
 | 
					    pub fn friendly_sight(&self, color: Color) -> BitBoard {
 | 
				
			||||||
        // TODO: Probably want to implement a caching layer here.
 | 
					        self.board.friendly_sight(color)
 | 
				
			||||||
        self.board
 | 
					 | 
				
			||||||
            .friendly_occupancy(color)
 | 
					 | 
				
			||||||
            .occupied_squares(&IterationDirection::default())
 | 
					 | 
				
			||||||
            .map(|square| self.sight(square))
 | 
					 | 
				
			||||||
            .fold(BitBoard::empty(), BitOr::bitor)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// A [`BitBoard`] of all squares visible by colors that oppose the given color.
 | 
					    /// A [`BitBoard`] of all squares visible by colors that oppose the given color.
 | 
				
			||||||
    pub fn opposing_sight(&self) -> BitBoard {
 | 
					    pub fn opposing_sight(&self) -> BitBoard {
 | 
				
			||||||
        // TODO: Probably want to implement a caching layer here.
 | 
					        self.board.opposing_sight()
 | 
				
			||||||
        let active_color = self.board.active_color;
 | 
					 | 
				
			||||||
        Color::ALL
 | 
					 | 
				
			||||||
            .into_iter()
 | 
					 | 
				
			||||||
            .filter_map(|c| {
 | 
					 | 
				
			||||||
                if c == active_color {
 | 
					 | 
				
			||||||
                    None
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    Some(self.friendly_sight(c))
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .fold(BitBoard::empty(), BitOr::bitor)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -292,27 +266,6 @@ mod tests {
 | 
				
			||||||
    //     assert!(!rights.color_has_right(Color::White, Castle::QueenSide));
 | 
					    //     assert!(!rights.color_has_right(Color::White, Castle::QueenSide));
 | 
				
			||||||
    // }
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn friendly_sight() {
 | 
					 | 
				
			||||||
        let pos = test_position!(
 | 
					 | 
				
			||||||
            White King on E4,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let sight = pos.active_sight();
 | 
					 | 
				
			||||||
        assert_eq!(sight, bitboard![E5 F5 F4 F3 E3 D3 D4 D5]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn opposing_sight() {
 | 
					 | 
				
			||||||
        let pos = test_position!(
 | 
					 | 
				
			||||||
            White King on E4,
 | 
					 | 
				
			||||||
            Black Rook on E7,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let sight = pos.opposing_sight();
 | 
					 | 
				
			||||||
        assert_eq!(sight, bitboard![A7 B7 C7 D7 F7 G7 H7 E8 E6 E5 E4]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // #[test]
 | 
					    // #[test]
 | 
				
			||||||
    // fn danger_squares() {
 | 
					    // fn danger_squares() {
 | 
				
			||||||
    //     let pos = test_position!(Black, [
 | 
					    //     let pos = test_position!(Black, [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue