WIP
This commit is contained in:
		
							parent
							
								
									d5cdf273c8
								
							
						
					
					
						commit
						091cc99cb3
					
				
					 42 changed files with 805 additions and 1662 deletions
				
			
		| 
						 | 
				
			
			@ -8,4 +8,5 @@ edition = "2021"
 | 
			
		|||
[dependencies]
 | 
			
		||||
chessfriend_core = { path = "../core" }
 | 
			
		||||
chessfriend_bitboard = { path = "../bitboard" }
 | 
			
		||||
chessfriend_board = { path = "../board" }
 | 
			
		||||
chessfriend_moves = { path = "../moves" }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,8 +40,8 @@ impl CheckingPieces {
 | 
			
		|||
 | 
			
		||||
    /// A BitBoard representing the set of squares to which a player can move a piece to block a
 | 
			
		||||
    /// checking piece.
 | 
			
		||||
    pub fn push_mask(&self, king: &BitBoard) -> BitBoard {
 | 
			
		||||
        let target = king.first_occupied_square().unwrap();
 | 
			
		||||
    pub fn push_mask(&self, king: BitBoard) -> BitBoard {
 | 
			
		||||
        let target = king.first_occupied_square_leading().unwrap();
 | 
			
		||||
 | 
			
		||||
        macro_rules! push_mask_for_shape {
 | 
			
		||||
            ($push_mask:expr, $shape:ident, $king:expr) => {{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,4 +13,4 @@ mod macros;
 | 
			
		|||
#[macro_use]
 | 
			
		||||
mod testing;
 | 
			
		||||
 | 
			
		||||
pub use position::{MakeMoveError, MoveBuilder as MakeMoveBuilder, Position, PositionBuilder};
 | 
			
		||||
pub use position::{MakeMoveError, MoveBuilder as MakeMoveBuilder, Position};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,16 +3,7 @@
 | 
			
		|||
#[macro_export]
 | 
			
		||||
macro_rules! position {
 | 
			
		||||
    [$($color:ident $shape:ident on $square:ident),* $(,)?] => {
 | 
			
		||||
        $crate::PositionBuilder::new()
 | 
			
		||||
            $(.place_piece(
 | 
			
		||||
                chessfriend_core::PlacedPiece::new(
 | 
			
		||||
                    chessfriend_core::Piece::new(
 | 
			
		||||
                        chessfriend_core::Color::$color,
 | 
			
		||||
                        chessfriend_core::Shape::$shape),
 | 
			
		||||
                    chessfriend_core::Square::$square
 | 
			
		||||
                )
 | 
			
		||||
            ))*
 | 
			
		||||
            .build()
 | 
			
		||||
        $crate::Position::new(chessfriend_board::board!($($color $shape on $square),*))
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -21,72 +12,26 @@ macro_rules! position {
 | 
			
		|||
macro_rules! test_position {
 | 
			
		||||
    ($to_move:ident, [ $($color:ident $shape:ident on $square:ident),* $(,)? ], $en_passant:ident) => {
 | 
			
		||||
        {
 | 
			
		||||
            let pos = $crate::PositionBuilder::new()
 | 
			
		||||
            $(.place_piece(
 | 
			
		||||
                chessfriend_core::PlacedPiece::new(
 | 
			
		||||
                    chessfriend_core::Piece::new(
 | 
			
		||||
                        chessfriend_core::Color::$color,
 | 
			
		||||
                        chessfriend_core::Shape::$shape
 | 
			
		||||
                    ),
 | 
			
		||||
                    chessfriend_core::Square::$square
 | 
			
		||||
                ))
 | 
			
		||||
            )*
 | 
			
		||||
            .to_move(chessfriend_core::Color::$to_move)
 | 
			
		||||
            .en_passant(Some(chessfriend_moves::EnPassant::from_target_square(chessfriend_core::Square::$en_passant)).unwrap())
 | 
			
		||||
            .build();
 | 
			
		||||
            println!("{pos}");
 | 
			
		||||
 | 
			
		||||
            pos
 | 
			
		||||
            let board = chessfriend_board::test_board!($to_move, [ $($color $shape on $square),*], $en_passant);
 | 
			
		||||
            $crate::Position::new(board)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($to_move:ident, [ $($color:ident $shape:ident on $square:ident),* $(,)? ]) => {
 | 
			
		||||
        {
 | 
			
		||||
            let pos = $crate::PositionBuilder::new()
 | 
			
		||||
            $(.place_piece(
 | 
			
		||||
                chessfriend_core::PlacedPiece::new(
 | 
			
		||||
                    chessfriend_core::Piece::new(
 | 
			
		||||
                        chessfriend_core::Color::$color,
 | 
			
		||||
                        chessfriend_core::Shape::$shape
 | 
			
		||||
                    ),
 | 
			
		||||
                    chessfriend_core::Square::$square
 | 
			
		||||
                ))
 | 
			
		||||
            )*
 | 
			
		||||
            .to_move(chessfriend_core::Color::$to_move)
 | 
			
		||||
            .build();
 | 
			
		||||
            println!("{pos}");
 | 
			
		||||
 | 
			
		||||
            pos
 | 
			
		||||
            let board = chessfriend_board::test_board!($to_move, [ $($color $shape on $square),* ]);
 | 
			
		||||
            $crate::Position::new(board)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($($color:ident $shape:ident on $square:ident),* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            let pos = $crate::PositionBuilder::new()
 | 
			
		||||
                $(.place_piece(
 | 
			
		||||
                    chessfriend_core::PlacedPiece::new(
 | 
			
		||||
                        chessfriend_core::Piece::new(
 | 
			
		||||
                            chessfriend_core::Color::$color,
 | 
			
		||||
                            chessfriend_core::Shape::$shape
 | 
			
		||||
                        ),
 | 
			
		||||
                        chessfriend_core::Square::$square
 | 
			
		||||
                    ))
 | 
			
		||||
                )*
 | 
			
		||||
                .build();
 | 
			
		||||
            println!("{pos}");
 | 
			
		||||
            pos
 | 
			
		||||
            let board = chessfriend_board::test_board!($($color $shape on $square),*);
 | 
			
		||||
            $crate::Position::new(board)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (empty) => {
 | 
			
		||||
        {
 | 
			
		||||
            let pos = Position::empty();
 | 
			
		||||
            println!("{pos}");
 | 
			
		||||
            pos
 | 
			
		||||
        }
 | 
			
		||||
        Position::new(chessfriend_board::test_board!(empty))
 | 
			
		||||
    };
 | 
			
		||||
    (starting) => {
 | 
			
		||||
        {
 | 
			
		||||
            let pos = Position::starting();
 | 
			
		||||
            println!("{pos}");
 | 
			
		||||
            pos
 | 
			
		||||
        }
 | 
			
		||||
        Position::new(chessfriend_board::test_board!(starting))
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,7 @@ use self::{
 | 
			
		|||
};
 | 
			
		||||
use crate::Position;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::Board;
 | 
			
		||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
 | 
			
		||||
use chessfriend_moves::Move;
 | 
			
		||||
use std::collections::BTreeMap;
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +51,7 @@ macro_rules! move_generator_declaration {
 | 
			
		|||
    ($name:ident, new) => {
 | 
			
		||||
        impl $name {
 | 
			
		||||
            pub(super) fn new(
 | 
			
		||||
                position: &$crate::Position,
 | 
			
		||||
                board: &chessfriend_board::Board,
 | 
			
		||||
                color: chessfriend_core::Color,
 | 
			
		||||
                capture_mask: chessfriend_bitboard::BitBoard,
 | 
			
		||||
                push_mask: chessfriend_bitboard::BitBoard,
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +59,7 @@ macro_rules! move_generator_declaration {
 | 
			
		|||
                let move_sets = if Self::shape() == chessfriend_core::Shape::King
 | 
			
		||||
                    || !(capture_mask.is_empty() && push_mask.is_empty())
 | 
			
		||||
                {
 | 
			
		||||
                    Self::move_sets(position, color, capture_mask, push_mask)
 | 
			
		||||
                    Self::move_sets(board, color, capture_mask, push_mask)
 | 
			
		||||
                } else {
 | 
			
		||||
                    std::collections::BTreeMap::new()
 | 
			
		||||
                };
 | 
			
		||||
| 
						 | 
				
			
			@ -101,27 +102,26 @@ trait MoveGeneratorInternal {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn move_sets(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        color: Color,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
    ) -> BTreeMap<Square, MoveSet> {
 | 
			
		||||
        let piece = Self::piece(color);
 | 
			
		||||
        BTreeMap::from_iter(
 | 
			
		||||
            position
 | 
			
		||||
            board
 | 
			
		||||
                .bitboard_for_piece(piece)
 | 
			
		||||
                .occupied_squares()
 | 
			
		||||
                .map(|square| {
 | 
			
		||||
                    let piece = PlacedPiece::new(piece, square);
 | 
			
		||||
                    let move_set =
 | 
			
		||||
                        Self::move_set_for_piece(position, &piece, capture_mask, push_mask);
 | 
			
		||||
                    let move_set = Self::move_set_for_piece(board, &piece, capture_mask, push_mask);
 | 
			
		||||
                    (square, move_set)
 | 
			
		||||
                }),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn move_set_for_piece(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        piece: &PlacedPiece,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
| 
						 | 
				
			
			@ -139,19 +139,14 @@ pub struct Moves {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl Moves {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        color: Color,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
    ) -> Moves {
 | 
			
		||||
    pub fn new(board: &Board, color: Color, capture_mask: BitBoard, push_mask: BitBoard) -> Moves {
 | 
			
		||||
        Moves {
 | 
			
		||||
            pawn_moves: PawnMoveGenerator::new(position, color, capture_mask, push_mask),
 | 
			
		||||
            knight_moves: KnightMoveGenerator::new(position, color, capture_mask, push_mask),
 | 
			
		||||
            bishop_moves: BishopMoveGenerator::new(position, color, capture_mask, push_mask),
 | 
			
		||||
            rook_moves: RookMoveGenerator::new(position, color, capture_mask, push_mask),
 | 
			
		||||
            queen_moves: QueenMoveGenerator::new(position, color, capture_mask, push_mask),
 | 
			
		||||
            king_moves: KingMoveGenerator::new(position, color, capture_mask, push_mask),
 | 
			
		||||
            pawn_moves: PawnMoveGenerator::new(board, color, capture_mask, push_mask),
 | 
			
		||||
            knight_moves: KnightMoveGenerator::new(board, color, capture_mask, push_mask),
 | 
			
		||||
            bishop_moves: BishopMoveGenerator::new(board, color, capture_mask, push_mask),
 | 
			
		||||
            rook_moves: RookMoveGenerator::new(board, color, capture_mask, push_mask),
 | 
			
		||||
            queen_moves: QueenMoveGenerator::new(board, color, capture_mask, push_mask),
 | 
			
		||||
            king_moves: KingMoveGenerator::new(board, color, capture_mask, push_mask),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
 | 
			
		||||
use crate::Position;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::Board;
 | 
			
		||||
use chessfriend_core::{Direction, PlacedPiece, Shape};
 | 
			
		||||
 | 
			
		||||
move_generator_declaration!(ClassicalMoveGenerator);
 | 
			
		||||
| 
						 | 
				
			
			@ -13,16 +13,16 @@ impl MoveGeneratorInternal for ClassicalMoveGenerator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn move_set_for_piece(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        piece: &PlacedPiece,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
    ) -> MoveSet {
 | 
			
		||||
        let square = piece.square();
 | 
			
		||||
 | 
			
		||||
        let blockers = position.occupied_squares();
 | 
			
		||||
        let blockers = board.occupied_squares();
 | 
			
		||||
        let empty_squares = !blockers;
 | 
			
		||||
        let (friendly_pieces, opposing_pieces) = position.all_pieces();
 | 
			
		||||
        let (friendly_pieces, opposing_pieces) = board.all_pieces();
 | 
			
		||||
 | 
			
		||||
        let mut all_moves = BitBoard::empty();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +58,7 @@ impl MoveGeneratorInternal for ClassicalMoveGenerator {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::{position, position::DiagramFormatter};
 | 
			
		||||
    use crate::position;
 | 
			
		||||
    use chessfriend_bitboard::BitBoard;
 | 
			
		||||
    use chessfriend_core::Color;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -69,7 +69,7 @@ mod tests {
 | 
			
		|||
        ];
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
| 
						 | 
				
			
			@ -87,10 +87,10 @@ mod tests {
 | 
			
		|||
            White Knight on E5,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        println!("{}", DiagramFormatter::new(&pos));
 | 
			
		||||
        println!("{}", pos.display());
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +109,7 @@ mod tests {
 | 
			
		|||
        ];
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
| 
						 | 
				
			
			@ -125,10 +125,10 @@ mod tests {
 | 
			
		|||
            White Bishop on E4,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        println!("{}", DiagramFormatter::new(&pos));
 | 
			
		||||
        println!("{}", pos.display());
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let bitboard = generator._test_bitboard();
 | 
			
		||||
        let expected = BitBoard::new(
 | 
			
		||||
            0b00000001_10000010_01000100_00101000_00000000_00101000_01000100_10000010,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,10 +4,9 @@
 | 
			
		|||
//! generating the possible moves for the king in the given position.
 | 
			
		||||
 | 
			
		||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
 | 
			
		||||
use crate::Position;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::{castle::Castle, Board};
 | 
			
		||||
use chessfriend_core::{PlacedPiece, Shape};
 | 
			
		||||
use chessfriend_moves::Castle;
 | 
			
		||||
 | 
			
		||||
move_generator_declaration!(KingMoveGenerator, struct);
 | 
			
		||||
move_generator_declaration!(KingMoveGenerator, new);
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +18,7 @@ impl MoveGeneratorInternal for KingMoveGenerator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn move_set_for_piece(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        placed_piece: &PlacedPiece,
 | 
			
		||||
        _capture_mask: BitBoard,
 | 
			
		||||
        _push_mask: BitBoard,
 | 
			
		||||
| 
						 | 
				
			
			@ -28,13 +27,13 @@ impl MoveGeneratorInternal for KingMoveGenerator {
 | 
			
		|||
        let color = piece.color();
 | 
			
		||||
        let square = placed_piece.square();
 | 
			
		||||
 | 
			
		||||
        let safe_squares = !position.king_danger(color);
 | 
			
		||||
        let safe_squares = BitBoard::FULL;
 | 
			
		||||
        let all_king_moves = BitBoard::king_moves(square);
 | 
			
		||||
 | 
			
		||||
        let empty_squares = position.empty_squares();
 | 
			
		||||
        let empty_squares = board.empty_squares();
 | 
			
		||||
        let safe_empty_squares = empty_squares & safe_squares;
 | 
			
		||||
 | 
			
		||||
        let opposing_pieces = position.bitboard_for_color(color.other());
 | 
			
		||||
        let opposing_pieces = board.bitboard_for_color(color.other());
 | 
			
		||||
        let opposing_pieces_on_safe_squares = opposing_pieces & safe_squares;
 | 
			
		||||
 | 
			
		||||
        let quiet_moves = all_king_moves & safe_empty_squares;
 | 
			
		||||
| 
						 | 
				
			
			@ -44,10 +43,10 @@ impl MoveGeneratorInternal for KingMoveGenerator {
 | 
			
		|||
            .quiet_moves(quiet_moves)
 | 
			
		||||
            .capture_moves(capture_moves);
 | 
			
		||||
 | 
			
		||||
        if position.player_can_castle(color, Castle::KingSide) {
 | 
			
		||||
        if board.player_can_castle(color, Castle::KingSide) {
 | 
			
		||||
            move_set.kingside_castle();
 | 
			
		||||
        }
 | 
			
		||||
        if position.player_can_castle(color, Castle::QueenSide) {
 | 
			
		||||
        if board.player_can_castle(color, Castle::QueenSide) {
 | 
			
		||||
            move_set.queenside_castle();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,21 +57,23 @@ impl MoveGeneratorInternal for KingMoveGenerator {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::{assert_move_list, position, test_position, testing::*, PositionBuilder};
 | 
			
		||||
    use crate::{assert_move_list, test_position, testing::*};
 | 
			
		||||
    use chessfriend_bitboard::bitboard;
 | 
			
		||||
    use chessfriend_board::castle::Castle;
 | 
			
		||||
    use chessfriend_core::{piece, Color, Square};
 | 
			
		||||
    use chessfriend_moves::{Builder as MoveBuilder, Castle, Move};
 | 
			
		||||
    use chessfriend_moves::{Builder as MoveBuilder, Move};
 | 
			
		||||
    use std::collections::HashSet;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn one_king() -> TestResult {
 | 
			
		||||
        let pos = position![White King on E4];
 | 
			
		||||
        let pos = test_position![White King on E4];
 | 
			
		||||
 | 
			
		||||
        let generator = KingMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
            bitboard![E5, F5, F4, F3, E3, D3, D4, D5]
 | 
			
		||||
            bitboard![E5 F5 F4 F3 E3 D3 D4 D5]
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let builder = MoveBuilder::push(&piece!(White King on E4));
 | 
			
		||||
| 
						 | 
				
			
			@ -96,15 +97,16 @@ mod tests {
 | 
			
		|||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn one_king_corner() -> TestResult {
 | 
			
		||||
        let pos = position![White King on A1];
 | 
			
		||||
        let pos = test_position![White King on A1];
 | 
			
		||||
 | 
			
		||||
        let generator = KingMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        let generated_bitboard = generator._test_bitboard();
 | 
			
		||||
        let expected_bitboard = bitboard![A2, B2, B1];
 | 
			
		||||
        let expected_bitboard = bitboard![A2 B2 B1];
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
            bitboard![A2, B2, B1],
 | 
			
		||||
            bitboard![A2 B2 B1],
 | 
			
		||||
            "Generated:\n{generated_bitboard}\nExpected:\n{expected_bitboard}"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -136,19 +138,19 @@ mod tests {
 | 
			
		|||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn black_king_in_check_by_rook() {
 | 
			
		||||
        let pos = PositionBuilder::new()
 | 
			
		||||
            .place_piece(piece!(White King on E1))
 | 
			
		||||
            .place_piece(piece!(White Rook on E4))
 | 
			
		||||
            .place_piece(piece!(Black King on E7))
 | 
			
		||||
            .to_move(Color::Black)
 | 
			
		||||
            .build();
 | 
			
		||||
        let pos = test_position!(Black, [
 | 
			
		||||
            White King on E1,
 | 
			
		||||
            White Rook on E4,
 | 
			
		||||
            Black King on E7,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        assert!(pos.is_king_in_check());
 | 
			
		||||
 | 
			
		||||
        let generator = KingMoveGenerator::new(&pos, Color::Black, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            KingMoveGenerator::new(&pos.board, Color::Black, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generated_moves = generator._test_bitboard();
 | 
			
		||||
 | 
			
		||||
        let expected_moves = bitboard![F8, F7, F6, D6, D7, D8];
 | 
			
		||||
        let expected_moves = bitboard![F8 F7 F6 D6 D7 D8];
 | 
			
		||||
 | 
			
		||||
        assert_eq!(generated_moves, expected_moves);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +166,8 @@ mod tests {
 | 
			
		|||
        assert!(pos.player_can_castle(Color::White, Castle::KingSide));
 | 
			
		||||
        assert!(pos.player_can_castle(Color::White, Castle::QueenSide));
 | 
			
		||||
 | 
			
		||||
        let generator = KingMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generated_moves: HashSet<Move> = generator.iter().collect();
 | 
			
		||||
 | 
			
		||||
        assert!(generated_moves
 | 
			
		||||
| 
						 | 
				
			
			@ -187,7 +190,8 @@ mod tests {
 | 
			
		|||
        assert!(pos.player_can_castle(Color::White, Castle::KingSide));
 | 
			
		||||
        assert!(!pos.player_can_castle(Color::White, Castle::QueenSide));
 | 
			
		||||
 | 
			
		||||
        let generator = KingMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generated_moves: HashSet<Move> = generator.iter().collect();
 | 
			
		||||
 | 
			
		||||
        assert!(generated_moves
 | 
			
		||||
| 
						 | 
				
			
			@ -210,7 +214,8 @@ mod tests {
 | 
			
		|||
        assert!(!pos.player_can_castle(Color::White, Castle::KingSide));
 | 
			
		||||
        assert!(pos.player_can_castle(Color::White, Castle::QueenSide));
 | 
			
		||||
 | 
			
		||||
        let generator = KingMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            KingMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generated_moves: HashSet<Move> = generator.iter().collect();
 | 
			
		||||
 | 
			
		||||
        assert!(!generated_moves
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
 | 
			
		||||
use crate::Position;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::Board;
 | 
			
		||||
use chessfriend_core::{PlacedPiece, Shape};
 | 
			
		||||
 | 
			
		||||
move_generator_declaration!(KnightMoveGenerator);
 | 
			
		||||
| 
						 | 
				
			
			@ -13,13 +13,13 @@ impl MoveGeneratorInternal for KnightMoveGenerator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn move_set_for_piece(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        placed_piece: &PlacedPiece,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
    ) -> MoveSet {
 | 
			
		||||
        let opposing_pieces = position.bitboard_for_color(placed_piece.piece().color().other());
 | 
			
		||||
        let empty_squares = position.empty_squares();
 | 
			
		||||
        let opposing_pieces = board.bitboard_for_color(placed_piece.piece().color().other());
 | 
			
		||||
        let empty_squares = board.empty_squares();
 | 
			
		||||
        let knight_moves = BitBoard::knight_moves(placed_piece.square());
 | 
			
		||||
 | 
			
		||||
        let quiet_moves = knight_moves & empty_squares & push_mask;
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ mod tests {
 | 
			
		|||
        ];
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            KnightMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            KnightMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generated_moves: HashSet<_> = generator.iter().collect();
 | 
			
		||||
 | 
			
		||||
        let piece = piece!(White Knight on E4);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::{castle::Castle, en_passant::EnPassant};
 | 
			
		||||
use chessfriend_core::{PlacedPiece, Square};
 | 
			
		||||
use chessfriend_moves::{Builder as MoveBuilder, Castle, EnPassant, Move};
 | 
			
		||||
use chessfriend_moves::{Builder as MoveBuilder, Move};
 | 
			
		||||
 | 
			
		||||
/// A set of bitboards defining the moves for a single piece on the board.
 | 
			
		||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +64,7 @@ impl MoveSet {
 | 
			
		|||
            None => {}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.bitboard().is_set(target_square)
 | 
			
		||||
        self.bitboard().contains(target_square)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn can_castle(&self, castle: Castle) -> bool {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,10 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
 | 
			
		||||
use crate::Position;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::{en_passant::EnPassant, Board};
 | 
			
		||||
use chessfriend_core::{Color, PlacedPiece, Rank, Shape, Square};
 | 
			
		||||
use chessfriend_moves::{EnPassant, Move};
 | 
			
		||||
use chessfriend_moves::Move;
 | 
			
		||||
use std::collections::BTreeMap;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
| 
						 | 
				
			
			@ -25,21 +25,19 @@ impl MoveGeneratorInternal for PawnMoveGenerator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn move_set_for_piece(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        placed_piece: &PlacedPiece,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
    ) -> MoveSet {
 | 
			
		||||
        let capture_moves = Self::attacks(position, &placed_piece) & capture_mask;
 | 
			
		||||
        let quiet_moves = Self::pushes(position, &placed_piece) & push_mask;
 | 
			
		||||
        let capture_moves = Self::attacks(board, &placed_piece) & capture_mask;
 | 
			
		||||
        let quiet_moves = Self::pushes(board, &placed_piece) & push_mask;
 | 
			
		||||
 | 
			
		||||
        let mut move_set = MoveSet::new(*placed_piece)
 | 
			
		||||
            .quiet_moves(quiet_moves)
 | 
			
		||||
            .capture_moves(capture_moves);
 | 
			
		||||
 | 
			
		||||
        if let Some(en_passant) =
 | 
			
		||||
            Self::en_passant(position, placed_piece, &push_mask, &capture_mask)
 | 
			
		||||
        {
 | 
			
		||||
        if let Some(en_passant) = Self::en_passant(board, placed_piece, &push_mask, &capture_mask) {
 | 
			
		||||
            move_set.en_passant(en_passant);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,13 +47,13 @@ impl MoveGeneratorInternal for PawnMoveGenerator {
 | 
			
		|||
 | 
			
		||||
impl PawnMoveGenerator {
 | 
			
		||||
    pub(super) fn new(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        player_to_move: Color,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let move_sets = if !(capture_mask.is_empty() && push_mask.is_empty()) {
 | 
			
		||||
            Self::move_sets(position, player_to_move, capture_mask, push_mask)
 | 
			
		||||
            Self::move_sets(board, player_to_move, capture_mask, push_mask)
 | 
			
		||||
        } else {
 | 
			
		||||
            std::collections::BTreeMap::new()
 | 
			
		||||
        };
 | 
			
		||||
| 
						 | 
				
			
			@ -68,32 +66,33 @@ impl PawnMoveGenerator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn move_sets(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        color: Color,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
    ) -> BTreeMap<Square, MoveSet> {
 | 
			
		||||
        let piece = Self::piece(color);
 | 
			
		||||
        let moves_for_pieces =
 | 
			
		||||
            BTreeMap::from_iter(position.bitboard_for_piece(piece).occupied_squares().map(
 | 
			
		||||
                |square| {
 | 
			
		||||
        let moves_for_pieces = BTreeMap::from_iter(
 | 
			
		||||
            board
 | 
			
		||||
                .bitboard_for_piece(piece)
 | 
			
		||||
                .occupied_squares()
 | 
			
		||||
                .map(|square| {
 | 
			
		||||
                    let piece = PlacedPiece::new(piece, square);
 | 
			
		||||
                    let move_set =
 | 
			
		||||
                        Self::move_set_for_piece(position, &piece, capture_mask, push_mask);
 | 
			
		||||
                    let move_set = Self::move_set_for_piece(board, &piece, capture_mask, push_mask);
 | 
			
		||||
                    (square, move_set)
 | 
			
		||||
                },
 | 
			
		||||
            ));
 | 
			
		||||
                }),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        moves_for_pieces
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn pushes(position: &Position, piece: &PlacedPiece) -> BitBoard {
 | 
			
		||||
    fn pushes(board: &Board, piece: &PlacedPiece) -> BitBoard {
 | 
			
		||||
        let color = piece.color();
 | 
			
		||||
        let square = piece.square();
 | 
			
		||||
        let bitboard: BitBoard = square.into();
 | 
			
		||||
 | 
			
		||||
        let starting_rank = Rank::PAWN_STARTING_RANKS[color as usize];
 | 
			
		||||
        let empty_squares = position.empty_squares();
 | 
			
		||||
        let empty_squares = board.empty_squares();
 | 
			
		||||
 | 
			
		||||
        match color {
 | 
			
		||||
            Color::White => {
 | 
			
		||||
| 
						 | 
				
			
			@ -115,21 +114,21 @@ impl PawnMoveGenerator {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn attacks(position: &Position, piece: &PlacedPiece) -> BitBoard {
 | 
			
		||||
    fn attacks(board: &Board, piece: &PlacedPiece) -> BitBoard {
 | 
			
		||||
        let color = piece.color();
 | 
			
		||||
 | 
			
		||||
        let opponent_pieces = position.bitboard_for_color(color.other());
 | 
			
		||||
        let opponent_pieces = board.bitboard_for_color(color.other());
 | 
			
		||||
 | 
			
		||||
        BitBoard::pawn_attacks(piece.square(), color) & opponent_pieces
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn en_passant(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        piece: &PlacedPiece,
 | 
			
		||||
        push_mask: &BitBoard,
 | 
			
		||||
        capture_mask: &BitBoard,
 | 
			
		||||
    ) -> Option<EnPassant> {
 | 
			
		||||
        match position.en_passant() {
 | 
			
		||||
        match board.en_passant() {
 | 
			
		||||
            Some(en_passant) => {
 | 
			
		||||
                let target_square: BitBoard = en_passant.target_square().into();
 | 
			
		||||
                let capture_square: BitBoard = en_passant.capture_square().into();
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +146,7 @@ impl PawnMoveGenerator {
 | 
			
		|||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                match position.piece_on_square(en_passant.capture_square()) {
 | 
			
		||||
                match board.piece_on_square(en_passant.capture_square()) {
 | 
			
		||||
                    Some(_) => Some(en_passant),
 | 
			
		||||
                    None => None,
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -173,10 +172,7 @@ impl PawnMoveGenerator {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::{
 | 
			
		||||
        assert_move_list, formatted_move_list, position::DiagramFormatter, test_position,
 | 
			
		||||
        testing::*,
 | 
			
		||||
    };
 | 
			
		||||
    use crate::{assert_move_list, formatted_move_list, test_position, testing::*};
 | 
			
		||||
    use chessfriend_core::{piece, Color, Square};
 | 
			
		||||
    use chessfriend_moves::{Builder as MoveBuilder, Move};
 | 
			
		||||
    use std::collections::HashSet;
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +181,8 @@ mod tests {
 | 
			
		|||
    fn one_double_push() -> TestResult {
 | 
			
		||||
        let pos = test_position![White Pawn on E2];
 | 
			
		||||
 | 
			
		||||
        let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            PawnMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        let pawn = piece!(White Pawn on E2);
 | 
			
		||||
        let expected_moves = HashSet::from_iter([
 | 
			
		||||
| 
						 | 
				
			
			@ -204,7 +201,8 @@ mod tests {
 | 
			
		|||
    fn one_single_push() -> TestResult {
 | 
			
		||||
        let pos = test_position![White Pawn on E3];
 | 
			
		||||
 | 
			
		||||
        let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            PawnMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generated_moves: HashSet<_> = generator.iter().collect();
 | 
			
		||||
 | 
			
		||||
        let expected_moves = HashSet::from_iter([MoveBuilder::push(&piece!(White Pawn on E3))
 | 
			
		||||
| 
						 | 
				
			
			@ -223,9 +221,8 @@ mod tests {
 | 
			
		|||
            White Knight on E4,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        println!("{}", DiagramFormatter::new(&pos));
 | 
			
		||||
 | 
			
		||||
        let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            PawnMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        let expected_moves = HashSet::from_iter([MoveBuilder::push(&piece!(White Pawn on E2))
 | 
			
		||||
            .to(Square::E3)
 | 
			
		||||
| 
						 | 
				
			
			@ -245,7 +242,8 @@ mod tests {
 | 
			
		|||
            White Knight on E3,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            PawnMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        let generated_moves: HashSet<_> = generator.iter().collect();
 | 
			
		||||
        let expected_moves: HashSet<_> = HashSet::new();
 | 
			
		||||
| 
						 | 
				
			
			@ -261,7 +259,8 @@ mod tests {
 | 
			
		|||
            Black Knight on D5,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            PawnMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        let expected_moves = HashSet::from_iter([MoveBuilder::push(&piece!(White Pawn on E4))
 | 
			
		||||
            .capturing_on(Square::D5)
 | 
			
		||||
| 
						 | 
				
			
			@ -283,7 +282,8 @@ mod tests {
 | 
			
		|||
            Black Queen on F5,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            PawnMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        let builder = MoveBuilder::push(&piece!(White Pawn on E4));
 | 
			
		||||
        let expected_moves = HashSet::from_iter([
 | 
			
		||||
| 
						 | 
				
			
			@ -309,7 +309,8 @@ mod tests {
 | 
			
		|||
            Black Pawn on E4,
 | 
			
		||||
        ], D3);
 | 
			
		||||
 | 
			
		||||
        let generator = PawnMoveGenerator::new(&pos, Color::Black, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generator =
 | 
			
		||||
            PawnMoveGenerator::new(&pos.board, Color::Black, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let generated_moves: HashSet<Move> = generator.iter().collect();
 | 
			
		||||
 | 
			
		||||
        let builder = MoveBuilder::push(&piece!(Black Pawn on E4));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
 | 
			
		||||
use crate::Position;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::Board;
 | 
			
		||||
use chessfriend_core::{Direction, PlacedPiece, Shape};
 | 
			
		||||
 | 
			
		||||
move_generator_declaration!(ClassicalMoveGenerator);
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ impl MoveGeneratorInternal for ClassicalMoveGenerator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn move_set_for_piece(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        placed_piece: &PlacedPiece,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
| 
						 | 
				
			
			@ -22,10 +22,10 @@ impl MoveGeneratorInternal for ClassicalMoveGenerator {
 | 
			
		|||
        let color = piece.color();
 | 
			
		||||
        let square = placed_piece.square();
 | 
			
		||||
 | 
			
		||||
        let blockers = position.occupied_squares();
 | 
			
		||||
        let blockers = board.occupied_squares();
 | 
			
		||||
        let empty_squares = !blockers;
 | 
			
		||||
        let friendly_pieces = position.bitboard_for_color(color);
 | 
			
		||||
        let opposing_pieces = position.bitboard_for_color(color.other());
 | 
			
		||||
        let friendly_pieces = board.bitboard_for_color(color);
 | 
			
		||||
        let opposing_pieces = board.bitboard_for_color(color.other());
 | 
			
		||||
 | 
			
		||||
        let mut all_moves = BitBoard::empty();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,22 +65,22 @@ impl MoveGeneratorInternal for ClassicalMoveGenerator {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::{position, position::DiagramFormatter};
 | 
			
		||||
    use crate::test_position;
 | 
			
		||||
    use chessfriend_bitboard::{bitboard, BitBoard};
 | 
			
		||||
    use chessfriend_core::Color;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn classical_single_queen_bitboard() {
 | 
			
		||||
        let pos = position![White Queen on B2];
 | 
			
		||||
        let pos = test_position![White Queen on B2];
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let bitboard = generator._test_bitboard();
 | 
			
		||||
        let expected = bitboard![
 | 
			
		||||
            A2, C2, D2, E2, F2, G2, H2, // Rank
 | 
			
		||||
            B1, B3, B4, B5, B6, B7, B8, // File
 | 
			
		||||
            A1, C3, D4, E5, F6, G7, H8, // Diagonal
 | 
			
		||||
            C1, A3 // Anti-diagonal
 | 
			
		||||
            A2 C2 D2 E2 F2 G2 H2 // Rank
 | 
			
		||||
            B1 B3 B4 B5 B6 B7 B8 // File
 | 
			
		||||
            A1 C3 D4 E5 F6 G7 H8 // Diagonal
 | 
			
		||||
            C1 A3 // Anti-diagonal
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
| 
						 | 
				
			
			@ -93,15 +93,13 @@ mod tests {
 | 
			
		|||
    /// Test that a rook can move up to, but not onto, a friendly piece.
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn classical_single_queen_with_same_color_blocker_bitboard() {
 | 
			
		||||
        let pos = position![
 | 
			
		||||
        let pos = test_position![
 | 
			
		||||
            White Queen on A1,
 | 
			
		||||
            White Knight on E1,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        println!("{}", DiagramFormatter::new(&pos));
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
        let bitboard = generator._test_bitboard();
 | 
			
		||||
        let expected = BitBoard::new(
 | 
			
		||||
            0b10000001_01000001_00100001_00010001_00001001_00000101_00000011_00001110,
 | 
			
		||||
| 
						 | 
				
			
			@ -117,41 +115,39 @@ mod tests {
 | 
			
		|||
    /// Test that a rook can move up to, and then capture, an enemy piece.
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn classical_single_queen_with_opposing_color_blocker_bitboard() {
 | 
			
		||||
        let pos = position![
 | 
			
		||||
        let pos = test_position![
 | 
			
		||||
            White Queen on B2,
 | 
			
		||||
            Black Knight on E5,
 | 
			
		||||
        ];
 | 
			
		||||
        println!("{}", DiagramFormatter::new(&pos));
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
            bitboard![
 | 
			
		||||
                A2, C2, D2, E2, F2, G2, H2, // Rank
 | 
			
		||||
                B1, B3, B4, B5, B6, B7, B8, // File
 | 
			
		||||
                A1, C3, D4, E5, // Diagonal
 | 
			
		||||
                C1, A3 // Anti-diagonal
 | 
			
		||||
                A2 C2 D2 E2 F2 G2 H2 // Rank
 | 
			
		||||
                B1 B3 B4 B5 B6 B7 B8 // File
 | 
			
		||||
                A1 C3 D4 E5 // Diagonal
 | 
			
		||||
                C1 A3 // Anti-diagonal
 | 
			
		||||
            ]
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn classical_single_queen_in_center() {
 | 
			
		||||
        let pos = position![White Queen on D3];
 | 
			
		||||
        println!("{}", DiagramFormatter::new(&pos));
 | 
			
		||||
        let pos = test_position![White Queen on D3];
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
            bitboard![
 | 
			
		||||
                A3, B3, C3, E3, F3, G3, H3, // Rank
 | 
			
		||||
                D1, D2, D4, D5, D6, D7, D8, // File
 | 
			
		||||
                B1, C2, E4, F5, G6, H7, // Diagonal
 | 
			
		||||
                F1, E2, C4, B5, A6, // Anti-diagonal
 | 
			
		||||
                A3 B3 C3 E3 F3 G3 H3 // Rank
 | 
			
		||||
                D1 D2 D4 D5 D6 D7 D8 // File
 | 
			
		||||
                B1 C2 E4 F5 G6 H7 // Diagonal
 | 
			
		||||
                F1 E2 C4 B5 A6 // Anti-diagonal
 | 
			
		||||
            ]
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
 | 
			
		||||
use crate::Position;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::Board;
 | 
			
		||||
use chessfriend_core::{Direction, PlacedPiece, Shape};
 | 
			
		||||
 | 
			
		||||
move_generator_declaration!(ClassicalMoveGenerator);
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ impl MoveGeneratorInternal for ClassicalMoveGenerator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn move_set_for_piece(
 | 
			
		||||
        position: &Position,
 | 
			
		||||
        board: &Board,
 | 
			
		||||
        placed_piece: &PlacedPiece,
 | 
			
		||||
        capture_mask: BitBoard,
 | 
			
		||||
        push_mask: BitBoard,
 | 
			
		||||
| 
						 | 
				
			
			@ -22,10 +22,10 @@ impl MoveGeneratorInternal for ClassicalMoveGenerator {
 | 
			
		|||
        let color = piece.color();
 | 
			
		||||
        let square = placed_piece.square();
 | 
			
		||||
 | 
			
		||||
        let blockers = position.occupied_squares();
 | 
			
		||||
        let blockers = board.occupied_squares();
 | 
			
		||||
        let empty_squares = !blockers;
 | 
			
		||||
        let friendly_pieces = position.bitboard_for_color(color);
 | 
			
		||||
        let opposing_pieces = position.bitboard_for_color(color.other());
 | 
			
		||||
        let friendly_pieces = board.bitboard_for_color(color);
 | 
			
		||||
        let opposing_pieces = board.bitboard_for_color(color.other());
 | 
			
		||||
 | 
			
		||||
        let mut all_moves = BitBoard::empty();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +61,7 @@ impl MoveGeneratorInternal for ClassicalMoveGenerator {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::{position::DiagramFormatter, test_position};
 | 
			
		||||
    use crate::test_position;
 | 
			
		||||
    use chessfriend_bitboard::{bitboard, BitBoard};
 | 
			
		||||
    use chessfriend_core::Color;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -70,11 +70,11 @@ mod tests {
 | 
			
		|||
        let pos = test_position![White Rook on A2];
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
            bitboard![A1, A3, A4, A5, A6, A7, A8, B2, C2, D2, E2, F2, G2, H2]
 | 
			
		||||
            bitboard![A1 A3 A4 A5 A6 A7 A8 B2 C2 D2 E2 F2 G2 H2]
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,10 +86,10 @@ mod tests {
 | 
			
		|||
            White Knight on E1,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        println!("{}", DiagramFormatter::new(&pos));
 | 
			
		||||
        println!("{}", pos.display());
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
| 
						 | 
				
			
			@ -108,11 +108,11 @@ mod tests {
 | 
			
		|||
        ];
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
            bitboard![A2, A3, A4, A5, A6, A7, A8, B1, C1, D1, E1]
 | 
			
		||||
            bitboard![A2 A3 A4 A5 A6 A7 A8 B1 C1 D1 E1]
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -121,11 +121,11 @@ mod tests {
 | 
			
		|||
        let pos = test_position![White Rook on D4];
 | 
			
		||||
 | 
			
		||||
        let generator =
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
            ClassicalMoveGenerator::new(&pos.board, Color::White, BitBoard::FULL, BitBoard::FULL);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            generator._test_bitboard(),
 | 
			
		||||
            bitboard![A4, B4, C4, E4, F4, G4, H4, D1, D2, D3, D5, D6, D7, D8]
 | 
			
		||||
            bitboard![A4 B4 C4 E4 F4 G4 H4 D1 D2 D3 D5 D6 D7 D8]
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,5 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
mod move_builder;
 | 
			
		||||
mod position_builder;
 | 
			
		||||
 | 
			
		||||
pub use move_builder::{Builder as MoveBuilder, MakeMoveError};
 | 
			
		||||
pub use position_builder::Builder as PositionBuilder;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,10 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use crate::{position::flags::Flags, Position};
 | 
			
		||||
use crate::Position;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::{castle, castle::Castle, en_passant::EnPassant};
 | 
			
		||||
use chessfriend_core::{Color, Direction, Piece, PlacedPiece, Shape, Square};
 | 
			
		||||
use chessfriend_moves::{Castle, EnPassant, Move};
 | 
			
		||||
use chessfriend_moves::Move;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 | 
			
		||||
pub enum MakeMoveError {
 | 
			
		||||
| 
						 | 
				
			
			@ -33,15 +34,15 @@ pub enum ValidatedMove {
 | 
			
		|||
        moving_piece: PlacedPiece,
 | 
			
		||||
        captured_piece: Option<PlacedPiece>,
 | 
			
		||||
        promotion: Option<Shape>,
 | 
			
		||||
        flags: Flags,
 | 
			
		||||
        castling_rights: castle::Rights,
 | 
			
		||||
        en_passant: Option<EnPassant>,
 | 
			
		||||
        increment_ply: bool,
 | 
			
		||||
        should_increment_ply: bool,
 | 
			
		||||
    },
 | 
			
		||||
    Castle {
 | 
			
		||||
        castle: Castle,
 | 
			
		||||
        king: PlacedPiece,
 | 
			
		||||
        rook: PlacedPiece,
 | 
			
		||||
        flags: Flags,
 | 
			
		||||
        castling_rights: castle::Rights,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +50,7 @@ impl MoveToMake for NoMove {}
 | 
			
		|||
impl MoveToMake for ValidatedMove {}
 | 
			
		||||
 | 
			
		||||
impl<'p> Builder<'p, NoMove> {
 | 
			
		||||
    pub fn new(position: &'p Position) -> Builder<'p, NoMove> {
 | 
			
		||||
    pub fn new(position: &'p Position) -> Self {
 | 
			
		||||
        Builder {
 | 
			
		||||
            position,
 | 
			
		||||
            move_to_make: NoMove,
 | 
			
		||||
| 
						 | 
				
			
			@ -66,6 +67,7 @@ where
 | 
			
		|||
 | 
			
		||||
        let piece = self
 | 
			
		||||
            .position
 | 
			
		||||
            .board
 | 
			
		||||
            .piece_on_square(origin_square)
 | 
			
		||||
            .ok_or(MakeMoveError::NoPiece)?;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -101,12 +103,14 @@ where
 | 
			
		|||
 | 
			
		||||
            Some(
 | 
			
		||||
                self.position
 | 
			
		||||
                    .board
 | 
			
		||||
                    .piece_on_square(capture_square)
 | 
			
		||||
                    .ok_or(MakeMoveError::NoCapturedPiece)?,
 | 
			
		||||
            )
 | 
			
		||||
        } else if mv.is_capture() {
 | 
			
		||||
            Some(
 | 
			
		||||
                self.position
 | 
			
		||||
                    .board
 | 
			
		||||
                    .piece_on_square(target_square)
 | 
			
		||||
                    .ok_or(MakeMoveError::NoCapturedPiece)?,
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			@ -117,15 +121,15 @@ where
 | 
			
		|||
        // TODO: Check whether the move is legal.
 | 
			
		||||
 | 
			
		||||
        let piece_is_king = piece.is_king();
 | 
			
		||||
        let mut flags = self.position.flags().clone();
 | 
			
		||||
        let mut castling_rights = self.position.board.castling_rights;
 | 
			
		||||
 | 
			
		||||
        if piece_is_king {
 | 
			
		||||
            flags.clear_player_has_right_to_castle_flag(player, Castle::KingSide);
 | 
			
		||||
            flags.clear_player_has_right_to_castle_flag(player, Castle::QueenSide);
 | 
			
		||||
            castling_rights.clear_player_has_right_to_castle_flag(player, Castle::KingSide);
 | 
			
		||||
            castling_rights.clear_player_has_right_to_castle_flag(player, Castle::QueenSide);
 | 
			
		||||
        } else if piece.is_kingside_rook() {
 | 
			
		||||
            flags.clear_player_has_right_to_castle_flag(player, Castle::KingSide);
 | 
			
		||||
            castling_rights.clear_player_has_right_to_castle_flag(player, Castle::KingSide);
 | 
			
		||||
        } else if piece.is_queenside_rook() {
 | 
			
		||||
            flags.clear_player_has_right_to_castle_flag(player, Castle::QueenSide);
 | 
			
		||||
            castling_rights.clear_player_has_right_to_castle_flag(player, Castle::QueenSide);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(castle) = mv.castle() {
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +149,7 @@ where
 | 
			
		|||
                    castle,
 | 
			
		||||
                    king: piece,
 | 
			
		||||
                    rook,
 | 
			
		||||
                    flags,
 | 
			
		||||
                    castling_rights,
 | 
			
		||||
                },
 | 
			
		||||
            })
 | 
			
		||||
        } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -167,9 +171,9 @@ where
 | 
			
		|||
                    moving_piece: piece,
 | 
			
		||||
                    captured_piece,
 | 
			
		||||
                    promotion: mv.promotion(),
 | 
			
		||||
                    flags,
 | 
			
		||||
                    castling_rights,
 | 
			
		||||
                    en_passant,
 | 
			
		||||
                    increment_ply: !(mv.is_capture() || piece.is_pawn()),
 | 
			
		||||
                    should_increment_ply: !(mv.is_capture() || piece.is_pawn()),
 | 
			
		||||
                },
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -180,8 +184,8 @@ impl<'p> Builder<'p, ValidatedMove> {
 | 
			
		|||
    pub fn build(&self) -> Position {
 | 
			
		||||
        let player = self.position.player_to_move();
 | 
			
		||||
 | 
			
		||||
        let updated_move_number =
 | 
			
		||||
            self.position.move_number() + if player == Color::Black { 1 } else { 0 };
 | 
			
		||||
        let updated_move_number = self.position.board.move_counter.fullmove_number
 | 
			
		||||
            + if player == Color::Black { 1 } else { 0 };
 | 
			
		||||
 | 
			
		||||
        match self.move_to_make {
 | 
			
		||||
            ValidatedMove::RegularMove {
 | 
			
		||||
| 
						 | 
				
			
			@ -190,69 +194,57 @@ impl<'p> Builder<'p, ValidatedMove> {
 | 
			
		|||
                moving_piece,
 | 
			
		||||
                captured_piece,
 | 
			
		||||
                promotion,
 | 
			
		||||
                flags,
 | 
			
		||||
                castling_rights,
 | 
			
		||||
                en_passant,
 | 
			
		||||
                increment_ply,
 | 
			
		||||
                should_increment_ply: increment_ply,
 | 
			
		||||
            } => {
 | 
			
		||||
                let mut pieces = self.position.piece_bitboards().clone();
 | 
			
		||||
                let mut board = self.position.board.clone();
 | 
			
		||||
 | 
			
		||||
                board.castling_rights = castling_rights;
 | 
			
		||||
                board.en_passant = en_passant;
 | 
			
		||||
 | 
			
		||||
                if let Some(captured_piece) = captured_piece {
 | 
			
		||||
                    pieces.remove_piece(&captured_piece);
 | 
			
		||||
                    board.remove_piece_from_square(captured_piece.square());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if let Some(promotion) = promotion {
 | 
			
		||||
                    pieces.remove_piece(&moving_piece);
 | 
			
		||||
                    let _ = pieces
 | 
			
		||||
                        .place_piece(&PlacedPiece::new(Piece::new(player, promotion), to_square));
 | 
			
		||||
                    board.remove_piece_from_square(moving_piece.square());
 | 
			
		||||
                    board.place_piece_on_square(Piece::new(player, promotion), to_square);
 | 
			
		||||
                } else {
 | 
			
		||||
                    pieces.move_piece(moving_piece.piece(), from_square, to_square);
 | 
			
		||||
                    board.remove_piece_from_square(from_square);
 | 
			
		||||
                    board.place_piece_on_square(moving_piece.piece(), to_square);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let ply = if increment_ply {
 | 
			
		||||
                    self.position.ply_counter() + 1
 | 
			
		||||
                    self.position.board.move_counter.halfmove_number + 1
 | 
			
		||||
                } else {
 | 
			
		||||
                    0
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                Position::new(
 | 
			
		||||
                    self.position.player_to_move().other(),
 | 
			
		||||
                    flags,
 | 
			
		||||
                    pieces,
 | 
			
		||||
                    en_passant,
 | 
			
		||||
                    ply,
 | 
			
		||||
                    updated_move_number,
 | 
			
		||||
                )
 | 
			
		||||
                Position::new(board)
 | 
			
		||||
            }
 | 
			
		||||
            ValidatedMove::Castle {
 | 
			
		||||
                castle,
 | 
			
		||||
                king,
 | 
			
		||||
                rook,
 | 
			
		||||
                flags,
 | 
			
		||||
                castling_rights,
 | 
			
		||||
            } => {
 | 
			
		||||
                let mut pieces = self.position.piece_bitboards().clone();
 | 
			
		||||
                let mut board = self.position.board.clone();
 | 
			
		||||
 | 
			
		||||
                board.castling_rights = castling_rights;
 | 
			
		||||
 | 
			
		||||
                let next_active_color = board.move_counter.active_color.next();
 | 
			
		||||
                board.move_counter.active_color = next_active_color;
 | 
			
		||||
 | 
			
		||||
                let parameters = castle.parameters(player);
 | 
			
		||||
 | 
			
		||||
                let king_origin_square: BitBoard = king.square().into();
 | 
			
		||||
                let king_target_square: BitBoard = parameters.king_target_square().into();
 | 
			
		||||
                *pieces.bitboard_for_piece_mut(king.piece()) ^=
 | 
			
		||||
                    king_origin_square | king_target_square;
 | 
			
		||||
                board.remove_piece_from_square(king.square());
 | 
			
		||||
                board.place_piece_on_square(king.piece(), parameters.king_target_square());
 | 
			
		||||
 | 
			
		||||
                let rook_from: BitBoard = rook.square().into();
 | 
			
		||||
                let rook_to: BitBoard = parameters.rook_target_square().into();
 | 
			
		||||
                *pieces.bitboard_for_piece_mut(rook.piece()) ^= rook_from | rook_to;
 | 
			
		||||
                board.remove_piece_from_square(rook.square());
 | 
			
		||||
                board.place_piece_on_square(rook.piece(), parameters.rook_target_square());
 | 
			
		||||
 | 
			
		||||
                *pieces.bitboard_for_color_mut(player) &=
 | 
			
		||||
                    !(king_origin_square | rook_from) | (king_target_square | rook_to);
 | 
			
		||||
 | 
			
		||||
                Position::new(
 | 
			
		||||
                    player.other(),
 | 
			
		||||
                    flags,
 | 
			
		||||
                    pieces,
 | 
			
		||||
                    None,
 | 
			
		||||
                    self.position.ply_counter() + 1,
 | 
			
		||||
                    updated_move_number,
 | 
			
		||||
                )
 | 
			
		||||
                Position::new(board)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +283,7 @@ mod tests {
 | 
			
		|||
        println!("{}", &new_position);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            new_position.piece_on_square(Square::E3),
 | 
			
		||||
            new_position.board.piece_on_square(Square::E3),
 | 
			
		||||
            Some(piece!(White Pawn on E3))
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -308,16 +300,13 @@ mod tests {
 | 
			
		|||
        println!("{}", &new_position);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            new_position.piece_on_square(Square::E4),
 | 
			
		||||
            new_position.board.piece_on_square(Square::E4),
 | 
			
		||||
            Some(piece!(White Pawn on E4))
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let en_passant = new_position.en_passant();
 | 
			
		||||
        assert!(en_passant.is_some());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            en_passant.as_ref().map(EnPassant::target_square),
 | 
			
		||||
            Some(Square::E3)
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(en_passant.map(EnPassant::target_square), Some(Square::E3));
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -339,11 +328,11 @@ mod tests {
 | 
			
		|||
        println!("{}", &new_position);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            new_position.piece_on_square(Square::G1),
 | 
			
		||||
            new_position.board.piece_on_square(Square::G1),
 | 
			
		||||
            Some(piece!(White King on G1))
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            new_position.piece_on_square(Square::F1),
 | 
			
		||||
            new_position.board.piece_on_square(Square::F1),
 | 
			
		||||
            Some(piece!(White Rook on F1))
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -366,11 +355,11 @@ mod tests {
 | 
			
		|||
        println!("{en_passant_position}");
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            en_passant_position.piece_on_square(Square::A5),
 | 
			
		||||
            en_passant_position.board.piece_on_square(Square::A5),
 | 
			
		||||
            Some(piece!(Black Pawn on A5))
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            en_passant_position.piece_on_square(Square::B5),
 | 
			
		||||
            en_passant_position.board.piece_on_square(Square::B5),
 | 
			
		||||
            Some(piece!(White Pawn on B5))
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -382,10 +371,10 @@ mod tests {
 | 
			
		|||
            .build();
 | 
			
		||||
        println!("{en_passant_capture}");
 | 
			
		||||
 | 
			
		||||
        assert_eq!(en_passant_capture.piece_on_square(Square::A5), None);
 | 
			
		||||
        assert_eq!(en_passant_capture.piece_on_square(Square::B5), None);
 | 
			
		||||
        assert_eq!(en_passant_capture.board.piece_on_square(Square::A5), None);
 | 
			
		||||
        assert_eq!(en_passant_capture.board.piece_on_square(Square::B5), None);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            en_passant_capture.piece_on_square(Square::A6),
 | 
			
		||||
            en_passant_capture.board.piece_on_square(Square::A6),
 | 
			
		||||
            Some(piece!(White Pawn on A6))
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,194 +0,0 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    position::{flags::Flags, piece_sets::PieceBitBoards},
 | 
			
		||||
    Position,
 | 
			
		||||
};
 | 
			
		||||
use chessfriend_core::{piece, Color, Piece, PlacedPiece, Rank, Shape, Square};
 | 
			
		||||
use chessfriend_moves::{Castle, EnPassant};
 | 
			
		||||
use std::collections::BTreeMap;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct Builder {
 | 
			
		||||
    player_to_move: Color,
 | 
			
		||||
    flags: Flags,
 | 
			
		||||
    pieces: BTreeMap<Square, Piece>,
 | 
			
		||||
    kings: [Option<Square>; 2],
 | 
			
		||||
    en_passant: Option<EnPassant>,
 | 
			
		||||
    ply_counter: u16,
 | 
			
		||||
    move_number: u16,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Builder {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self::default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn empty() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            player_to_move: Color::default(),
 | 
			
		||||
            flags: Flags::default(),
 | 
			
		||||
            pieces: BTreeMap::default(),
 | 
			
		||||
            kings: [None, None],
 | 
			
		||||
            en_passant: None,
 | 
			
		||||
            ply_counter: 0,
 | 
			
		||||
            move_number: 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn from_position(position: &Position) -> Self {
 | 
			
		||||
        let pieces = BTreeMap::from_iter(
 | 
			
		||||
            position
 | 
			
		||||
                .pieces(Color::White)
 | 
			
		||||
                .chain(position.pieces(Color::Black))
 | 
			
		||||
                .map(|placed_piece| (placed_piece.square(), *placed_piece.piece())),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let white_king = position.king_square(Color::White);
 | 
			
		||||
        let black_king = position.king_square(Color::Black);
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            player_to_move: position.player_to_move(),
 | 
			
		||||
            flags: position.flags(),
 | 
			
		||||
            pieces,
 | 
			
		||||
            kings: [Some(white_king), Some(black_king)],
 | 
			
		||||
            en_passant: position.en_passant(),
 | 
			
		||||
            ply_counter: position.ply_counter(),
 | 
			
		||||
            move_number: position.move_number(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_move(&mut self, player: Color) -> &mut Self {
 | 
			
		||||
        self.player_to_move = player;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn ply_counter(&mut self, num: u16) -> &mut Self {
 | 
			
		||||
        self.ply_counter = num;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn move_number(&mut self, num: u16) -> &mut Self {
 | 
			
		||||
        self.move_number = num;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn en_passant(&mut self, en_passant: Option<EnPassant>) -> &mut Self {
 | 
			
		||||
        self.en_passant = en_passant;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn place_piece(&mut self, piece: PlacedPiece) -> &mut Self {
 | 
			
		||||
        let square = piece.square();
 | 
			
		||||
        let shape = piece.shape();
 | 
			
		||||
 | 
			
		||||
        if shape == Shape::King {
 | 
			
		||||
            let color = piece.color();
 | 
			
		||||
            let color_index: usize = color as usize;
 | 
			
		||||
 | 
			
		||||
            if let Some(king_square) = self.kings[color_index] {
 | 
			
		||||
                self.pieces.remove(&king_square);
 | 
			
		||||
            }
 | 
			
		||||
            self.kings[color_index] = Some(square);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.pieces.insert(square, *piece.piece());
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn player_can_castle(&mut self, color: Color, castle: Castle) -> &mut Self {
 | 
			
		||||
        self.flags
 | 
			
		||||
            .set_player_has_right_to_castle_flag(color, castle);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn no_castling_rights(&mut self) -> &mut Self {
 | 
			
		||||
        self.flags.clear_all_castling_rights();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn build(&self) -> Position {
 | 
			
		||||
        let pieces = PieceBitBoards::from_iter(
 | 
			
		||||
            self.pieces
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(PlacedPiece::from)
 | 
			
		||||
                .filter(Self::is_piece_placement_valid),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let mut flags = self.flags;
 | 
			
		||||
 | 
			
		||||
        for color in Color::ALL {
 | 
			
		||||
            for castle in Castle::ALL {
 | 
			
		||||
                let parameters = castle.parameters(color);
 | 
			
		||||
                let has_rook_on_starting_square = self
 | 
			
		||||
                    .pieces
 | 
			
		||||
                    .get(¶meters.rook_origin_square())
 | 
			
		||||
                    .is_some_and(|piece| piece.shape() == Shape::Rook);
 | 
			
		||||
                let king_is_on_starting_square =
 | 
			
		||||
                    self.kings[color as usize] == Some(parameters.king_origin_square());
 | 
			
		||||
 | 
			
		||||
                if !king_is_on_starting_square || !has_rook_on_starting_square {
 | 
			
		||||
                    flags.clear_player_has_right_to_castle_flag(color, castle);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Position::new(
 | 
			
		||||
            self.player_to_move,
 | 
			
		||||
            flags,
 | 
			
		||||
            pieces,
 | 
			
		||||
            self.en_passant,
 | 
			
		||||
            self.ply_counter,
 | 
			
		||||
            self.move_number,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Builder {
 | 
			
		||||
    fn is_piece_placement_valid(piece: &PlacedPiece) -> bool {
 | 
			
		||||
        if piece.shape() == Shape::Pawn {
 | 
			
		||||
            // Pawns cannot be placed on the first (back) rank of their side,
 | 
			
		||||
            // and cannot be placed on the final rank without a promotion.
 | 
			
		||||
            let rank = piece.square().rank();
 | 
			
		||||
            return rank != Rank::ONE && rank != Rank::EIGHT;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Builder {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        let white_king_square = Square::E1;
 | 
			
		||||
        let black_king_square = Square::E8;
 | 
			
		||||
 | 
			
		||||
        let pieces = BTreeMap::from_iter([
 | 
			
		||||
            (white_king_square, piece!(White King)),
 | 
			
		||||
            (black_king_square, piece!(Black King)),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            player_to_move: Color::White,
 | 
			
		||||
            flags: Flags::default(),
 | 
			
		||||
            pieces: pieces,
 | 
			
		||||
            kings: [Some(white_king_square), Some(black_king_square)],
 | 
			
		||||
            en_passant: None,
 | 
			
		||||
            ply_counter: 0,
 | 
			
		||||
            move_number: 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::PositionBuilder;
 | 
			
		||||
    use chessfriend_core::piece;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn place_piece() {
 | 
			
		||||
        let piece = piece!(White Queen on E4);
 | 
			
		||||
        let builder = PositionBuilder::new().place_piece(piece).build();
 | 
			
		||||
        assert_eq!(builder.piece_on_square(piece.square()), Some(piece));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,68 +0,0 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use crate::Position;
 | 
			
		||||
use chessfriend_core::{File, Rank, Square};
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
pub struct DiagramFormatter<'a>(&'a Position);
 | 
			
		||||
 | 
			
		||||
impl<'a> DiagramFormatter<'a> {
 | 
			
		||||
    pub fn new(position: &'a Position) -> DiagramFormatter {
 | 
			
		||||
        DiagramFormatter(position)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> fmt::Display for DiagramFormatter<'a> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(f, "  ╔═════════════════╗\n")?;
 | 
			
		||||
 | 
			
		||||
        for rank in Rank::ALL.iter().rev() {
 | 
			
		||||
            write!(f, "{rank} ║ ")?;
 | 
			
		||||
 | 
			
		||||
            for file in File::ALL.iter() {
 | 
			
		||||
                let square = Square::from_file_rank(*file, *rank);
 | 
			
		||||
                match self.0.piece_on_square(square) {
 | 
			
		||||
                    Some(placed_piece) => write!(f, "{} ", placed_piece.piece())?,
 | 
			
		||||
                    None => write!(f, "· ")?,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            write!(f, "║\n")?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        write!(f, "  ╚═════════════════╝\n")?;
 | 
			
		||||
        write!(f, "    a b c d e f g h\n")?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::{position, Position};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[ignore]
 | 
			
		||||
    fn empty() {
 | 
			
		||||
        let pos = Position::empty();
 | 
			
		||||
        let diagram = DiagramFormatter(&pos);
 | 
			
		||||
        println!("{}", diagram);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[ignore]
 | 
			
		||||
    fn one_king() {
 | 
			
		||||
        let pos = position![Black King on H3];
 | 
			
		||||
        let diagram = DiagramFormatter(&pos);
 | 
			
		||||
        println!("{}", diagram);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[ignore]
 | 
			
		||||
    fn starting() {
 | 
			
		||||
        let pos = Position::starting();
 | 
			
		||||
        let diagram = DiagramFormatter(&pos);
 | 
			
		||||
        println!("{}", diagram);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,89 +0,0 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use chessfriend_core::Color;
 | 
			
		||||
use chessfriend_moves::Castle;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
 | 
			
		||||
pub struct Flags(u8);
 | 
			
		||||
 | 
			
		||||
impl Flags {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub(super) fn player_has_right_to_castle_flag_offset(color: Color, castle: Castle) -> usize {
 | 
			
		||||
        ((color as usize) << 1) + castle as usize
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn player_has_right_to_castle(&self, color: Color, castle: Castle) -> bool {
 | 
			
		||||
        (self.0 & (1 << Self::player_has_right_to_castle_flag_offset(color, castle))) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn set_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) {
 | 
			
		||||
        self.0 |= 1 << Self::player_has_right_to_castle_flag_offset(color, castle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn clear_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) {
 | 
			
		||||
        self.0 &= !(1 << Self::player_has_right_to_castle_flag_offset(color, castle));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn clear_all_castling_rights(&mut self) {
 | 
			
		||||
        self.0 &= 0b11111100;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for Flags {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(f, "Flags({:08b})", self.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Flags {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Flags(0b00001111)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn castle_flags() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Flags::player_has_right_to_castle_flag_offset(Color::White, Castle::KingSide),
 | 
			
		||||
            0
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Flags::player_has_right_to_castle_flag_offset(Color::White, Castle::QueenSide),
 | 
			
		||||
            1
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Flags::player_has_right_to_castle_flag_offset(Color::Black, Castle::KingSide),
 | 
			
		||||
            2
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Flags::player_has_right_to_castle_flag_offset(Color::Black, Castle::QueenSide),
 | 
			
		||||
            3
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn defaults() {
 | 
			
		||||
        let mut flags: Flags = Default::default();
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide));
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::White, Castle::QueenSide));
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide));
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide));
 | 
			
		||||
 | 
			
		||||
        flags.clear_player_has_right_to_castle_flag(Color::White, Castle::QueenSide);
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide));
 | 
			
		||||
        assert!(!flags.player_has_right_to_castle(Color::White, Castle::QueenSide));
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide));
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide));
 | 
			
		||||
 | 
			
		||||
        flags.set_player_has_right_to_castle_flag(Color::White, Castle::QueenSide);
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide));
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::White, Castle::QueenSide));
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide));
 | 
			
		||||
        assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +1,9 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
pub mod piece_sets;
 | 
			
		||||
 | 
			
		||||
mod builders;
 | 
			
		||||
mod diagram_formatter;
 | 
			
		||||
mod flags;
 | 
			
		||||
mod pieces;
 | 
			
		||||
mod position;
 | 
			
		||||
 | 
			
		||||
pub use {
 | 
			
		||||
    builders::{MakeMoveError, MoveBuilder, PositionBuilder},
 | 
			
		||||
    diagram_formatter::DiagramFormatter,
 | 
			
		||||
    pieces::Pieces,
 | 
			
		||||
    builders::{MakeMoveError, MoveBuilder},
 | 
			
		||||
    position::Position,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,174 +0,0 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_core::{Color, Piece, PlacedPiece, Square};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Eq, PartialEq)]
 | 
			
		||||
pub enum PlacePieceStrategy {
 | 
			
		||||
    Replace,
 | 
			
		||||
    PreserveExisting,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Eq, PartialEq)]
 | 
			
		||||
pub enum PlacePieceError {
 | 
			
		||||
    ExisitingPiece,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PlacePieceStrategy {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::Replace
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
 | 
			
		||||
pub(crate) struct PieceBitBoards {
 | 
			
		||||
    by_color: ByColor,
 | 
			
		||||
    by_color_and_shape: ByColorAndShape,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
 | 
			
		||||
struct ByColor(BitBoard, [BitBoard; 2]);
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
 | 
			
		||||
struct ByColorAndShape([[BitBoard; 6]; 2]);
 | 
			
		||||
 | 
			
		||||
impl PieceBitBoards {
 | 
			
		||||
    pub(super) fn new(pieces: [[BitBoard; 6]; 2]) -> Self {
 | 
			
		||||
        use std::ops::BitOr;
 | 
			
		||||
 | 
			
		||||
        let white_pieces = pieces[Color::White as usize]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .fold(BitBoard::empty(), BitOr::bitor);
 | 
			
		||||
        let black_pieces = pieces[Color::Black as usize]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .fold(BitBoard::empty(), BitOr::bitor);
 | 
			
		||||
 | 
			
		||||
        let all_pieces = white_pieces | black_pieces;
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            by_color: ByColor(all_pieces, [white_pieces, black_pieces]),
 | 
			
		||||
            by_color_and_shape: ByColorAndShape(pieces),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A BitBoard representing all the pieces currently on the board. Other
 | 
			
		||||
    /// engines might refer to this concept as 'occupancy'.
 | 
			
		||||
    pub(crate) fn all_pieces(&self) -> &BitBoard {
 | 
			
		||||
        self.by_color.all()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn all_pieces_of_color(&self, color: Color) -> &BitBoard {
 | 
			
		||||
        self.by_color.bitboard(color)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn bitboard_for_color(&self, color: Color) -> &BitBoard {
 | 
			
		||||
        self.by_color.bitboard(color)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn bitboard_for_color_mut(&mut self, color: Color) -> &mut BitBoard {
 | 
			
		||||
        self.by_color.bitboard_mut(color)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn bitboard_for_piece(&self, piece: &Piece) -> &BitBoard {
 | 
			
		||||
        self.by_color_and_shape.bitboard_for_piece(piece)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn bitboard_for_piece_mut(&mut self, piece: &Piece) -> &mut BitBoard {
 | 
			
		||||
        self.by_color_and_shape.bitboard_for_piece_mut(piece)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn place_piece(&mut self, piece: &PlacedPiece) -> Result<(), PlacePieceError> {
 | 
			
		||||
        self.place_piece_with_strategy(piece, PlacePieceStrategy::default())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn place_piece_with_strategy(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        piece: &PlacedPiece,
 | 
			
		||||
        strategy: PlacePieceStrategy,
 | 
			
		||||
    ) -> Result<(), PlacePieceError> {
 | 
			
		||||
        let color = piece.color();
 | 
			
		||||
        let square = piece.square();
 | 
			
		||||
 | 
			
		||||
        if strategy == PlacePieceStrategy::PreserveExisting
 | 
			
		||||
            && self.by_color.bitboard(color).is_set(piece.square())
 | 
			
		||||
        {
 | 
			
		||||
            return Err(PlacePieceError::ExisitingPiece);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.by_color_and_shape.set_square(square, piece.piece());
 | 
			
		||||
        self.by_color.set_square(square, color);
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn remove_piece(&mut self, piece: &PlacedPiece) {
 | 
			
		||||
        let color = piece.color();
 | 
			
		||||
        let square = piece.square();
 | 
			
		||||
 | 
			
		||||
        self.by_color_and_shape.clear_square(square, piece.piece());
 | 
			
		||||
        self.by_color.clear_square(square, color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn move_piece(&mut self, piece: &Piece, from_square: Square, to_square: Square) {
 | 
			
		||||
        let color = piece.color();
 | 
			
		||||
 | 
			
		||||
        self.by_color_and_shape.clear_square(from_square, piece);
 | 
			
		||||
        self.by_color.clear_square(from_square, color);
 | 
			
		||||
        self.by_color_and_shape.set_square(to_square, piece);
 | 
			
		||||
        self.by_color.set_square(to_square, color);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FromIterator<PlacedPiece> for PieceBitBoards {
 | 
			
		||||
    fn from_iter<T: IntoIterator<Item = PlacedPiece>>(iter: T) -> Self {
 | 
			
		||||
        let mut pieces: Self = Default::default();
 | 
			
		||||
 | 
			
		||||
        for piece in iter {
 | 
			
		||||
            let _ = pieces.place_piece(&piece);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pieces
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ByColor {
 | 
			
		||||
    fn all(&self) -> &BitBoard {
 | 
			
		||||
        &self.0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn bitboard(&self, color: Color) -> &BitBoard {
 | 
			
		||||
        &self.1[color as usize]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn bitboard_mut(&mut self, color: Color) -> &mut BitBoard {
 | 
			
		||||
        &mut self.1[color as usize]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_square(&mut self, square: Square, color: Color) {
 | 
			
		||||
        self.0.set_square(square);
 | 
			
		||||
        self.1[color as usize].set_square(square)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn clear_square(&mut self, square: Square, color: Color) {
 | 
			
		||||
        self.0.clear_square(square);
 | 
			
		||||
        self.1[color as usize].clear_square(square);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ByColorAndShape {
 | 
			
		||||
    fn bitboard_for_piece(&self, piece: &Piece) -> &BitBoard {
 | 
			
		||||
        &self.0[piece.color() as usize][piece.shape() as usize]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bitboard_for_piece_mut(&mut self, piece: &Piece) -> &mut BitBoard {
 | 
			
		||||
        &mut self.0[piece.color() as usize][piece.shape() as usize]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_square(&mut self, square: Square, piece: &Piece) {
 | 
			
		||||
        self.bitboard_for_piece_mut(piece).set_square(square);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn clear_square(&mut self, square: Square, piece: &Piece) {
 | 
			
		||||
        self.bitboard_for_piece_mut(piece).clear_square(square);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,130 +0,0 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use super::Position;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
 | 
			
		||||
 | 
			
		||||
pub struct Pieces<'a> {
 | 
			
		||||
    color: Color,
 | 
			
		||||
    position: &'a Position,
 | 
			
		||||
 | 
			
		||||
    current_shape: Option<Shape>,
 | 
			
		||||
 | 
			
		||||
    shape_iterator: Box<dyn Iterator<Item = &'static Shape>>,
 | 
			
		||||
    square_iterator: Option<Box<dyn Iterator<Item = Square>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Pieces<'a> {
 | 
			
		||||
    pub(crate) fn new(position: &Position, color: Color) -> Pieces {
 | 
			
		||||
        Pieces {
 | 
			
		||||
            color,
 | 
			
		||||
            position,
 | 
			
		||||
            current_shape: None,
 | 
			
		||||
            shape_iterator: Box::new(Shape::iter()),
 | 
			
		||||
            square_iterator: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Iterator for Pieces<'a> {
 | 
			
		||||
    type Item = PlacedPiece;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        if let Some(square_iterator) = &mut self.square_iterator {
 | 
			
		||||
            if let (Some(square), Some(shape)) = (square_iterator.next(), self.current_shape) {
 | 
			
		||||
                return Some(PlacedPiece::new(Piece::new(self.color, shape), square));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut current_shape: Option<Shape> = None;
 | 
			
		||||
        let mut next_nonempty_bitboard: Option<&BitBoard> = None;
 | 
			
		||||
 | 
			
		||||
        while let Some(shape) = self.shape_iterator.next() {
 | 
			
		||||
            let piece = Piece::new(self.color, *shape);
 | 
			
		||||
 | 
			
		||||
            let bitboard = self.position.bitboard_for_piece(piece);
 | 
			
		||||
            if bitboard.is_empty() {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            next_nonempty_bitboard = Some(bitboard);
 | 
			
		||||
            current_shape = Some(*shape);
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let (Some(bitboard), Some(shape)) = (next_nonempty_bitboard, current_shape) {
 | 
			
		||||
            let mut square_iterator = bitboard.occupied_squares();
 | 
			
		||||
 | 
			
		||||
            let mut next_placed_piece: Option<PlacedPiece> = None;
 | 
			
		||||
            if let Some(square) = square_iterator.next() {
 | 
			
		||||
                next_placed_piece = Some(PlacedPiece::new(Piece::new(self.color, shape), square));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self.square_iterator = Some(Box::new(square_iterator));
 | 
			
		||||
            self.current_shape = Some(shape);
 | 
			
		||||
 | 
			
		||||
            return next_placed_piece;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::{Position, PositionBuilder};
 | 
			
		||||
    use chessfriend_core::{piece, Color};
 | 
			
		||||
    use std::collections::HashSet;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn empty() {
 | 
			
		||||
        let pos = Position::empty();
 | 
			
		||||
        let mut pieces = pos.pieces(Color::White);
 | 
			
		||||
        assert_eq!(pieces.next(), None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn one() {
 | 
			
		||||
        let pos = PositionBuilder::new()
 | 
			
		||||
            .place_piece(piece!(White Queen on E4))
 | 
			
		||||
            .build();
 | 
			
		||||
        println!("{:#?}", &pos);
 | 
			
		||||
 | 
			
		||||
        let mut pieces = pos.pieces(Color::White);
 | 
			
		||||
        assert_eq!(pieces.next(), Some(piece!(White Queen on E4)));
 | 
			
		||||
        assert_eq!(pieces.next(), Some(piece!(White King on E1)));
 | 
			
		||||
        assert_eq!(pieces.next(), None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn multiple_pieces() {
 | 
			
		||||
        let pos = PositionBuilder::new()
 | 
			
		||||
            .place_piece(piece!(White Queen on E4))
 | 
			
		||||
            .place_piece(piece!(White King on A1))
 | 
			
		||||
            .place_piece(piece!(White Pawn on B2))
 | 
			
		||||
            .place_piece(piece!(White Pawn on C2))
 | 
			
		||||
            .build();
 | 
			
		||||
        println!("{}", crate::position::DiagramFormatter::new(&pos));
 | 
			
		||||
 | 
			
		||||
        let expected_placed_pieces = HashSet::from([
 | 
			
		||||
            piece!(White Queen on E4),
 | 
			
		||||
            piece!(White King on A1),
 | 
			
		||||
            piece!(White Pawn on B2),
 | 
			
		||||
            piece!(White Pawn on C2),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        let placed_pieces = HashSet::from_iter(pos.pieces(Color::White));
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            placed_pieces,
 | 
			
		||||
            expected_placed_pieces,
 | 
			
		||||
            "{:#?}",
 | 
			
		||||
            placed_pieces
 | 
			
		||||
                .symmetric_difference(&expected_placed_pieces)
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|pp| format!("{}", pp))
 | 
			
		||||
                .collect::<Vec<String>>()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,112 +1,45 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use super::{flags::Flags, piece_sets::PieceBitBoards, Pieces};
 | 
			
		||||
use crate::{
 | 
			
		||||
    check::CheckingPieces,
 | 
			
		||||
    move_generator::{MoveSet, Moves},
 | 
			
		||||
    position::DiagramFormatter,
 | 
			
		||||
    sight::SightExt,
 | 
			
		||||
};
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_board::{castle::Castle, display::DiagramFormatter, en_passant::EnPassant, Board};
 | 
			
		||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
 | 
			
		||||
use chessfriend_moves::{Castle, EnPassant};
 | 
			
		||||
use chessfriend_moves::Move;
 | 
			
		||||
use std::{cell::OnceCell, fmt};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Eq)]
 | 
			
		||||
pub struct Position {
 | 
			
		||||
    color_to_move: Color,
 | 
			
		||||
    flags: Flags,
 | 
			
		||||
    pieces: PieceBitBoards,
 | 
			
		||||
    en_passant: Option<EnPassant>,
 | 
			
		||||
    pub board: Board,
 | 
			
		||||
    moves: OnceCell<Moves>,
 | 
			
		||||
    half_move_counter: u16,
 | 
			
		||||
    full_move_number: u16,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Position {
 | 
			
		||||
    pub fn empty() -> Position {
 | 
			
		||||
    pub fn empty() -> Self {
 | 
			
		||||
        Default::default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return a starting position.
 | 
			
		||||
    pub fn starting() -> Self {
 | 
			
		||||
        const BLACK_PIECES: [BitBoard; 6] = [
 | 
			
		||||
            BitBoard::new(0b0000000011111111 << 48),
 | 
			
		||||
            BitBoard::new(0b0100001000000000 << 48),
 | 
			
		||||
            BitBoard::new(0b0010010000000000 << 48),
 | 
			
		||||
            BitBoard::new(0b1000000100000000 << 48),
 | 
			
		||||
            BitBoard::new(0b0000100000000000 << 48),
 | 
			
		||||
            BitBoard::new(0b0001000000000000 << 48),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        const WHITE_PIECES: [BitBoard; 6] = [
 | 
			
		||||
            BitBoard::new(0b1111111100000000),
 | 
			
		||||
            BitBoard::new(0b0000000001000010),
 | 
			
		||||
            BitBoard::new(0b0000000000100100),
 | 
			
		||||
            BitBoard::new(0b0000000010000001),
 | 
			
		||||
            BitBoard::new(0b0000000000001000),
 | 
			
		||||
            BitBoard::new(0b0000000000010000),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            color_to_move: Color::White,
 | 
			
		||||
            pieces: PieceBitBoards::new([WHITE_PIECES, BLACK_PIECES]),
 | 
			
		||||
            board: Board::starting(),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn player_to_move(&self) -> Color {
 | 
			
		||||
        self.color_to_move
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn move_number(&self) -> u16 {
 | 
			
		||||
        self.full_move_number
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn ply_counter(&self) -> u16 {
 | 
			
		||||
        self.half_move_counter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the player has the right to castle on the given side of
 | 
			
		||||
    /// the board.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The right to castle on a particular side of the board is retained as
 | 
			
		||||
    /// long as the player has not moved their king, or the rook on that side of
 | 
			
		||||
    /// the board.
 | 
			
		||||
    pub(crate) fn player_has_right_to_castle(&self, color: Color, castle: Castle) -> bool {
 | 
			
		||||
        self.flags.player_has_right_to_castle(color, castle)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if the player is able to castle on the given side of the board.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The following requirements must be met:
 | 
			
		||||
    ///
 | 
			
		||||
    /// 1. The player must still have the right to castle on that side of the
 | 
			
		||||
    ///    board. The king and rook involved in the castle must not have moved.
 | 
			
		||||
    /// 1. The spaces between the king and rook must be clear
 | 
			
		||||
    /// 2. The king must not be in check
 | 
			
		||||
    /// 3. In the course of castling on that side, the king must not pass
 | 
			
		||||
    ///    through a square that an enemy piece can see
 | 
			
		||||
    pub(crate) fn player_can_castle(&self, player: Color, castle: Castle) -> bool {
 | 
			
		||||
        if !self.player_has_right_to_castle(player, castle.into()) {
 | 
			
		||||
            return false;
 | 
			
		||||
    pub fn new(board: Board) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            board,
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let castling_parameters = castle.parameters(player);
 | 
			
		||||
 | 
			
		||||
        let all_pieces = self.occupied_squares();
 | 
			
		||||
        if !(all_pieces & castling_parameters.clear_squares()).is_empty() {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let danger_squares = self.king_danger(player);
 | 
			
		||||
        if !(danger_squares & castling_parameters.check_squares()).is_empty() {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
impl Position {
 | 
			
		||||
    /// Return a PlacedPiece representing the rook to use for a castling move.
 | 
			
		||||
    pub(crate) fn rook_for_castle(&self, player: Color, castle: Castle) -> Option<PlacedPiece> {
 | 
			
		||||
        let square = match (player, castle) {
 | 
			
		||||
| 
						 | 
				
			
			@ -116,103 +49,68 @@ impl Position {
 | 
			
		|||
            (Color::Black, Castle::QueenSide) => Square::A8,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.piece_on_square(square)
 | 
			
		||||
        self.board.piece_on_square(square)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn moves(&self) -> &Moves {
 | 
			
		||||
        self.moves.get_or_init(|| {
 | 
			
		||||
            let player_to_move = self.player_to_move();
 | 
			
		||||
            let checking_pieces = self.checking_pieces();
 | 
			
		||||
            match checking_pieces.count() {
 | 
			
		||||
                // Normal, unrestricted move generation
 | 
			
		||||
                0 => Moves::new(self, self.color_to_move, BitBoard::FULL, BitBoard::FULL),
 | 
			
		||||
                0 => Moves::new(
 | 
			
		||||
                    &self.board,
 | 
			
		||||
                    player_to_move,
 | 
			
		||||
                    BitBoard::full(),
 | 
			
		||||
                    BitBoard::full(),
 | 
			
		||||
                ),
 | 
			
		||||
                1 => {
 | 
			
		||||
                    // Calculate push and capture masks for checking piece. Moves are restricted to those that intersect those masks.
 | 
			
		||||
                    // Calculate push and capture masks for checking piece. Moves are restricted to
 | 
			
		||||
                    // those that intersect those masks.
 | 
			
		||||
                    let capture_mask = checking_pieces.capture_mask();
 | 
			
		||||
                    let push_mask =
 | 
			
		||||
                        checking_pieces.push_mask(self.king_bitboard(self.color_to_move));
 | 
			
		||||
                    Moves::new(self, self.color_to_move, capture_mask, push_mask)
 | 
			
		||||
                    let push_mask = checking_pieces.push_mask(self.king_bitboard(player_to_move));
 | 
			
		||||
                    Moves::new(&self.board, player_to_move, capture_mask, push_mask)
 | 
			
		||||
                }
 | 
			
		||||
                // With more than one checking piece, the only legal moves are king moves.
 | 
			
		||||
                _ => Moves::new(self, self.color_to_move, BitBoard::EMPTY, BitBoard::EMPTY),
 | 
			
		||||
                _ => Moves::new(
 | 
			
		||||
                    &self.board,
 | 
			
		||||
                    player_to_move,
 | 
			
		||||
                    BitBoard::empty(),
 | 
			
		||||
                    BitBoard::empty(),
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A [BitBoard] representing the set of squares containing a piece.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub(crate) fn occupied_squares(&self) -> &BitBoard {
 | 
			
		||||
        &self.pieces.all_pieces()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub(crate) fn friendly_pieces(&self) -> &BitBoard {
 | 
			
		||||
        self.pieces.all_pieces_of_color(self.color_to_move)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub(crate) fn opposing_pieces(&self) -> &BitBoard {
 | 
			
		||||
        self.pieces.all_pieces_of_color(self.color_to_move.other())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub(crate) fn all_pieces(&self) -> (&BitBoard, &BitBoard) {
 | 
			
		||||
        (self.friendly_pieces(), self.opposing_pieces())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return a BitBoard representing the set of squares containing a piece.
 | 
			
		||||
    /// This set is the inverse of `occupied_squares`.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub(crate) fn empty_squares(&self) -> BitBoard {
 | 
			
		||||
        !self.occupied_squares()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn piece_on_square(&self, sq: Square) -> Option<PlacedPiece> {
 | 
			
		||||
        for color in Color::iter() {
 | 
			
		||||
            for shape in Shape::iter() {
 | 
			
		||||
                let piece = Piece::new(*color, *shape);
 | 
			
		||||
                if self.pieces.bitboard_for_piece(&piece).is_set(sq) {
 | 
			
		||||
                    return Some(PlacedPiece::new(piece, sq));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn pieces(&self, color: Color) -> Pieces {
 | 
			
		||||
        Pieces::new(&self, color)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn has_en_passant_square(&self) -> bool {
 | 
			
		||||
        self.en_passant.is_some()
 | 
			
		||||
        self.board.en_passant().is_some()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn en_passant(&self) -> Option<EnPassant> {
 | 
			
		||||
        self.en_passant
 | 
			
		||||
        self.board.en_passant()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn _sight_of_player(&self, player: Color, pieces: &PieceBitBoards) -> BitBoard {
 | 
			
		||||
        let en_passant_target_square = self.en_passant.map(|ep| ep.target_square());
 | 
			
		||||
    fn _en_passant_target_square(&self) -> Option<Square> {
 | 
			
		||||
        self.board.en_passant().map(EnPassant::target_square)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn _sight_of_player(&self, player: Color, board: &Board) -> BitBoard {
 | 
			
		||||
        let en_passant_target_square = self._en_passant_target_square();
 | 
			
		||||
 | 
			
		||||
        Shape::ALL
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter_map(|&shape| {
 | 
			
		||||
                let piece = Piece::new(player, shape);
 | 
			
		||||
                let bitboard = pieces.bitboard_for_piece(&piece);
 | 
			
		||||
                let bitboard = board.bitboard_for_piece(piece);
 | 
			
		||||
                if !bitboard.is_empty() {
 | 
			
		||||
                    Some((piece, bitboard))
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .flat_map(|(piece, &bitboard)| {
 | 
			
		||||
            .flat_map(|(piece, bitboard)| {
 | 
			
		||||
                bitboard.occupied_squares().map(move |square| {
 | 
			
		||||
                    PlacedPiece::new(piece, square).sight(pieces, en_passant_target_square)
 | 
			
		||||
                    PlacedPiece::new(piece, square).sight(board, en_passant_target_square)
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
            .fold(BitBoard::empty(), |acc, sight| acc | sight)
 | 
			
		||||
| 
						 | 
				
			
			@ -224,52 +122,37 @@ impl Position {
 | 
			
		|||
 | 
			
		||||
    #[cfg(test)]
 | 
			
		||||
    pub(crate) fn sight_of_piece(&self, piece: &PlacedPiece) -> BitBoard {
 | 
			
		||||
        piece.sight(&self.pieces, self.en_passant.map(|ep| ep.target_square()))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A bitboard representing the squares where a king of the given color will
 | 
			
		||||
    /// be in danger of being captured by the opposing player. If the king is on
 | 
			
		||||
    /// one of these squares, it is in check. The king cannot move to these
 | 
			
		||||
    /// squares.
 | 
			
		||||
    pub(crate) fn king_danger(&self, color: Color) -> BitBoard {
 | 
			
		||||
        let pieces_without_king = {
 | 
			
		||||
            let mut cloned_pieces = self.pieces.clone();
 | 
			
		||||
            let placed_king = PlacedPiece::new(Piece::king(color), self.king_square(color));
 | 
			
		||||
            cloned_pieces.remove_piece(&placed_king);
 | 
			
		||||
 | 
			
		||||
            cloned_pieces
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self._sight_of_player(color.other(), &pieces_without_king)
 | 
			
		||||
        piece.sight(&self.board, self._en_passant_target_square())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(test)]
 | 
			
		||||
    pub(crate) fn is_king_in_check(&self) -> bool {
 | 
			
		||||
        let danger_squares = self.king_danger(self.color_to_move);
 | 
			
		||||
        !(danger_squares & self.king_bitboard(self.color_to_move)).is_empty()
 | 
			
		||||
        let danger_squares = self.king_danger(self.player_to_move());
 | 
			
		||||
        !(danger_squares & self.king_bitboard(self.player_to_move())).is_empty()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn king_bitboard(&self, player: Color) -> &BitBoard {
 | 
			
		||||
        self.pieces.bitboard_for_piece(&Piece::king(player))
 | 
			
		||||
    fn king_bitboard(&self, player: Color) -> BitBoard {
 | 
			
		||||
        self.board.pieces.bitboard_for_piece(Piece::king(player))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn king_square(&self, player: Color) -> Square {
 | 
			
		||||
        self.king_bitboard(player)
 | 
			
		||||
            .occupied_squares()
 | 
			
		||||
            .occupied_squares(&IterationDirection::default())
 | 
			
		||||
            .next()
 | 
			
		||||
            .unwrap()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn checking_pieces(&self) -> CheckingPieces {
 | 
			
		||||
        let opponent = self.color_to_move.other();
 | 
			
		||||
        let king_square = self.king_square(self.color_to_move);
 | 
			
		||||
        let opponent = self.player_to_move().other();
 | 
			
		||||
        let king_square = self.king_square(self.player_to_move());
 | 
			
		||||
 | 
			
		||||
        let checking_pawns = {
 | 
			
		||||
            // The current player's pawn attack moves *from* this square are the
 | 
			
		||||
            // same as the pawn moves for the opposing player attacking this square.
 | 
			
		||||
            let pawn_moves_to_king_square = BitBoard::pawn_attacks(king_square, self.color_to_move);
 | 
			
		||||
            let pawn_moves_to_king_square =
 | 
			
		||||
                BitBoard::pawn_attacks(king_square, self.player_to_move());
 | 
			
		||||
            let opposing_pawn = Piece::pawn(opponent);
 | 
			
		||||
            let opposing_pawns = self.pieces.bitboard_for_piece(&opposing_pawn);
 | 
			
		||||
            let opposing_pawns = self.board.bitboard_for_piece(opposing_pawn);
 | 
			
		||||
 | 
			
		||||
            pawn_moves_to_king_square & opposing_pawns
 | 
			
		||||
        };
 | 
			
		||||
| 
						 | 
				
			
			@ -278,7 +161,7 @@ impl Position {
 | 
			
		|||
            ($moves_bb_fn:path, $piece_fn:ident) => {{
 | 
			
		||||
                let moves_from_opposing_square = $moves_bb_fn(king_square);
 | 
			
		||||
                let piece = Piece::$piece_fn(opponent);
 | 
			
		||||
                let opposing_pieces = self.pieces.bitboard_for_piece(&piece);
 | 
			
		||||
                let opposing_pieces = self.board.bitboard_for_piece(piece);
 | 
			
		||||
 | 
			
		||||
                moves_from_opposing_square & opposing_pieces
 | 
			
		||||
            }};
 | 
			
		||||
| 
						 | 
				
			
			@ -298,88 +181,49 @@ impl Position {
 | 
			
		|||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// crate::position methods
 | 
			
		||||
impl Position {
 | 
			
		||||
    pub(super) fn new(
 | 
			
		||||
        player_to_move: Color,
 | 
			
		||||
        flags: Flags,
 | 
			
		||||
        pieces: PieceBitBoards,
 | 
			
		||||
        en_passant: Option<EnPassant>,
 | 
			
		||||
        half_move_counter: u16,
 | 
			
		||||
        full_move_number: u16,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            color_to_move: player_to_move,
 | 
			
		||||
            flags,
 | 
			
		||||
            en_passant,
 | 
			
		||||
            pieces,
 | 
			
		||||
            half_move_counter,
 | 
			
		||||
            full_move_number,
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn flags(&self) -> Flags {
 | 
			
		||||
        self.flags
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn piece_bitboards(&self) -> &PieceBitBoards {
 | 
			
		||||
        &self.pieces
 | 
			
		||||
    pub fn display(&self) -> DiagramFormatter {
 | 
			
		||||
        self.board.display()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// crate methods
 | 
			
		||||
impl Position {
 | 
			
		||||
    pub(crate) fn bitboard_for_color(&self, color: Color) -> &BitBoard {
 | 
			
		||||
        self.pieces.bitboard_for_color(color)
 | 
			
		||||
    pub fn make_move(&mut self, ply: &Move) -> Result<(), MakeMoveError> {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn bitboard_for_piece(&self, piece: Piece) -> &BitBoard {
 | 
			
		||||
        self.pieces.bitboard_for_piece(&piece)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
impl Position {
 | 
			
		||||
    pub(crate) fn test_set_en_passant(&mut self, en_passant: EnPassant) {
 | 
			
		||||
        self.en_passant = Some(en_passant);
 | 
			
		||||
    pub fn unmake_move(&mut self, ply: &Move) -> Result<(), UnmakeMoveError> {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Position {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            color_to_move: Color::White,
 | 
			
		||||
            flags: Flags::default(),
 | 
			
		||||
            pieces: PieceBitBoards::default(),
 | 
			
		||||
            en_passant: None,
 | 
			
		||||
            board: Board::default(),
 | 
			
		||||
            moves: OnceCell::new(),
 | 
			
		||||
            half_move_counter: 0,
 | 
			
		||||
            full_move_number: 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialEq for Position {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.pieces == other.pieces
 | 
			
		||||
            && self.color_to_move == other.color_to_move
 | 
			
		||||
            && self.flags == other.flags
 | 
			
		||||
            && self.en_passant == other.en_passant
 | 
			
		||||
        self.board == other.board
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Position {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(f, "{}", DiagramFormatter::new(self))
 | 
			
		||||
        write!(f, "{}", self.board.display())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::{assert_eq_bitboards, position, test_position, Position, PositionBuilder};
 | 
			
		||||
    use crate::{assert_eq_bitboards, position, test_position, Position};
 | 
			
		||||
    use chessfriend_bitboard::bitboard;
 | 
			
		||||
    use chessfriend_core::piece;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -389,7 +233,7 @@ mod tests {
 | 
			
		|||
            Black Bishop on F7,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        let piece = pos.piece_on_square(Square::F7);
 | 
			
		||||
        let piece = pos.board.piece_on_square(Square::F7);
 | 
			
		||||
        assert_eq!(piece, Some(piece!(Black Bishop on F7)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -398,11 +242,11 @@ mod tests {
 | 
			
		|||
        let pos = test_position!(starting);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            pos.piece_on_square(Square::H1),
 | 
			
		||||
            pos.board.piece_on_square(Square::H1),
 | 
			
		||||
            Some(piece!(White Rook on H1))
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            pos.piece_on_square(Square::A8),
 | 
			
		||||
            pos.board.piece_on_square(Square::A8),
 | 
			
		||||
            Some(piece!(Black Rook on A8))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -464,16 +308,14 @@ mod tests {
 | 
			
		|||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn danger_squares() {
 | 
			
		||||
        let pos = PositionBuilder::new()
 | 
			
		||||
            .place_piece(piece!(White King on E1))
 | 
			
		||||
            .place_piece(piece!(Black King on E7))
 | 
			
		||||
            .place_piece(piece!(White Rook on E4))
 | 
			
		||||
            .to_move(Color::Black)
 | 
			
		||||
            .build();
 | 
			
		||||
        let pos = test_position!(Black, [
 | 
			
		||||
            White King on E1,
 | 
			
		||||
            Black King on E7,
 | 
			
		||||
            White Rook on E4,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        let danger_squares = pos.king_danger(Color::Black);
 | 
			
		||||
        let expected =
 | 
			
		||||
            bitboard![D1, F1, D2, E2, F2, E3, A4, B4, C4, D4, F4, G4, H4, E5, E6, E7, E8];
 | 
			
		||||
        let expected = bitboard![D1 F1 D2 E2 F2 E3 A4 B4 C4 D4 F4 G4 H4 E5 E6 E7 E8];
 | 
			
		||||
        assert_eq_bitboards!(danger_squares, expected);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,15 +9,15 @@ use chessfriend_board::Board;
 | 
			
		|||
use chessfriend_core::{Color, Direction, PlacedPiece, Shape, Square};
 | 
			
		||||
 | 
			
		||||
macro_rules! ray_in_direction {
 | 
			
		||||
    ($square:expr, $blockers:expr, $direction:ident, $occupied_squares:tt) => {{
 | 
			
		||||
    ($square:expr, $blockers:expr, $direction:ident, $first_occupied_square:tt) => {{
 | 
			
		||||
        let ray = BitBoard::ray($square, Direction::$direction);
 | 
			
		||||
        if let Some(first_occupied_square) = BitBoard::$occupied_squares(&(ray & $blockers)).next()
 | 
			
		||||
        {
 | 
			
		||||
        let ray_blockers = ray & $blockers;
 | 
			
		||||
        if let Some(first_occupied_square) = ray_blockers.$first_occupied_square() {
 | 
			
		||||
            let remainder = BitBoard::ray(first_occupied_square, Direction::$direction);
 | 
			
		||||
            let attack_ray = ray & !remainder;
 | 
			
		||||
            attack_ray
 | 
			
		||||
        } else {
 | 
			
		||||
            *ray
 | 
			
		||||
            ray
 | 
			
		||||
        }
 | 
			
		||||
    }};
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -51,34 +51,34 @@ fn _knight_sight(knight_square: Square, blockers: BitBoard) -> BitBoard {
 | 
			
		|||
 | 
			
		||||
fn _bishop_sight(bishop_square: Square, occupancy: BitBoard) -> BitBoard {
 | 
			
		||||
    #[rustfmt::skip]
 | 
			
		||||
    let sight = ray_in_direction!(bishop_square, occupancy, NorthEast, occupied_squares_trailing)
 | 
			
		||||
        | ray_in_direction!(bishop_square, occupancy, NorthWest, occupied_squares_trailing)
 | 
			
		||||
        | ray_in_direction!(bishop_square, occupancy, SouthEast, occupied_squares)
 | 
			
		||||
        | ray_in_direction!(bishop_square, occupancy, SouthWest, occupied_squares);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    sight
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn _rook_sight(rook_square: Square, occupancy: BitBoard) -> BitBoard {
 | 
			
		||||
    #[rustfmt::skip]
 | 
			
		||||
    let sight = ray_in_direction!(rook_square, occupancy, North, occupied_squares_trailing)
 | 
			
		||||
        | ray_in_direction!(rook_square, occupancy, East, occupied_squares_trailing)
 | 
			
		||||
        | ray_in_direction!(rook_square, occupancy, South, occupied_squares)
 | 
			
		||||
        | ray_in_direction!(rook_square, occupancy, West, occupied_squares);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    sight
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn _queen_sight(queen_square: Square, occupancy: BitBoard) -> BitBoard {
 | 
			
		||||
    #[rustfmt::skip]
 | 
			
		||||
    let sight = ray_in_direction!(queen_square, occupancy, NorthWest, occupied_squares_trailing)
 | 
			
		||||
        | ray_in_direction!(queen_square, occupancy, North, occupied_squares_trailing)
 | 
			
		||||
        | ray_in_direction!(queen_square, occupancy, NorthEast, occupied_squares_trailing)
 | 
			
		||||
        | ray_in_direction!(queen_square, occupancy, East, occupied_squares_trailing)
 | 
			
		||||
        | ray_in_direction!(queen_square, occupancy, SouthEast, occupied_squares)
 | 
			
		||||
        | ray_in_direction!(queen_square, occupancy, South, occupied_squares)
 | 
			
		||||
        | ray_in_direction!(queen_square, occupancy, SouthWest, occupied_squares)
 | 
			
		||||
        | ray_in_direction!(queen_square, occupancy, West, occupied_squares);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    sight
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -129,9 +129,9 @@ impl SightExt for PlacedPiece {
 | 
			
		|||
                Color::Black => self.black_pawn_sight(board, en_passant_square),
 | 
			
		||||
            },
 | 
			
		||||
            Shape::Knight => self.knight_sight(board),
 | 
			
		||||
            Shape::Bishop => self.bishop_sight(board.all_pieces_bitboard()),
 | 
			
		||||
            Shape::Rook => self.rook_sight(board.all_pieces_bitboard()),
 | 
			
		||||
            Shape::Queen => self.queen_sight(board.all_pieces_bitboard()),
 | 
			
		||||
            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),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -139,46 +139,40 @@ impl SightExt for PlacedPiece {
 | 
			
		|||
 | 
			
		||||
impl KingSightExt for PlacedPiece {
 | 
			
		||||
    fn king_sight(&self, board: &Board) -> BitBoard {
 | 
			
		||||
        _king_sight(
 | 
			
		||||
            self.square(),
 | 
			
		||||
            board.all_pieces_of_color_bitboard(self.color()),
 | 
			
		||||
        )
 | 
			
		||||
        _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.all_pieces_of_color_bitboard(self.color()),
 | 
			
		||||
        )
 | 
			
		||||
        _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() {
 | 
			
		||||
        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();
 | 
			
		||||
        let opponent = self.color.other();
 | 
			
		||||
        _white_pawn_sight(
 | 
			
		||||
            self.square().into(),
 | 
			
		||||
            board.all_pieces_bitboard(),
 | 
			
		||||
            board.all_pieces_of_color_bitboard(opponent),
 | 
			
		||||
            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.color().other();
 | 
			
		||||
        let opponent = self.piece.color.other();
 | 
			
		||||
        _black_pawn_sight(
 | 
			
		||||
            self.square().into(),
 | 
			
		||||
            board.all_pieces_bitboard(),
 | 
			
		||||
            board.all_pieces_of_color_bitboard(opponent),
 | 
			
		||||
            self.square.into(),
 | 
			
		||||
            board.pieces.all_pieces(),
 | 
			
		||||
            board.pieces.all_pieces_of_color(opponent),
 | 
			
		||||
            en_passant_square.into(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -186,19 +180,19 @@ impl PawnSightExt for PlacedPiece {
 | 
			
		|||
 | 
			
		||||
impl BishopSightExt for PlacedPiece {
 | 
			
		||||
    fn bishop_sight(&self, occupancy: BitBoard) -> BitBoard {
 | 
			
		||||
        _bishop_sight(self.square(), occupancy)
 | 
			
		||||
        _bishop_sight(self.square, occupancy)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RookSightExt for PlacedPiece {
 | 
			
		||||
    fn rook_sight(&self, occupancy: BitBoard) -> BitBoard {
 | 
			
		||||
        _rook_sight(self.square(), occupancy)
 | 
			
		||||
        _rook_sight(self.square, occupancy)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl QueenSightExt for PlacedPiece {
 | 
			
		||||
    fn queen_sight(&self, occupancy: BitBoard) -> BitBoard {
 | 
			
		||||
        _queen_sight(self.square(), occupancy)
 | 
			
		||||
        _queen_sight(self.square, occupancy)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -225,7 +219,7 @@ impl SliderRayToSquareExt for Shape {
 | 
			
		|||
                ray!(origin, NorthWest),
 | 
			
		||||
            ]
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .find(|(&ray, _)| !(target_bitboard & ray).is_empty()),
 | 
			
		||||
            .find(|(ray, _)| !(target_bitboard & ray).is_empty()),
 | 
			
		||||
            Shape::Rook => [
 | 
			
		||||
                ray!(origin, North),
 | 
			
		||||
                ray!(origin, East),
 | 
			
		||||
| 
						 | 
				
			
			@ -233,7 +227,7 @@ impl SliderRayToSquareExt for Shape {
 | 
			
		|||
                ray!(origin, West),
 | 
			
		||||
            ]
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .find(|(&ray, _)| !(target_bitboard & ray).is_empty()),
 | 
			
		||||
            .find(|(ray, _)| !(target_bitboard & ray).is_empty()),
 | 
			
		||||
            Shape::Queen => [
 | 
			
		||||
                ray!(origin, North),
 | 
			
		||||
                ray!(origin, NorthEast),
 | 
			
		||||
| 
						 | 
				
			
			@ -245,7 +239,7 @@ impl SliderRayToSquareExt for Shape {
 | 
			
		|||
                ray!(origin, NorthWest),
 | 
			
		||||
            ]
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .find(|(&ray, _)| !(target_bitboard & ray).is_empty()),
 | 
			
		||||
            .find(|(ray, _)| !(target_bitboard & ray).is_empty()),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -307,10 +301,9 @@ mod tests {
 | 
			
		|||
    mod pawn {
 | 
			
		||||
        use crate::test_position;
 | 
			
		||||
        use chessfriend_bitboard::{bitboard, BitBoard};
 | 
			
		||||
        use chessfriend_core::{piece, Square};
 | 
			
		||||
        use chessfriend_moves::EnPassant;
 | 
			
		||||
        use chessfriend_core::piece;
 | 
			
		||||
 | 
			
		||||
        sight_test!(e4_pawn, piece!(White Pawn on E4), bitboard!(D5, F5));
 | 
			
		||||
        sight_test!(e4_pawn, piece!(White Pawn on E4), bitboard![D5 F5]);
 | 
			
		||||
 | 
			
		||||
        sight_test!(
 | 
			
		||||
            e4_pawn_one_blocker,
 | 
			
		||||
| 
						 | 
				
			
			@ -352,15 +345,14 @@ mod tests {
 | 
			
		|||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn e5_en_passant() {
 | 
			
		||||
            let mut pos = test_position!(
 | 
			
		||||
            let pos = test_position!(White, [
 | 
			
		||||
                White Pawn on E5,
 | 
			
		||||
                Black Pawn on D5,
 | 
			
		||||
            );
 | 
			
		||||
            pos.test_set_en_passant(EnPassant::from_target_square(Square::D6).unwrap());
 | 
			
		||||
            ], D6);
 | 
			
		||||
            let piece = piece!(White Pawn on E5);
 | 
			
		||||
            let sight = pos.sight_of_piece(&piece);
 | 
			
		||||
 | 
			
		||||
            assert_eq!(sight, bitboard!(D6, F6));
 | 
			
		||||
            assert_eq!(sight, bitboard!(D6 F6));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -372,7 +364,7 @@ mod tests {
 | 
			
		|||
        sight_test!(
 | 
			
		||||
            f6_knight,
 | 
			
		||||
            piece!(Black Knight on F6),
 | 
			
		||||
            bitboard!(H7, G8, E8, D7, D5, E4, G4, H5)
 | 
			
		||||
            bitboard![H7 G8 E8 D7 D5 E4 G4 H5]
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -382,13 +374,13 @@ mod tests {
 | 
			
		|||
        sight_test!(
 | 
			
		||||
            c2_bishop,
 | 
			
		||||
            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 = Shape::Bishop.ray_to_square(Square::C5, Square::E7);
 | 
			
		||||
            let expected_ray = bitboard![D6, E7];
 | 
			
		||||
            let expected_ray = bitboard![D6 E7];
 | 
			
		||||
            assert_eq!(generated_ray, Some(expected_ray));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -400,7 +392,7 @@ mod tests {
 | 
			
		|||
        sight_test!(
 | 
			
		||||
            g3_rook,
 | 
			
		||||
            piece!(White Rook on G3),
 | 
			
		||||
            bitboard!(G1, G2, G4, G5, G6, G7, G8, A3, B3, C3, D3, E3, F3, H3)
 | 
			
		||||
            bitboard![G1 G2 G4 G5 G6 G7 G8 A3 B3 C3 D3 E3 F3 H3]
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        sight_test!(
 | 
			
		||||
| 
						 | 
				
			
			@ -411,25 +403,25 @@ mod tests {
 | 
			
		|||
                Black King on E7,
 | 
			
		||||
            ],
 | 
			
		||||
            piece!(White Rook on E4),
 | 
			
		||||
            bitboard!(A4, B4, C4, D4, F4, G4, H4, E2, E3, E5, E6, E7, E1)
 | 
			
		||||
            bitboard![A4 B4 C4 D4 F4 G4 H4 E2 E3 E5 E6 E7 E1]
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        #[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];
 | 
			
		||||
            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];
 | 
			
		||||
            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];
 | 
			
		||||
            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];
 | 
			
		||||
            let expected_ray = bitboard![A5 A4 A3];
 | 
			
		||||
            assert_eq!(generated_ray, Some(expected_ray));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -438,10 +430,6 @@ mod tests {
 | 
			
		|||
        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 on E1), bitboard![D1 D2 E2 F2 F1]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue