Move a whole bunch of stuff to the new chessfriend_board package
This commit is contained in:
		
							parent
							
								
									797606785e
								
							
						
					
					
						commit
						1d82d27f84
					
				
					 12 changed files with 1130 additions and 41 deletions
				
			
		
							
								
								
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -69,6 +69,14 @@ dependencies = [
 | 
			
		|||
 "chessfriend_core",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "chessfriend_board"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "chessfriend_bitboard",
 | 
			
		||||
 "chessfriend_core",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "chessfriend_core"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,10 @@
 | 
			
		|||
[package]
 | 
			
		||||
name = "board"
 | 
			
		||||
name = "chessfriend_board"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
chessfriend_bitboard = { path = "../bitboard" }
 | 
			
		||||
chessfriend_core = { path = "../core" }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										290
									
								
								board/src/board.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								board/src/board.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,290 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use crate::{display::DiagramFormatter, Castle, EnPassant, Flags, PieceBitBoards, Pieces};
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Eq)]
 | 
			
		||||
pub struct Board {
 | 
			
		||||
    player_to_move: Color,
 | 
			
		||||
    flags: Flags,
 | 
			
		||||
    pieces: PieceBitBoards,
 | 
			
		||||
    en_passant: Option<EnPassant>,
 | 
			
		||||
    half_move_counter: u16,
 | 
			
		||||
    full_move_number: u16,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Board {
 | 
			
		||||
    /// An empty board
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn empty() -> Self {
 | 
			
		||||
        Board::default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// The starting position
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn starting() -> Self {
 | 
			
		||||
        const BLACK_PIECES: [BitBoard; Shape::NUM] = [
 | 
			
		||||
            BitBoard::new(0b0000_0000_1111_1111 << 48),
 | 
			
		||||
            BitBoard::new(0b0100_0010_0000_0000 << 48),
 | 
			
		||||
            BitBoard::new(0b0010_0100_0000_0000 << 48),
 | 
			
		||||
            BitBoard::new(0b1000_0001_0000_0000 << 48),
 | 
			
		||||
            BitBoard::new(0b0000_1000_0000_0000 << 48),
 | 
			
		||||
            BitBoard::new(0b0001_0000_0000_0000 << 48),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        const WHITE_PIECES: [BitBoard; Shape::NUM] = [
 | 
			
		||||
            BitBoard::new(0b1111_1111_0000_0000),
 | 
			
		||||
            BitBoard::new(0b0000_0000_0100_0010),
 | 
			
		||||
            BitBoard::new(0b0000_0000_0010_0100),
 | 
			
		||||
            BitBoard::new(0b0000_0000_1000_0001),
 | 
			
		||||
            BitBoard::new(0b0000_0000_0000_1000),
 | 
			
		||||
            BitBoard::new(0b0000_0000_0001_0000),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            player_to_move: Color::White,
 | 
			
		||||
            pieces: PieceBitBoards::new([WHITE_PIECES, BLACK_PIECES]),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn new(
 | 
			
		||||
        player_to_move: Color,
 | 
			
		||||
        flags: Flags,
 | 
			
		||||
        pieces: PieceBitBoards,
 | 
			
		||||
        en_passant: Option<EnPassant>,
 | 
			
		||||
        half_move_counter: u16,
 | 
			
		||||
        full_move_number: u16,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            player_to_move,
 | 
			
		||||
            flags,
 | 
			
		||||
            pieces,
 | 
			
		||||
            en_passant,
 | 
			
		||||
            half_move_counter,
 | 
			
		||||
            full_move_number,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn player_to_move(&self) -> Color {
 | 
			
		||||
        self.player_to_move
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn move_number(&self) -> u16 {
 | 
			
		||||
        self.full_move_number
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn ply_counter(&self) -> u16 {
 | 
			
		||||
        self.half_move_counter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub(crate) fn flags(&self) -> &Flags {
 | 
			
		||||
        &self.flags
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn player_has_right_to_castle(&self, color: Color, castle: Castle) -> bool {
 | 
			
		||||
        self.flags.player_has_right_to_castle(color, castle)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// The rook to use for a castling move.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn rook_for_castle(&self, player: Color, castle: Castle) -> Option<PlacedPiece> {
 | 
			
		||||
        let square = castle.parameters(player).rook_origin_square();
 | 
			
		||||
        self.piece_on_square(square)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A [`BitBoard`] representing the set of squares containing a piece.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn occupied_squares(&self) -> &BitBoard {
 | 
			
		||||
        self.pieces.all_pieces()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn friendly_pieces(&self) -> &BitBoard {
 | 
			
		||||
        self.pieces.all_pieces_of_color(self.player_to_move)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn opposing_pieces(&self) -> &BitBoard {
 | 
			
		||||
        self.pieces.all_pieces_of_color(self.player_to_move.other())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn all_pieces(&self) -> (&BitBoard, &BitBoard) {
 | 
			
		||||
        (self.friendly_pieces(), self.opposing_pieces())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A [`BitBoard`] representing the set of squares containing a piece. This set is the inverse of
 | 
			
		||||
    /// `Board::occupied_squares`.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn empty_squares(&self) -> BitBoard {
 | 
			
		||||
        !self.occupied_squares()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn pieces(&self, color: Color) -> Pieces {
 | 
			
		||||
        Pieces::new(self, color)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn has_en_passant_square(&self) -> bool {
 | 
			
		||||
        self.en_passant.is_some()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn en_passant(&self) -> Option<EnPassant> {
 | 
			
		||||
        self.en_passant
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn king_bitboard(&self, player: Color) -> &BitBoard {
 | 
			
		||||
        self.pieces.bitboard_for_piece(&Piece::king(player))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn king_square(&self, player: Color) -> Square {
 | 
			
		||||
        self.king_bitboard(player)
 | 
			
		||||
            .occupied_squares()
 | 
			
		||||
            .next()
 | 
			
		||||
            .unwrap()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn display(&self) -> DiagramFormatter<'_> {
 | 
			
		||||
        DiagramFormatter::new(self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn bitboard_for_color(&self, color: Color) -> &BitBoard {
 | 
			
		||||
        self.pieces.bitboard_for_color(color)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn bitboard_for_piece(&self, piece: Piece) -> &BitBoard {
 | 
			
		||||
        self.pieces.bitboard_for_piece(&piece)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
impl Board {
 | 
			
		||||
    pub(crate) fn test_set_en_passant(&mut self, en_passant: EnPassant) {
 | 
			
		||||
        self.en_passant = Some(en_passant);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Board {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            player_to_move: Color::White,
 | 
			
		||||
            flags: Flags::default(),
 | 
			
		||||
            pieces: PieceBitBoards::default(),
 | 
			
		||||
            en_passant: None,
 | 
			
		||||
            half_move_counter: 0,
 | 
			
		||||
            full_move_number: 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialEq for Board {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.pieces == other.pieces
 | 
			
		||||
            && self.player_to_move == other.player_to_move
 | 
			
		||||
            && self.flags == other.flags
 | 
			
		||||
            && self.en_passant == other.en_passant
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::test_board;
 | 
			
		||||
    use chessfriend_core::piece;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn piece_on_square() {
 | 
			
		||||
        let pos = test_board![
 | 
			
		||||
            Black Bishop on F7,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        let piece = pos.piece_on_square(Square::F7);
 | 
			
		||||
        assert_eq!(piece, Some(piece!(Black Bishop on F7)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn piece_in_starting_position() {
 | 
			
		||||
        let board = test_board!(starting);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            board.piece_on_square(Square::H1),
 | 
			
		||||
            Some(piece!(White Rook on H1))
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            board.piece_on_square(Square::A8),
 | 
			
		||||
            Some(piece!(Black Rook on A8))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn king_not_on_starting_square_cannot_castle() {
 | 
			
		||||
        let board = test_board!(White King on E4);
 | 
			
		||||
        assert!(!board.player_has_right_to_castle(Color::White, Castle::KingSide));
 | 
			
		||||
        assert!(!board.player_has_right_to_castle(Color::White, Castle::QueenSide));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn king_on_starting_square_can_castle() {
 | 
			
		||||
        let board = test_board!(
 | 
			
		||||
            White King on E1,
 | 
			
		||||
            White Rook on A1,
 | 
			
		||||
            White Rook on H1
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert!(board.player_has_right_to_castle(Color::White, Castle::KingSide));
 | 
			
		||||
        assert!(board.player_has_right_to_castle(Color::White, Castle::QueenSide));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn rook_for_castle() {
 | 
			
		||||
        let board = test_board![
 | 
			
		||||
            White King on E1,
 | 
			
		||||
            White Rook on H1,
 | 
			
		||||
            White Rook on A1,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            board.rook_for_castle(Color::White, Castle::KingSide),
 | 
			
		||||
            Some(piece!(White Rook on H1))
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            board.rook_for_castle(Color::White, Castle::QueenSide),
 | 
			
		||||
            Some(piece!(White Rook on A1))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										179
									
								
								board/src/builder.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								board/src/builder.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,179 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use crate::{Board, Castle, EnPassant, Flags, PieceBitBoards};
 | 
			
		||||
use chessfriend_core::{piece, Color, Piece, PlacedPiece, Rank, Shape, Square};
 | 
			
		||||
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 {
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self::default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn from_board(board: &Board) -> Self {
 | 
			
		||||
        let pieces = board
 | 
			
		||||
            .pieces(Color::White)
 | 
			
		||||
            .chain(board.pieces(Color::Black))
 | 
			
		||||
            .map(|placed_piece| (placed_piece.square(), *placed_piece.piece()))
 | 
			
		||||
            .collect::<BTreeMap<_, _>>();
 | 
			
		||||
 | 
			
		||||
        let white_king = board.king_square(Color::White);
 | 
			
		||||
        let black_king = board.king_square(Color::Black);
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            player_to_move: board.player_to_move(),
 | 
			
		||||
            flags: *board.flags(),
 | 
			
		||||
            pieces,
 | 
			
		||||
            kings: [Some(white_king), Some(black_king)],
 | 
			
		||||
            en_passant: board.en_passant(),
 | 
			
		||||
            ply_counter: board.ply_counter(),
 | 
			
		||||
            move_number: board.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) -> Board {
 | 
			
		||||
        let pieces = self
 | 
			
		||||
            .pieces
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(PlacedPiece::from)
 | 
			
		||||
            .filter(Self::is_piece_placement_valid)
 | 
			
		||||
            .collect::<PieceBitBoards>();
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Board::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,
 | 
			
		||||
            kings: [Some(white_king_square), Some(black_king_square)],
 | 
			
		||||
            en_passant: None,
 | 
			
		||||
            ply_counter: 0,
 | 
			
		||||
            move_number: 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::Builder;
 | 
			
		||||
    use chessfriend_core::piece;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn place_piece() {
 | 
			
		||||
        let piece = piece!(White Queen on E4);
 | 
			
		||||
        let builder = Builder::new().place_piece(piece).build();
 | 
			
		||||
        assert_eq!(builder.piece_on_square(piece.square()), Some(piece));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -12,41 +12,41 @@ pub enum Castle {
 | 
			
		|||
 | 
			
		||||
pub struct Parameters {
 | 
			
		||||
    /// Origin squares of the king and rook.
 | 
			
		||||
    origin_squares: Squares,
 | 
			
		||||
    origin: Squares,
 | 
			
		||||
 | 
			
		||||
    /// Target or destination squares for the king and rook.
 | 
			
		||||
    target_squares: Squares,
 | 
			
		||||
    target: Squares,
 | 
			
		||||
 | 
			
		||||
    /// The set of squares that must be clear of any pieces in order to perform this castle.
 | 
			
		||||
    clear_squares: BitBoard,
 | 
			
		||||
    clear: BitBoard,
 | 
			
		||||
 | 
			
		||||
    /// The set of squares that must not be attacked in order to perform this castle.
 | 
			
		||||
    check_squares: BitBoard,
 | 
			
		||||
    check: BitBoard,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Parameters {
 | 
			
		||||
    pub fn king_origin_square(&self) -> Square {
 | 
			
		||||
        self.origin_squares.king
 | 
			
		||||
        self.origin.king
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn rook_origin_square(&self) -> Square {
 | 
			
		||||
        self.origin_squares.rook
 | 
			
		||||
        self.origin.rook
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn king_target_square(&self) -> Square {
 | 
			
		||||
        self.target_squares.king
 | 
			
		||||
        self.target.king
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn rook_target_square(&self) -> Square {
 | 
			
		||||
        self.target_squares.rook
 | 
			
		||||
        self.target.rook
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn clear_squares(&self) -> &BitBoard {
 | 
			
		||||
        &self.clear_squares
 | 
			
		||||
        &self.clear
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn check_squares(&self) -> &BitBoard {
 | 
			
		||||
        &self.check_squares
 | 
			
		||||
        &self.check
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -63,59 +63,59 @@ impl Castle {
 | 
			
		|||
    const PARAMETERS: [[Parameters; 2]; 2] = [
 | 
			
		||||
        [
 | 
			
		||||
            Parameters {
 | 
			
		||||
                origin_squares: Squares {
 | 
			
		||||
                origin: Squares {
 | 
			
		||||
                    king: Square::E1,
 | 
			
		||||
                    rook: Square::H1,
 | 
			
		||||
                },
 | 
			
		||||
                target_squares: Squares {
 | 
			
		||||
                target: Squares {
 | 
			
		||||
                    king: Square::G1,
 | 
			
		||||
                    rook: Square::F1,
 | 
			
		||||
                },
 | 
			
		||||
                clear_squares: BitBoard::new(0b01100000),
 | 
			
		||||
                check_squares: BitBoard::new(0b01110000),
 | 
			
		||||
                clear: BitBoard::new(0b0110_0000),
 | 
			
		||||
                check: BitBoard::new(0b0111_0000),
 | 
			
		||||
            },
 | 
			
		||||
            Parameters {
 | 
			
		||||
                origin_squares: Squares {
 | 
			
		||||
                origin: Squares {
 | 
			
		||||
                    king: Square::E1,
 | 
			
		||||
                    rook: Square::A1,
 | 
			
		||||
                },
 | 
			
		||||
                target_squares: Squares {
 | 
			
		||||
                target: Squares {
 | 
			
		||||
                    king: Square::C1,
 | 
			
		||||
                    rook: Square::D1,
 | 
			
		||||
                },
 | 
			
		||||
                clear_squares: BitBoard::new(0b00001110),
 | 
			
		||||
                check_squares: BitBoard::new(0b00011100),
 | 
			
		||||
                clear: BitBoard::new(0b0000_1110),
 | 
			
		||||
                check: BitBoard::new(0b0001_1100),
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            Parameters {
 | 
			
		||||
                origin_squares: Squares {
 | 
			
		||||
                origin: Squares {
 | 
			
		||||
                    king: Square::E8,
 | 
			
		||||
                    rook: Square::H8,
 | 
			
		||||
                },
 | 
			
		||||
                target_squares: Squares {
 | 
			
		||||
                target: Squares {
 | 
			
		||||
                    king: Square::G8,
 | 
			
		||||
                    rook: Square::F8,
 | 
			
		||||
                },
 | 
			
		||||
                clear_squares: BitBoard::new(0b01100000 << 8 * 7),
 | 
			
		||||
                check_squares: BitBoard::new(0b01110000 << 8 * 7),
 | 
			
		||||
                clear: BitBoard::new(0b0110_0000 << (8 * 7)),
 | 
			
		||||
                check: BitBoard::new(0b0111_0000 << (8 * 7)),
 | 
			
		||||
            },
 | 
			
		||||
            Parameters {
 | 
			
		||||
                origin_squares: Squares {
 | 
			
		||||
                origin: Squares {
 | 
			
		||||
                    king: Square::E8,
 | 
			
		||||
                    rook: Square::A8,
 | 
			
		||||
                },
 | 
			
		||||
                target_squares: Squares {
 | 
			
		||||
                target: Squares {
 | 
			
		||||
                    king: Square::C8,
 | 
			
		||||
                    rook: Square::D8,
 | 
			
		||||
                },
 | 
			
		||||
                clear_squares: BitBoard::new(0b00001110 << 8 * 7),
 | 
			
		||||
                check_squares: BitBoard::new(0b00011100 << 8 * 7),
 | 
			
		||||
                clear: BitBoard::new(0b0000_1110 << (8 * 7)),
 | 
			
		||||
                check: BitBoard::new(0b0001_1100 << (8 * 7)),
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    pub fn parameters(&self, color: Color) -> &'static Parameters {
 | 
			
		||||
        &Castle::PARAMETERS[color as usize][*self as usize]
 | 
			
		||||
    pub fn parameters(self, color: Color) -> &'static Parameters {
 | 
			
		||||
        &Castle::PARAMETERS[color as usize][self as usize]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										68
									
								
								board/src/display.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								board/src/display.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,68 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use crate::Board;
 | 
			
		||||
use chessfriend_core::{File, Rank, Square};
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
pub struct DiagramFormatter<'a>(&'a Board);
 | 
			
		||||
 | 
			
		||||
impl<'a> DiagramFormatter<'a> {
 | 
			
		||||
    pub fn new(board: &'a Board) -> Self {
 | 
			
		||||
        Self(board)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> fmt::Display for DiagramFormatter<'a> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        writeln!(f, "  ╔═════════════════╗")?;
 | 
			
		||||
 | 
			
		||||
        for rank in Rank::ALL.into_iter().rev() {
 | 
			
		||||
            write!(f, "{rank} ║ ")?;
 | 
			
		||||
 | 
			
		||||
            for file in File::ALL {
 | 
			
		||||
                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, "· ")?,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            writeln!(f, "║")?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        writeln!(f, "  ╚═════════════════╝")?;
 | 
			
		||||
        writeln!(f, "    a b c d e f g h")?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::{test_board, Board};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[ignore]
 | 
			
		||||
    fn empty() {
 | 
			
		||||
        let pos = test_board!(empty);
 | 
			
		||||
        let diagram = DiagramFormatter(&pos);
 | 
			
		||||
        println!("{diagram}");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[ignore]
 | 
			
		||||
    fn one_king() {
 | 
			
		||||
        let pos = test_board![Black King on H3];
 | 
			
		||||
        let diagram = DiagramFormatter(&pos);
 | 
			
		||||
        println!("{diagram}");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[ignore]
 | 
			
		||||
    fn starting() {
 | 
			
		||||
        let pos = test_board!(starting);
 | 
			
		||||
        let diagram = DiagramFormatter(&pos);
 | 
			
		||||
        println!("{diagram}");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								board/src/en_passant.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								board/src/en_passant.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use chessfriend_core::{Rank, Square};
 | 
			
		||||
 | 
			
		||||
/// En passant information.
 | 
			
		||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 | 
			
		||||
pub struct EnPassant {
 | 
			
		||||
    target: Square,
 | 
			
		||||
    capture: Square,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EnPassant {
 | 
			
		||||
    fn _capture_square(target: Square) -> Option<Square> {
 | 
			
		||||
        let (file, rank) = target.file_rank();
 | 
			
		||||
        match rank {
 | 
			
		||||
            Rank::THREE => Some(Square::from_file_rank(file, Rank::FOUR)),
 | 
			
		||||
            Rank::SIX => Some(Square::from_file_rank(file, Rank::FIVE)),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return en passant information for a particular target square. The target
 | 
			
		||||
    /// square is the square a pawn capturing en passant will move to.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Return `None` if the square is not eligible for en passant.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ## Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use chessfriend_board::en_passant::EnPassant;
 | 
			
		||||
    /// use chessfriend_core::Square;
 | 
			
		||||
    /// assert!(EnPassant::from_target_square(Square::E3).is_some());
 | 
			
		||||
    /// assert!(EnPassant::from_target_square(Square::B4).is_none());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn from_target_square(target: Square) -> Option<Self> {
 | 
			
		||||
        Self::_capture_square(target).map(|capture| Self { target, capture })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// The square the capturing piece will move to.
 | 
			
		||||
    pub fn target_square(self) -> Square {
 | 
			
		||||
        self.target
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// The square on which the captured pawn sits.
 | 
			
		||||
    pub fn capture_square(self) -> Square {
 | 
			
		||||
        self.capture
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								board/src/flags.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								board/src/flags.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use crate::Castle;
 | 
			
		||||
use chessfriend_core::Color;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
 | 
			
		||||
pub struct Flags(u8);
 | 
			
		||||
 | 
			
		||||
impl Flags {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn player_has_right_to_castle_flag_offset(color: Color, castle: Castle) -> usize {
 | 
			
		||||
        ((color as usize) << 1) + castle as usize
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    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 &= 0b1111_1100;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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(0b0000_1111)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[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,14 +1,21 @@
 | 
			
		|||
pub fn add(left: usize, right: usize) -> usize {
 | 
			
		||||
    left + right
 | 
			
		||||
}
 | 
			
		||||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
pub mod en_passant;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn it_works() {
 | 
			
		||||
        let result = add(2, 2);
 | 
			
		||||
        assert_eq!(result, 4);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
mod board;
 | 
			
		||||
mod builder;
 | 
			
		||||
mod castle;
 | 
			
		||||
mod display;
 | 
			
		||||
mod flags;
 | 
			
		||||
mod macros;
 | 
			
		||||
mod piece_sets;
 | 
			
		||||
mod pieces;
 | 
			
		||||
 | 
			
		||||
pub use board::Board;
 | 
			
		||||
pub use builder::Builder;
 | 
			
		||||
 | 
			
		||||
use castle::Castle;
 | 
			
		||||
use en_passant::EnPassant;
 | 
			
		||||
use flags::Flags;
 | 
			
		||||
use piece_sets::PieceBitBoards;
 | 
			
		||||
use pieces::Pieces;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										96
									
								
								board/src/macros.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								board/src/macros.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,96 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! board {
 | 
			
		||||
    [$($color:ident $shape:ident on $square:ident),* $(,)?] => {
 | 
			
		||||
        $crate::Builder::new()
 | 
			
		||||
            $(.place_piece(
 | 
			
		||||
                chessfriend_core::PlacedPiece::new(
 | 
			
		||||
                    chessfriend_core::Piece::new(
 | 
			
		||||
                        chessfriend_core::Color::$color,
 | 
			
		||||
                        chessfriend_core::Shape::$shape),
 | 
			
		||||
                    chessfriend_core::Square::$square
 | 
			
		||||
                )
 | 
			
		||||
            ))*
 | 
			
		||||
            .build()
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! test_board {
 | 
			
		||||
    ($to_move:ident, [ $($color:ident $shape:ident on $square:ident),* $(,)? ], $en_passant:ident) => {
 | 
			
		||||
        {
 | 
			
		||||
            let board = $crate::Builder::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!("{}", board.display());
 | 
			
		||||
 | 
			
		||||
            board
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($to_move:ident, [ $($color:ident $shape:ident on $square:ident),* $(,)? ]) => {
 | 
			
		||||
        {
 | 
			
		||||
            let board = $crate::Builder::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!("{}", board.display());
 | 
			
		||||
 | 
			
		||||
            pos
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($($color:ident $shape:ident on $square:ident),* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            let board = $crate::Builder::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!("{}", board.display());
 | 
			
		||||
 | 
			
		||||
            board
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (empty) => {
 | 
			
		||||
        {
 | 
			
		||||
            let board = Board::empty();
 | 
			
		||||
            println!("{}", board.display());
 | 
			
		||||
            board
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (starting) => {
 | 
			
		||||
        {
 | 
			
		||||
            let board = Board::starting();
 | 
			
		||||
            println!("{}", board.display());
 | 
			
		||||
            board
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										174
									
								
								board/src/piece_sets.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								board/src/piece_sets.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,174 @@
 | 
			
		|||
// 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										127
									
								
								board/src/pieces.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								board/src/pieces.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,127 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
use super::Board;
 | 
			
		||||
use chessfriend_bitboard::BitBoard;
 | 
			
		||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
 | 
			
		||||
 | 
			
		||||
pub struct Pieces<'a> {
 | 
			
		||||
    color: Color,
 | 
			
		||||
    board: &'a Board,
 | 
			
		||||
    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(board: &Board, color: Color) -> Pieces {
 | 
			
		||||
        Pieces {
 | 
			
		||||
            color,
 | 
			
		||||
            board,
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
        for shape in self.shape_iterator.by_ref() {
 | 
			
		||||
            let piece = Piece::new(self.color, *shape);
 | 
			
		||||
 | 
			
		||||
            let bitboard = self.board.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::{test_board, Board, Builder};
 | 
			
		||||
    use chessfriend_core::{piece, Color};
 | 
			
		||||
    use std::collections::HashSet;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn empty() {
 | 
			
		||||
        let board = Board::empty();
 | 
			
		||||
        let mut pieces = board.pieces(Color::White);
 | 
			
		||||
        assert_eq!(pieces.next(), None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn one() {
 | 
			
		||||
        let pos = Builder::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 board = test_board![
 | 
			
		||||
            White Queen on E4,
 | 
			
		||||
            White King on A1,
 | 
			
		||||
            White Pawn on B2,
 | 
			
		||||
            White Pawn on C2,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        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(board.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>>()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue