[board, core, moves, position] Implement castling
Implement a new method on Position that evaluates whether the active color can castle on a given wing of the board. Then, implement making a castling move in the position. Make a new Wing enum in the core crate to specify kingside or queenside. Replace the Castle enum from the board crate with this one. This caused a lot of churn... Along the way fix a bunch of tests. Note: there's still no way to actually make a castling move in explorer.
This commit is contained in:
		
							parent
							
								
									6816e350eb
								
							
						
					
					
						commit
						0c1863acb9
					
				
					 18 changed files with 499 additions and 258 deletions
				
			
		| 
						 | 
					@ -7,7 +7,7 @@ use crate::{
 | 
				
			||||||
    PieceSet,
 | 
					    PieceSet,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use chessfriend_bitboard::BitBoard;
 | 
					use chessfriend_bitboard::BitBoard;
 | 
				
			||||||
use chessfriend_core::{Color, Piece, Shape, Square};
 | 
					use chessfriend_core::{Color, Piece, Shape, Square, Wing};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
 | 
					#[derive(Clone, Debug, Default, Eq, PartialEq)]
 | 
				
			||||||
pub struct Board {
 | 
					pub struct Board {
 | 
				
			||||||
| 
						 | 
					@ -82,10 +82,12 @@ impl Board {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Board {
 | 
					impl Board {
 | 
				
			||||||
 | 
					    /// A [`BitBoard`] of squares occupied by pieces of all colors.
 | 
				
			||||||
    pub fn occupancy(&self) -> BitBoard {
 | 
					    pub fn occupancy(&self) -> BitBoard {
 | 
				
			||||||
        self.pieces.occpuancy()
 | 
					        self.pieces.occpuancy()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// A [`BitBoard`] of squares that are vacant.
 | 
				
			||||||
    pub fn vacancy(&self) -> BitBoard {
 | 
					    pub fn vacancy(&self) -> BitBoard {
 | 
				
			||||||
        !self.occupancy()
 | 
					        !self.occupancy()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -99,6 +101,13 @@ impl Board {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Board {
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn castling_parameters(&self, wing: Wing) -> &'static castle::Parameters {
 | 
				
			||||||
 | 
					        &castle::Parameters::BY_COLOR[self.active_color as usize][wing as usize]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Board {
 | 
					impl Board {
 | 
				
			||||||
    pub fn display(&self) -> DiagramFormatter<'_> {
 | 
					    pub fn display(&self) -> DiagramFormatter<'_> {
 | 
				
			||||||
        DiagramFormatter::new(self)
 | 
					        DiagramFormatter::new(self)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,22 +3,5 @@
 | 
				
			||||||
mod parameters;
 | 
					mod parameters;
 | 
				
			||||||
mod rights;
 | 
					mod rights;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub use parameters::Parameters;
 | 
				
			||||||
pub use rights::Rights;
 | 
					pub use rights::Rights;
 | 
				
			||||||
 | 
					 | 
				
			||||||
use chessfriend_core::Color;
 | 
					 | 
				
			||||||
use parameters::Parameters;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[repr(u8)]
 | 
					 | 
				
			||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
 | 
					 | 
				
			||||||
pub enum Castle {
 | 
					 | 
				
			||||||
    KingSide = 0,
 | 
					 | 
				
			||||||
    QueenSide = 1,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Castle {
 | 
					 | 
				
			||||||
    pub const ALL: [Castle; 2] = [Castle::KingSide, Castle::QueenSide];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn parameters(self, color: Color) -> &'static Parameters {
 | 
					 | 
				
			||||||
        &Parameters::BY_COLOR[color as usize][self as usize]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,31 +1,32 @@
 | 
				
			||||||
use chessfriend_bitboard::BitBoard;
 | 
					use chessfriend_bitboard::BitBoard;
 | 
				
			||||||
use chessfriend_core::{Color, Square};
 | 
					use chessfriend_core::{Color, Square, Wing};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub struct Parameters {
 | 
					pub struct Parameters {
 | 
				
			||||||
    /// Origin squares of the king and rook.
 | 
					    /// Origin squares of the king and rook.
 | 
				
			||||||
    origin: Squares,
 | 
					    pub origin: Squares,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Target or destination squares for the king and rook.
 | 
					    /// Target or destination squares for the king and rook.
 | 
				
			||||||
    target: Squares,
 | 
					    pub target: Squares,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// The set of squares that must be clear of any pieces in order to perform
 | 
					    /// The set of squares that must be clear of any pieces in order to perform
 | 
				
			||||||
    /// this castle.
 | 
					    /// this castle.
 | 
				
			||||||
    clear: BitBoard,
 | 
					    pub clear: BitBoard,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// The set of squares that must not be attacked (i.e. visible to opposing
 | 
					    /// The set of squares that must not be attacked (i.e. visible to opposing
 | 
				
			||||||
    /// pieces) in order to perform this castle.
 | 
					    /// pieces) in order to perform this castle.
 | 
				
			||||||
    check: BitBoard,
 | 
					    pub check: BitBoard,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub(super) struct Squares {
 | 
					pub struct Squares {
 | 
				
			||||||
    pub king: Square,
 | 
					    pub king: Square,
 | 
				
			||||||
    pub rook: Square,
 | 
					    pub rook: Square,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Parameters {
 | 
					impl Parameters {
 | 
				
			||||||
    /// Parameters for each castling move, organized by color and board-side.
 | 
					    /// Parameters for each castling move, organized by color and board-side.
 | 
				
			||||||
    pub(super) const BY_COLOR: [[Self; 2]; Color::NUM] = [
 | 
					    pub(crate) const BY_COLOR: [[Self; Wing::NUM]; Color::NUM] = [
 | 
				
			||||||
        [
 | 
					        [
 | 
				
			||||||
            Parameters {
 | 
					            Parameters {
 | 
				
			||||||
                origin: Squares {
 | 
					                origin: Squares {
 | 
				
			||||||
| 
						 | 
					@ -80,31 +81,8 @@ impl Parameters {
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn king_origin_square(&self) -> Square {
 | 
					    #[must_use]
 | 
				
			||||||
        self.origin.king
 | 
					    pub fn get(color: Color, wing: Wing) -> &'static Parameters {
 | 
				
			||||||
    }
 | 
					        &Self::BY_COLOR[color as usize][wing as usize]
 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn rook_origin_square(&self) -> Square {
 | 
					 | 
				
			||||||
        self.origin.rook
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn king_target_square(&self) -> Square {
 | 
					 | 
				
			||||||
        self.target.king
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn rook_target_square(&self) -> Square {
 | 
					 | 
				
			||||||
        self.target.rook
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// A [`BitBoard`] of the squares that must be clear of any piece in order
 | 
					 | 
				
			||||||
    /// to perform this castle move.
 | 
					 | 
				
			||||||
    pub fn clear_squares(&self) -> &BitBoard {
 | 
					 | 
				
			||||||
        &self.clear
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// A [`BitBoard`] of the squares that must not be visible to opposing
 | 
					 | 
				
			||||||
    /// pieces in order to perform this castle move.
 | 
					 | 
				
			||||||
    pub fn check_squares(&self) -> &BitBoard {
 | 
					 | 
				
			||||||
        &self.check
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
use super::Castle;
 | 
					use chessfriend_core::{Color, Wing};
 | 
				
			||||||
use chessfriend_core::Color;
 | 
					 | 
				
			||||||
use std::fmt;
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
 | 
					#[derive(Clone, Copy, Eq, Hash, PartialEq)]
 | 
				
			||||||
| 
						 | 
					@ -13,16 +12,16 @@ impl Rights {
 | 
				
			||||||
    /// as long as they have not moved their king, or the rook on that side of
 | 
					    /// as long as they have not moved their king, or the rook on that side of
 | 
				
			||||||
    /// the board.
 | 
					    /// the board.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn color_has_right(self, color: Color, castle: Castle) -> bool {
 | 
					    pub fn color_has_right(self, color: Color, wing: Wing) -> bool {
 | 
				
			||||||
        (self.0 & (1 << Self::flag_offset(color, castle))) != 0
 | 
					        (self.0 & (1 << Self::flag_offset(color, wing))) != 0
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn grant(&mut self, color: Color, castle: Castle) {
 | 
					    pub fn grant(&mut self, color: Color, wing: Wing) {
 | 
				
			||||||
        self.0 |= 1 << Self::flag_offset(color, castle);
 | 
					        self.0 |= 1 << Self::flag_offset(color, wing);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn revoke(&mut self, color: Color, castle: Castle) {
 | 
					    pub fn revoke(&mut self, color: Color, wing: Wing) {
 | 
				
			||||||
        self.0 &= !(1 << Self::flag_offset(color, castle));
 | 
					        self.0 &= !(1 << Self::flag_offset(color, wing));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Revoke castling rights for all colors and all sides of the board.
 | 
					    /// Revoke castling rights for all colors and all sides of the board.
 | 
				
			||||||
| 
						 | 
					@ -32,8 +31,8 @@ impl Rights {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Rights {
 | 
					impl Rights {
 | 
				
			||||||
    fn flag_offset(color: Color, castle: Castle) -> usize {
 | 
					    fn flag_offset(color: Color, wing: Wing) -> usize {
 | 
				
			||||||
        ((color as usize) << 1) + castle as usize
 | 
					        ((color as usize) << 1) + wing as usize
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,30 +54,30 @@ mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn bitfield_offsets() {
 | 
					    fn bitfield_offsets() {
 | 
				
			||||||
        assert_eq!(Rights::flag_offset(Color::White, Castle::KingSide), 0);
 | 
					        assert_eq!(Rights::flag_offset(Color::White, Wing::KingSide), 0);
 | 
				
			||||||
        assert_eq!(Rights::flag_offset(Color::White, Castle::QueenSide), 1);
 | 
					        assert_eq!(Rights::flag_offset(Color::White, Wing::QueenSide), 1);
 | 
				
			||||||
        assert_eq!(Rights::flag_offset(Color::Black, Castle::KingSide), 2);
 | 
					        assert_eq!(Rights::flag_offset(Color::Black, Wing::KingSide), 2);
 | 
				
			||||||
        assert_eq!(Rights::flag_offset(Color::Black, Castle::QueenSide), 3);
 | 
					        assert_eq!(Rights::flag_offset(Color::Black, Wing::QueenSide), 3);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn default_rights() {
 | 
					    fn default_rights() {
 | 
				
			||||||
        let mut rights = Rights::default();
 | 
					        let mut rights = Rights::default();
 | 
				
			||||||
        assert!(rights.color_has_right(Color::White, Castle::KingSide));
 | 
					        assert!(rights.color_has_right(Color::White, Wing::KingSide));
 | 
				
			||||||
        assert!(rights.color_has_right(Color::White, Castle::QueenSide));
 | 
					        assert!(rights.color_has_right(Color::White, Wing::QueenSide));
 | 
				
			||||||
        assert!(rights.color_has_right(Color::Black, Castle::KingSide));
 | 
					        assert!(rights.color_has_right(Color::Black, Wing::KingSide));
 | 
				
			||||||
        assert!(rights.color_has_right(Color::Black, Castle::QueenSide));
 | 
					        assert!(rights.color_has_right(Color::Black, Wing::QueenSide));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        rights.revoke(Color::White, Castle::QueenSide);
 | 
					        rights.revoke(Color::White, Wing::QueenSide);
 | 
				
			||||||
        assert!(rights.color_has_right(Color::White, Castle::KingSide));
 | 
					        assert!(rights.color_has_right(Color::White, Wing::KingSide));
 | 
				
			||||||
        assert!(!rights.color_has_right(Color::White, Castle::QueenSide));
 | 
					        assert!(!rights.color_has_right(Color::White, Wing::QueenSide));
 | 
				
			||||||
        assert!(rights.color_has_right(Color::Black, Castle::KingSide));
 | 
					        assert!(rights.color_has_right(Color::Black, Wing::KingSide));
 | 
				
			||||||
        assert!(rights.color_has_right(Color::Black, Castle::QueenSide));
 | 
					        assert!(rights.color_has_right(Color::Black, Wing::QueenSide));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        rights.grant(Color::White, Castle::QueenSide);
 | 
					        rights.grant(Color::White, Wing::QueenSide);
 | 
				
			||||||
        assert!(rights.color_has_right(Color::White, Castle::KingSide));
 | 
					        assert!(rights.color_has_right(Color::White, Wing::KingSide));
 | 
				
			||||||
        assert!(rights.color_has_right(Color::White, Castle::QueenSide));
 | 
					        assert!(rights.color_has_right(Color::White, Wing::QueenSide));
 | 
				
			||||||
        assert!(rights.color_has_right(Color::Black, Castle::KingSide));
 | 
					        assert!(rights.color_has_right(Color::Black, Wing::KingSide));
 | 
				
			||||||
        assert!(rights.color_has_right(Color::Black, Castle::QueenSide));
 | 
					        assert!(rights.color_has_right(Color::Black, Wing::QueenSide));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
// Eryn Wells <eryn@erynwells.me>
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{piece_sets::PlacePieceStrategy, Board, Castle};
 | 
					use crate::{piece_sets::PlacePieceStrategy, Board};
 | 
				
			||||||
use chessfriend_core::{coordinates::ParseSquareError, piece, Color, File, Piece, Rank, Square};
 | 
					use chessfriend_core::{
 | 
				
			||||||
 | 
					    coordinates::ParseSquareError, piece, Color, File, Piece, Rank, Square, Wing,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
use std::fmt::Write;
 | 
					use std::fmt::Write;
 | 
				
			||||||
use thiserror::Error;
 | 
					use thiserror::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -115,10 +117,10 @@ impl ToFenStr for Board {
 | 
				
			||||||
            .map_err(ToFenStrError::FmtError)?;
 | 
					            .map_err(ToFenStrError::FmtError)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let castling = [
 | 
					        let castling = [
 | 
				
			||||||
            (Color::White, Castle::KingSide),
 | 
					            (Color::White, Wing::KingSide),
 | 
				
			||||||
            (Color::White, Castle::QueenSide),
 | 
					            (Color::White, Wing::QueenSide),
 | 
				
			||||||
            (Color::Black, Castle::KingSide),
 | 
					            (Color::Black, Wing::KingSide),
 | 
				
			||||||
            (Color::Black, Castle::QueenSide),
 | 
					            (Color::Black, Wing::QueenSide),
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
        .map(|(color, castle)| {
 | 
					        .map(|(color, castle)| {
 | 
				
			||||||
            if !self.castling_rights.color_has_right(color, castle) {
 | 
					            if !self.castling_rights.color_has_right(color, castle) {
 | 
				
			||||||
| 
						 | 
					@ -126,10 +128,10 @@ impl ToFenStr for Board {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            match (color, castle) {
 | 
					            match (color, castle) {
 | 
				
			||||||
                (Color::White, Castle::KingSide) => "K",
 | 
					                (Color::White, Wing::KingSide) => "K",
 | 
				
			||||||
                (Color::White, Castle::QueenSide) => "Q",
 | 
					                (Color::White, Wing::QueenSide) => "Q",
 | 
				
			||||||
                (Color::Black, Castle::KingSide) => "k",
 | 
					                (Color::Black, Wing::KingSide) => "k",
 | 
				
			||||||
                (Color::Black, Castle::QueenSide) => "q",
 | 
					                (Color::Black, Wing::QueenSide) => "q",
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .concat();
 | 
					        .concat();
 | 
				
			||||||
| 
						 | 
					@ -232,10 +234,10 @@ impl FromFenStr for Board {
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            for ch in castling_rights.chars() {
 | 
					            for ch in castling_rights.chars() {
 | 
				
			||||||
                match ch {
 | 
					                match ch {
 | 
				
			||||||
                    'K' => board.castling_rights.grant(Color::White, Castle::KingSide),
 | 
					                    'K' => board.castling_rights.grant(Color::White, Wing::KingSide),
 | 
				
			||||||
                    'Q' => board.castling_rights.grant(Color::White, Castle::QueenSide),
 | 
					                    'Q' => board.castling_rights.grant(Color::White, Wing::QueenSide),
 | 
				
			||||||
                    'k' => board.castling_rights.grant(Color::Black, Castle::KingSide),
 | 
					                    'k' => board.castling_rights.grant(Color::Black, Wing::KingSide),
 | 
				
			||||||
                    'q' => board.castling_rights.grant(Color::Black, Castle::QueenSide),
 | 
					                    'q' => board.castling_rights.grant(Color::Black, Wing::QueenSide),
 | 
				
			||||||
                    _ => return Err(FromFenStrError::InvalidValue),
 | 
					                    _ => return Err(FromFenStrError::InvalidValue),
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,12 +10,7 @@ mod board;
 | 
				
			||||||
mod piece_sets;
 | 
					mod piece_sets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use board::Board;
 | 
					pub use board::Board;
 | 
				
			||||||
 | 
					pub use castle::Parameters as CastleParameters;
 | 
				
			||||||
pub use piece_sets::{PlacePieceError, PlacePieceStrategy};
 | 
					pub use piece_sets::{PlacePieceError, PlacePieceStrategy};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use castle::Castle;
 | 
					 | 
				
			||||||
use en_passant::EnPassant;
 | 
					 | 
				
			||||||
use piece_sets::PieceSet;
 | 
					use piece_sets::PieceSet;
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Used by macros.
 | 
					 | 
				
			||||||
#[allow(unused_imports)]
 | 
					 | 
				
			||||||
use piece_sets::{PlacePieceError, PlacePieceStrategy};
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,56 +0,0 @@
 | 
				
			||||||
use chessfriend_bitboard::BitBoard;
 | 
					 | 
				
			||||||
use chessfriend_core::{Color, Piece, Shape, Square};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A collection of bitboards that organize pieces by color.
 | 
					 | 
				
			||||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
 | 
					 | 
				
			||||||
pub(super) struct ByColor(BitBoard, [BitBoard; Color::NUM]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// A collection of bitboards that organize pieces first by color and then by piece type.
 | 
					 | 
				
			||||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
 | 
					 | 
				
			||||||
pub(super) struct ByColorAndShape([[BitBoard; Shape::NUM]; Color::NUM]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl ByColor {
 | 
					 | 
				
			||||||
    pub(super) fn new(all_pieces: BitBoard, bitboards_by_color: [BitBoard; Color::NUM]) -> Self {
 | 
					 | 
				
			||||||
        ByColor(all_pieces, bitboards_by_color)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(crate) fn all(&self) -> BitBoard {
 | 
					 | 
				
			||||||
        self.0
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(crate) fn bitboard(&self, color: Color) -> BitBoard {
 | 
					 | 
				
			||||||
        self.1[color as usize]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(super) fn set_square(&mut self, square: Square, color: Color) {
 | 
					 | 
				
			||||||
        self.0.set(square);
 | 
					 | 
				
			||||||
        self.1[color as usize].set(square);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(super) fn clear_square(&mut self, square: Square, color: Color) {
 | 
					 | 
				
			||||||
        self.0.clear(square);
 | 
					 | 
				
			||||||
        self.1[color as usize].clear(square);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl ByColorAndShape {
 | 
					 | 
				
			||||||
    pub(super) fn new(bitboards: [[BitBoard; Shape::NUM]; Color::NUM]) -> Self {
 | 
					 | 
				
			||||||
        Self(bitboards)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(super) fn bitboard_for_piece(&self, piece: Piece) -> BitBoard {
 | 
					 | 
				
			||||||
        self.0[piece.color as usize][piece.shape as usize]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(super) fn bitboard_for_piece_mut(&mut self, piece: Piece) -> &mut BitBoard {
 | 
					 | 
				
			||||||
        &mut self.0[piece.color as usize][piece.shape as usize]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(super) fn set_square(&mut self, square: Square, piece: Piece) {
 | 
					 | 
				
			||||||
        self.bitboard_for_piece_mut(piece).set(square);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(super) fn clear_square(&mut self, square: Square, piece: Piece) {
 | 
					 | 
				
			||||||
        self.bitboard_for_piece_mut(piece).clear(square);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,9 @@
 | 
				
			||||||
// Eryn Wells <eryn@erynwells.me>
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod wings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub use wings::Wing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::Color;
 | 
					use crate::Color;
 | 
				
			||||||
use std::fmt;
 | 
					use std::fmt;
 | 
				
			||||||
use thiserror::Error;
 | 
					use thiserror::Error;
 | 
				
			||||||
| 
						 | 
					@ -519,7 +523,7 @@ mod tests {
 | 
				
			||||||
    fn bad_algebraic_input() {
 | 
					    fn bad_algebraic_input() {
 | 
				
			||||||
        assert!("a0".parse::<Square>().is_err());
 | 
					        assert!("a0".parse::<Square>().is_err());
 | 
				
			||||||
        assert!("j3".parse::<Square>().is_err());
 | 
					        assert!("j3".parse::<Square>().is_err());
 | 
				
			||||||
        assert!("a11".parse::<Square>().is_err());
 | 
					        assert!("a9".parse::<Square>().is_err());
 | 
				
			||||||
        assert!("b-1".parse::<Square>().is_err());
 | 
					        assert!("b-1".parse::<Square>().is_err());
 | 
				
			||||||
        assert!("a 1".parse::<Square>().is_err());
 | 
					        assert!("a 1".parse::<Square>().is_err());
 | 
				
			||||||
        assert!("".parse::<Square>().is_err());
 | 
					        assert!("".parse::<Square>().is_err());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								core/src/coordinates/wings.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								core/src/coordinates/wings.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[repr(u8)]
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 | 
				
			||||||
 | 
					pub enum Wing {
 | 
				
			||||||
 | 
					    KingSide = 0,
 | 
				
			||||||
 | 
					    QueenSide = 1,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Wing {
 | 
				
			||||||
 | 
					    pub const NUM: usize = 2;
 | 
				
			||||||
 | 
					    pub const ALL: [Wing; Self::NUM] = [Self::KingSide, Self::QueenSide];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::fmt::Display for Wing {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Wing::KingSide => write!(f, "kingside"),
 | 
				
			||||||
 | 
					            Wing::QueenSide => write!(f, "queenside"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,6 @@ pub mod shapes;
 | 
				
			||||||
mod macros;
 | 
					mod macros;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use colors::Color;
 | 
					pub use colors::Color;
 | 
				
			||||||
pub use coordinates::{Direction, File, Rank, Square};
 | 
					pub use coordinates::{Direction, File, Rank, Square, Wing};
 | 
				
			||||||
pub use pieces::{Piece, PlacedPiece};
 | 
					pub use pieces::{Piece, PlacedPiece};
 | 
				
			||||||
pub use shapes::Shape;
 | 
					pub use shapes::Shape;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
// Eryn Wells <eryn@erynwells.me>
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{defs::Kind, Move, PromotionShape};
 | 
					use crate::{defs::Kind, Move, PromotionShape};
 | 
				
			||||||
use chessfriend_board::{castle, en_passant::EnPassant};
 | 
					use chessfriend_board::{en_passant::EnPassant, CastleParameters};
 | 
				
			||||||
use chessfriend_core::{Color, File, PlacedPiece, Rank, Square};
 | 
					use chessfriend_core::{Color, File, PlacedPiece, Rank, Square, Wing};
 | 
				
			||||||
use std::result::Result as StdResult;
 | 
					use std::result::Result as StdResult;
 | 
				
			||||||
use thiserror::Error;
 | 
					use thiserror::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,7 +92,7 @@ pub struct Promotion<S> {
 | 
				
			||||||
#[derive(Clone, Debug, Eq, PartialEq)]
 | 
					#[derive(Clone, Debug, Eq, PartialEq)]
 | 
				
			||||||
pub struct Castle {
 | 
					pub struct Castle {
 | 
				
			||||||
    color: Color,
 | 
					    color: Color,
 | 
				
			||||||
    castle: castle::Castle,
 | 
					    wing: Wing,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Style for Null {}
 | 
					impl Style for Null {}
 | 
				
			||||||
| 
						 | 
					@ -119,13 +119,13 @@ impl Style for Capture {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Style for Castle {
 | 
					impl Style for Castle {
 | 
				
			||||||
    fn origin_square(&self) -> Option<Square> {
 | 
					    fn origin_square(&self) -> Option<Square> {
 | 
				
			||||||
        let parameters = self.castle.parameters(self.color);
 | 
					        let parameters = CastleParameters::get(self.color, self.wing);
 | 
				
			||||||
        Some(parameters.king_origin_square())
 | 
					        Some(parameters.origin.king)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn target_square(&self) -> Option<Square> {
 | 
					    fn target_square(&self) -> Option<Square> {
 | 
				
			||||||
        let parameters = self.castle.parameters(self.color);
 | 
					        let parameters = CastleParameters::get(self.color, self.wing);
 | 
				
			||||||
        Some(parameters.king_target_square())
 | 
					        Some(parameters.target.king)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -255,9 +255,9 @@ impl Builder<Null> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn castling(color: Color, castle: castle::Castle) -> Builder<Castle> {
 | 
					    pub fn castling(color: Color, wing: Wing) -> Builder<Castle> {
 | 
				
			||||||
        Builder {
 | 
					        Builder {
 | 
				
			||||||
            style: Castle { color, castle },
 | 
					            style: Castle { color, wing },
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -357,9 +357,9 @@ impl Builder<Push> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Builder<Castle> {
 | 
					impl Builder<Castle> {
 | 
				
			||||||
    fn bits(&self) -> u16 {
 | 
					    fn bits(&self) -> u16 {
 | 
				
			||||||
        let bits = match self.style.castle {
 | 
					        let bits = match self.style.wing {
 | 
				
			||||||
            castle::Castle::KingSide => Kind::KingSideCastle,
 | 
					            Wing::KingSide => Kind::KingSideCastle,
 | 
				
			||||||
            castle::Castle::QueenSide => Kind::QueenSideCastle,
 | 
					            Wing::QueenSide => Kind::QueenSideCastle,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bits as u16
 | 
					        bits as u16
 | 
				
			||||||
| 
						 | 
					@ -403,6 +403,11 @@ impl Builder<EnPassantCapture> {
 | 
				
			||||||
        Move(Kind::EnPassantCapture as u16 | self.style.move_bits_unchecked())
 | 
					        Move(Kind::EnPassantCapture as u16 | self.style.move_bits_unchecked())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Build an en passant move.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// ## Errors
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns an error if the target or origin squares are invalid.
 | 
				
			||||||
    pub fn build(&self) -> Result {
 | 
					    pub fn build(&self) -> Result {
 | 
				
			||||||
        Ok(Move(
 | 
					        Ok(Move(
 | 
				
			||||||
            Kind::EnPassantCapture as u16 | self.style.move_bits()?,
 | 
					            Kind::EnPassantCapture as u16 | self.style.move_bits()?,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,8 +2,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::builder::Builder;
 | 
					use crate::builder::Builder;
 | 
				
			||||||
use crate::defs::Kind;
 | 
					use crate::defs::Kind;
 | 
				
			||||||
use chessfriend_board::castle::Castle;
 | 
					use chessfriend_core::{Rank, Shape, Square, Wing};
 | 
				
			||||||
use chessfriend_core::{Rank, Shape, Square};
 | 
					 | 
				
			||||||
use std::fmt;
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A single player's move. In game theory parlance, this is a "ply".
 | 
					/// A single player's move. In game theory parlance, this is a "ply".
 | 
				
			||||||
| 
						 | 
					@ -62,10 +61,10 @@ impl Move {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn castle(&self) -> Option<Castle> {
 | 
					    pub fn castle(&self) -> Option<Wing> {
 | 
				
			||||||
        match self.flags() {
 | 
					        match self.flags() {
 | 
				
			||||||
            0b0010 => Some(Castle::KingSide),
 | 
					            0b0010 => Some(Wing::KingSide),
 | 
				
			||||||
            0b0011 => Some(Castle::QueenSide),
 | 
					            0b0011 => Some(Wing::QueenSide),
 | 
				
			||||||
            _ => None,
 | 
					            _ => None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -77,7 +76,7 @@ impl Move {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn is_en_passant(&self) -> bool {
 | 
					    pub fn is_en_passant(&self) -> bool {
 | 
				
			||||||
        self.0 == Kind::EnPassantCapture as u16
 | 
					        self.flags() == Kind::EnPassantCapture as u16
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
| 
						 | 
					@ -130,8 +129,8 @@ impl fmt::Display for Move {
 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
        if let Some(castle) = self.castle() {
 | 
					        if let Some(castle) = self.castle() {
 | 
				
			||||||
            return match castle {
 | 
					            return match castle {
 | 
				
			||||||
                Castle::KingSide => write!(f, "{KINGSIDE_CASTLE_STR}"),
 | 
					                Wing::KingSide => write!(f, "{KINGSIDE_CASTLE_STR}"),
 | 
				
			||||||
                Castle::QueenSide => write!(f, "{QUEENSIDE_CASTLE_STR}"),
 | 
					                Wing::QueenSide => write!(f, "{QUEENSIDE_CASTLE_STR}"),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
// Eryn Wells <eryn@erynwells.me>
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use chessfriend_board::castle::Castle;
 | 
					use chessfriend_core::{piece, Color, File, Shape, Square, Wing};
 | 
				
			||||||
use chessfriend_core::{piece, Color, File, Shape, Square};
 | 
					 | 
				
			||||||
use chessfriend_moves::{testing::*, Builder, PromotionShape};
 | 
					use chessfriend_moves::{testing::*, Builder, PromotionShape};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
macro_rules! assert_flag {
 | 
					macro_rules! assert_flag {
 | 
				
			||||||
| 
						 | 
					@ -58,51 +57,50 @@ fn move_flags_capture() -> TestResult {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn move_flags_en_passant_capture() -> TestResult {
 | 
					fn move_flags_en_passant_capture() -> TestResult {
 | 
				
			||||||
    let mv = unsafe {
 | 
					    let ply = Builder::new()
 | 
				
			||||||
        Builder::new()
 | 
					        .from(Square::A4)
 | 
				
			||||||
            .from(Square::A4)
 | 
					        .capturing_en_passant_on(Square::B3)
 | 
				
			||||||
            .capturing_en_passant_on(Square::B3)
 | 
					        .build()?;
 | 
				
			||||||
            .build_unchecked()
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert_flags!(mv, false, false, true, true, false, false);
 | 
					    assert!(ply.is_en_passant());
 | 
				
			||||||
    assert_eq!(mv.origin_square(), Square::A4);
 | 
					    assert_eq!(ply.origin_square(), Square::A4);
 | 
				
			||||||
    assert_eq!(mv.target_square(), Square::B3);
 | 
					    assert_eq!(ply.target_square(), Square::B3);
 | 
				
			||||||
    assert_eq!(mv.capture_square(), Some(Square::B4));
 | 
					    assert_eq!(ply.capture_square(), Some(Square::B4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn move_flags_promotion() -> TestResult {
 | 
					fn move_flags_promotion() -> TestResult {
 | 
				
			||||||
    let mv = Builder::push(&piece!(White Pawn on H7))
 | 
					    let ply = Builder::push(&piece!(White Pawn on H7))
 | 
				
			||||||
        .to(Square::H8)
 | 
					        .to(Square::H8)
 | 
				
			||||||
        .promoting_to(PromotionShape::Queen)
 | 
					        .promoting_to(PromotionShape::Queen)
 | 
				
			||||||
        .build()?;
 | 
					        .build()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert_flags!(mv, false, false, false, false, false, true);
 | 
					    assert!(ply.is_promotion());
 | 
				
			||||||
    assert_eq!(mv.promotion(), Some(Shape::Queen));
 | 
					    assert_eq!(ply.promotion(), Some(Shape::Queen));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn move_flags_capture_promotion() -> TestResult {
 | 
					fn move_flags_capture_promotion() -> TestResult {
 | 
				
			||||||
    let mv = Builder::push(&piece!(White Pawn on H7))
 | 
					    let ply = Builder::push(&piece!(White Pawn on H7))
 | 
				
			||||||
        .to(Square::H8)
 | 
					        .to(Square::H8)
 | 
				
			||||||
        .capturing_piece(&piece!(Black Knight on G8))
 | 
					        .capturing_piece(&piece!(Black Knight on G8))
 | 
				
			||||||
        .promoting_to(PromotionShape::Queen)
 | 
					        .promoting_to(PromotionShape::Queen)
 | 
				
			||||||
        .build()?;
 | 
					        .build()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert_flags!(mv, false, false, false, true, false, true);
 | 
					    assert!(ply.is_capture());
 | 
				
			||||||
    assert_eq!(mv.promotion(), Some(Shape::Queen));
 | 
					    assert!(ply.is_promotion());
 | 
				
			||||||
 | 
					    assert_eq!(ply.promotion(), Some(Shape::Queen));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn move_flags_castle() -> TestResult {
 | 
					fn move_flags_castle() -> TestResult {
 | 
				
			||||||
    let mv = Builder::castling(Color::White, Castle::KingSide).build()?;
 | 
					    let mv = Builder::castling(Color::White, Wing::KingSide).build()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert_flags!(mv, false, false, false, false, true, false);
 | 
					    assert_flags!(mv, false, false, false, false, true, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,4 +14,5 @@ mod macros;
 | 
				
			||||||
#[macro_use]
 | 
					#[macro_use]
 | 
				
			||||||
mod testing;
 | 
					mod testing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use position::{MakeMoveError, MoveBuilder as MakeMoveBuilder, Position};
 | 
					pub use chessfriend_board::{fen, PlacePieceError, PlacePieceStrategy};
 | 
				
			||||||
 | 
					pub use position::{CastleEvaluationError, Position, ValidateMove};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,5 +5,5 @@ mod position;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use {
 | 
					pub use {
 | 
				
			||||||
    make_move::{MakeMoveError, ValidateMove},
 | 
					    make_move::{MakeMoveError, ValidateMove},
 | 
				
			||||||
    position::Position,
 | 
					    position::{CastleEvaluationError, Position},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,13 @@
 | 
				
			||||||
// Eryn Wells <eryn@erynwells.me>
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{movement::Movement, Position};
 | 
					use crate::{movement::Movement, Position};
 | 
				
			||||||
use chessfriend_board::{PlacePieceError, PlacePieceStrategy};
 | 
					use chessfriend_board::{CastleParameters, PlacePieceError, PlacePieceStrategy};
 | 
				
			||||||
use chessfriend_core::{Color, Piece, Square};
 | 
					use chessfriend_core::{Color, Piece, Square, Wing};
 | 
				
			||||||
use chessfriend_moves::Move;
 | 
					use chessfriend_moves::Move;
 | 
				
			||||||
use thiserror::Error;
 | 
					use thiserror::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::CastleEvaluationError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MakeMoveResult = Result<(), MakeMoveError>;
 | 
					type MakeMoveResult = Result<(), MakeMoveError>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
 | 
					#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
 | 
				
			||||||
| 
						 | 
					@ -38,6 +40,9 @@ pub enum MakeMoveError {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[error("{0}")]
 | 
					    #[error("{0}")]
 | 
				
			||||||
    PlacePieceError(#[from] PlacePieceError),
 | 
					    PlacePieceError(#[from] PlacePieceError),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[error("{0}")]
 | 
				
			||||||
 | 
					    CastleError(#[from] CastleEvaluationError),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub enum UnmakeMoveError {}
 | 
					pub enum UnmakeMoveError {}
 | 
				
			||||||
| 
						 | 
					@ -60,6 +65,10 @@ impl Position {
 | 
				
			||||||
            return self.make_capture_move(ply);
 | 
					            return self.make_capture_move(ply);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(wing) = ply.castle() {
 | 
				
			||||||
 | 
					            return self.make_castle_move(wing);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,6 +109,27 @@ impl Position {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn make_castle_move(&mut self, wing: Wing) -> MakeMoveResult {
 | 
				
			||||||
 | 
					        self.active_color_can_castle(wing)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let active_color = self.board.active_color;
 | 
				
			||||||
 | 
					        let parameters = self.board.castling_parameters(wing);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let king = self.board.remove_piece(parameters.origin.king).unwrap();
 | 
				
			||||||
 | 
					        self.board
 | 
				
			||||||
 | 
					            .place_piece(king, parameters.target.king, PlacePieceStrategy::default())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let rook = self.board.remove_piece(parameters.origin.rook).unwrap();
 | 
				
			||||||
 | 
					        self.board
 | 
				
			||||||
 | 
					            .place_piece(rook, parameters.target.rook, PlacePieceStrategy::default())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.board.castling_rights.revoke(active_color, wing);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.advance_clocks(HalfMoveClock::Advance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Position {
 | 
					impl Position {
 | 
				
			||||||
| 
						 | 
					@ -142,21 +172,14 @@ impl Position {
 | 
				
			||||||
            return Ok(());
 | 
					            return Ok(());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let active_piece = self.validate_active_piece(ply)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let origin_square = ply.origin_square();
 | 
					        let origin_square = ply.origin_square();
 | 
				
			||||||
        let active_piece = self
 | 
					 | 
				
			||||||
            .board
 | 
					 | 
				
			||||||
            .get_piece(origin_square)
 | 
					 | 
				
			||||||
            .ok_or(MakeMoveError::NoPiece(origin_square))?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if active_piece.color != self.board.active_color {
 | 
					 | 
				
			||||||
            return Err(MakeMoveError::NonActiveColor {
 | 
					 | 
				
			||||||
                piece: active_piece,
 | 
					 | 
				
			||||||
                square: origin_square,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let target_square = ply.target_square();
 | 
					        let target_square = ply.target_square();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Pawns can see squares they can't move to. So, calculating valid
 | 
				
			||||||
 | 
					        // squares requires a concept that includes Sight, but adds pawn pushes.
 | 
				
			||||||
 | 
					        // In ChessFriend, that concept is Movement.
 | 
				
			||||||
        let movement = active_piece.movement(origin_square, &self.board);
 | 
					        let movement = active_piece.movement(origin_square, &self.board);
 | 
				
			||||||
        if !movement.contains(target_square) {
 | 
					        if !movement.contains(target_square) {
 | 
				
			||||||
            return Err(MakeMoveError::NoMove {
 | 
					            return Err(MakeMoveError::NoMove {
 | 
				
			||||||
| 
						 | 
					@ -166,6 +189,8 @@ impl Position {
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: En Passant capture.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ply.is_capture() {
 | 
					        if ply.is_capture() {
 | 
				
			||||||
            let target = ply.target_square();
 | 
					            let target = ply.target_square();
 | 
				
			||||||
            if let Some(captured_piece) = self.board.get_piece(target) {
 | 
					            if let Some(captured_piece) = self.board.get_piece(target) {
 | 
				
			||||||
| 
						 | 
					@ -179,4 +204,22 @@ impl Position {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn validate_active_piece(&self, ply: Move) -> Result<Piece, MakeMoveError> {
 | 
				
			||||||
 | 
					        let origin_square = ply.origin_square();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let active_piece = self
 | 
				
			||||||
 | 
					            .board
 | 
				
			||||||
 | 
					            .get_piece(origin_square)
 | 
				
			||||||
 | 
					            .ok_or(MakeMoveError::NoPiece(origin_square))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if active_piece.color != self.board.active_color {
 | 
				
			||||||
 | 
					            return Err(MakeMoveError::NonActiveColor {
 | 
				
			||||||
 | 
					                piece: active_piece,
 | 
				
			||||||
 | 
					                square: origin_square,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(active_piece)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,20 +38,168 @@ impl Position {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
impl Position {
 | 
					impl Position {
 | 
				
			||||||
    /// Return a PlacedPiece representing the rook to use for a castling move.
 | 
					    /// Place a piece on the board.
 | 
				
			||||||
    pub(crate) fn rook_for_castle(&self, player: Color, castle: Castle) -> Option<PlacedPiece> {
 | 
					    ///
 | 
				
			||||||
        let square = match (player, castle) {
 | 
					    /// ## Errors
 | 
				
			||||||
            (Color::White, Castle::KingSide) => Square::H1,
 | 
					    ///
 | 
				
			||||||
            (Color::White, Castle::QueenSide) => Square::A1,
 | 
					    /// See [`chessfriend_board::Board::place_piece`].
 | 
				
			||||||
            (Color::Black, Castle::KingSide) => Square::H8,
 | 
					    pub fn place_piece(
 | 
				
			||||||
            (Color::Black, Castle::QueenSide) => Square::A8,
 | 
					        &mut self,
 | 
				
			||||||
        };
 | 
					        piece: Piece,
 | 
				
			||||||
 | 
					        square: Square,
 | 
				
			||||||
        self.board.piece_on_square(square)
 | 
					        strategy: PlacePieceStrategy,
 | 
				
			||||||
 | 
					    ) -> Result<(), PlacePieceError> {
 | 
				
			||||||
 | 
					        self.board.place_piece(piece, square, strategy)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn get_piece(&self, square: Square) -> Option<Piece> {
 | 
				
			||||||
 | 
					        self.board.get_piece(square)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn remove_piece(&mut self, square: Square) -> Option<Piece> {
 | 
				
			||||||
 | 
					        self.board.remove_piece(square)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Position {
 | 
				
			||||||
 | 
					    pub fn sight(&self, square: Square) -> BitBoard {
 | 
				
			||||||
 | 
					        if let Some(piece) = self.get_piece(square) {
 | 
				
			||||||
 | 
					            piece.sight(square, &self.board)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            BitBoard::empty()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn movement(&self, square: Square) -> BitBoard {
 | 
				
			||||||
 | 
					        if let Some(piece) = self.get_piece(square) {
 | 
				
			||||||
 | 
					            piece.movement(square, &self.board)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            BitBoard::empty()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Position {
 | 
				
			||||||
 | 
					    pub fn active_sight(&self) -> BitBoard {
 | 
				
			||||||
 | 
					        self.friendly_sight(self.board.active_color)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// A [`BitBoard`] of all squares the given color can see.
 | 
				
			||||||
 | 
					    pub fn friendly_sight(&self, color: Color) -> BitBoard {
 | 
				
			||||||
 | 
					        // TODO: Probably want to implement a caching layer here.
 | 
				
			||||||
 | 
					        self.board
 | 
				
			||||||
 | 
					            .friendly_occupancy(color)
 | 
				
			||||||
 | 
					            .occupied_squares(&IterationDirection::default())
 | 
				
			||||||
 | 
					            .map(|square| self.sight(square))
 | 
				
			||||||
 | 
					            .fold(BitBoard::empty(), BitOr::bitor)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// A [`BitBoard`] of all squares visible by colors that oppose the given color.
 | 
				
			||||||
 | 
					    pub fn opposing_sight(&self) -> BitBoard {
 | 
				
			||||||
 | 
					        // TODO: Probably want to implement a caching layer here.
 | 
				
			||||||
 | 
					        let active_color = self.board.active_color;
 | 
				
			||||||
 | 
					        Color::ALL
 | 
				
			||||||
 | 
					            .into_iter()
 | 
				
			||||||
 | 
					            .filter_map(|c| {
 | 
				
			||||||
 | 
					                if c == active_color {
 | 
				
			||||||
 | 
					                    None
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Some(self.friendly_sight(c))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .fold(BitBoard::empty(), BitOr::bitor)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
 | 
				
			||||||
 | 
					pub enum CastleEvaluationError {
 | 
				
			||||||
 | 
					    #[error("{color} does not have the right to castle {wing}")]
 | 
				
			||||||
 | 
					    NoRights { color: Color, wing: Wing },
 | 
				
			||||||
 | 
					    #[error("no king")]
 | 
				
			||||||
 | 
					    NoKing,
 | 
				
			||||||
 | 
					    #[error("no rook")]
 | 
				
			||||||
 | 
					    NoRook,
 | 
				
			||||||
 | 
					    #[error("castling path is not clear")]
 | 
				
			||||||
 | 
					    ObstructingPieces,
 | 
				
			||||||
 | 
					    #[error("opposing pieces check castling path")]
 | 
				
			||||||
 | 
					    CheckingPieces,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Position {
 | 
				
			||||||
 | 
					    /// Evaluates whether the active color can castle toward the given wing of the board in the
 | 
				
			||||||
 | 
					    /// current position.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// ## Errors
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns an error indicating why the active color cannot castle.
 | 
				
			||||||
 | 
					    pub fn active_color_can_castle(&self, wing: Wing) -> Result<(), CastleEvaluationError> {
 | 
				
			||||||
 | 
					        // TODO: Cache this result. It's expensive!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let active_color = self.board.active_color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if !self
 | 
				
			||||||
 | 
					            .board
 | 
				
			||||||
 | 
					            .castling_rights
 | 
				
			||||||
 | 
					            .color_has_right(active_color, wing)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::NoRights {
 | 
				
			||||||
 | 
					                color: active_color,
 | 
				
			||||||
 | 
					                wing,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let parameters = self.board.castling_parameters(wing);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.castling_king(parameters.origin.king).is_none() {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::NoKing);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.castling_rook(parameters.origin.rook).is_none() {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::NoRook);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // All squares must be clear.
 | 
				
			||||||
 | 
					        let has_obstructing_pieces = (self.board.occupancy() & parameters.clear).is_populated();
 | 
				
			||||||
 | 
					        if has_obstructing_pieces {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::ObstructingPieces);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // King cannot pass through check.
 | 
				
			||||||
 | 
					        let opposing_sight = self.opposing_sight();
 | 
				
			||||||
 | 
					        let opposing_pieces_can_see_castling_path =
 | 
				
			||||||
 | 
					            (parameters.check & opposing_sight).is_populated();
 | 
				
			||||||
 | 
					        if opposing_pieces_can_see_castling_path {
 | 
				
			||||||
 | 
					            return Err(CastleEvaluationError::CheckingPieces);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn castling_king(&self, square: Square) -> Option<Piece> {
 | 
				
			||||||
 | 
					        self.get_piece(square).and_then(|piece| {
 | 
				
			||||||
 | 
					            if piece.color == self.board.active_color && piece.is_king() {
 | 
				
			||||||
 | 
					                Some(piece)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                None
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn castling_rook(&self, square: Square) -> Option<Piece> {
 | 
				
			||||||
 | 
					        self.get_piece(square).and_then(|piece| {
 | 
				
			||||||
 | 
					            if piece.color == self.board.active_color && piece.is_rook() {
 | 
				
			||||||
 | 
					                Some(piece)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                None
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					impl Position {
 | 
				
			||||||
    pub fn moves(&self) -> &Moves {
 | 
					    pub fn moves(&self) -> &Moves {
 | 
				
			||||||
        self.moves.get_or_init(|| {
 | 
					        self.moves.get_or_init(|| {
 | 
				
			||||||
            let player_to_move = self.player_to_move();
 | 
					            let player_to_move = self.player_to_move();
 | 
				
			||||||
| 
						 | 
					@ -120,11 +268,6 @@ impl Position {
 | 
				
			||||||
        self.moves().moves_for_piece(piece)
 | 
					        self.moves().moves_for_piece(piece)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[cfg(test)]
 | 
					 | 
				
			||||||
    pub(crate) fn sight_of_piece(&self, piece: &PlacedPiece) -> BitBoard {
 | 
					 | 
				
			||||||
        piece.sight(&self.board, self._en_passant_target_square())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[cfg(test)]
 | 
					    #[cfg(test)]
 | 
				
			||||||
    pub(crate) fn is_king_in_check(&self) -> bool {
 | 
					    pub(crate) fn is_king_in_check(&self) -> bool {
 | 
				
			||||||
        let danger_squares = self.king_danger(self.player_to_move());
 | 
					        let danger_squares = self.king_danger(self.player_to_move());
 | 
				
			||||||
| 
						 | 
					@ -213,9 +356,9 @@ impl fmt::Display for Position {
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
    use crate::{assert_eq_bitboards, position, test_position, Position};
 | 
					    use crate::{test_position, Position};
 | 
				
			||||||
    use chessfriend_bitboard::bitboard;
 | 
					    use chessfriend_bitboard::bitboard;
 | 
				
			||||||
    use chessfriend_core::piece;
 | 
					    use chessfriend_core::{piece, Wing};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn piece_on_square() {
 | 
					    fn piece_on_square() {
 | 
				
			||||||
| 
						 | 
					@ -223,22 +366,16 @@ mod tests {
 | 
				
			||||||
            Black Bishop on F7,
 | 
					            Black Bishop on F7,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let piece = pos.board.piece_on_square(Square::F7);
 | 
					        let piece = pos.board.get_piece(Square::F7);
 | 
				
			||||||
        assert_eq!(piece, Some(piece!(Black Bishop on F7)));
 | 
					        assert_eq!(piece, Some(piece!(Black Bishop)));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn piece_in_starting_position() {
 | 
					    fn piece_in_starting_position() {
 | 
				
			||||||
        let pos = test_position!(starting);
 | 
					        let pos = test_position!(starting);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(pos.board.get_piece(Square::H1), Some(piece!(White Rook)));
 | 
				
			||||||
            pos.board.piece_on_square(Square::H1),
 | 
					        assert_eq!(pos.board.get_piece(Square::A8), Some(piece!(Black Rook)));
 | 
				
			||||||
            Some(piece!(White Rook on H1))
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            pos.board.piece_on_square(Square::A8),
 | 
					 | 
				
			||||||
            Some(piece!(Black Rook on A8))
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
| 
						 | 
					@ -274,38 +411,160 @@ mod tests {
 | 
				
			||||||
            White Rook on H1
 | 
					            White Rook on H1
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert!(pos.player_can_castle(Color::White, Castle::KingSide));
 | 
					        let rights = pos.board.castling_rights;
 | 
				
			||||||
        assert!(pos.player_can_castle(Color::White, Castle::QueenSide));
 | 
					        assert!(rights.color_has_right(Color::White, Wing::KingSide));
 | 
				
			||||||
 | 
					        assert!(rights.color_has_right(Color::White, Wing::QueenSide));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn rook_for_castle() {
 | 
					    fn friendly_sight() {
 | 
				
			||||||
        let pos = position![
 | 
					        let pos = test_position!(
 | 
				
			||||||
 | 
					            White King on E4,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let sight = pos.active_sight();
 | 
				
			||||||
 | 
					        assert_eq!(sight, bitboard![E5 F5 F4 F3 E3 D3 D4 D5]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn opposing_sight() {
 | 
				
			||||||
 | 
					        let pos = test_position!(
 | 
				
			||||||
 | 
					            White King on E4,
 | 
				
			||||||
 | 
					            Black Rook on E7,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let sight = pos.opposing_sight();
 | 
				
			||||||
 | 
					        assert_eq!(sight, bitboard![A7 B7 C7 D7 F7 G7 H7 E8 E6 E5 E4]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn king_for_castle() {
 | 
				
			||||||
 | 
					        let pos = test_position![
 | 
				
			||||||
            White King on E1,
 | 
					            White King on E1,
 | 
				
			||||||
            White Rook on H1,
 | 
					            White Rook on H1,
 | 
				
			||||||
            White Rook on A1,
 | 
					            White Rook on A1,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let kingside_parameters = pos.board.castling_parameters(Wing::KingSide);
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(
 | 
				
			||||||
            pos.rook_for_castle(Color::White, Castle::KingSide),
 | 
					            pos.castling_king(kingside_parameters.origin.king),
 | 
				
			||||||
            Some(piece!(White Rook on H1))
 | 
					            Some(piece!(White King))
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let queenside_parameters = pos.board.castling_parameters(Wing::QueenSide);
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(
 | 
				
			||||||
            pos.rook_for_castle(Color::White, Castle::QueenSide),
 | 
					            pos.castling_king(queenside_parameters.origin.king),
 | 
				
			||||||
            Some(piece!(White Rook on A1))
 | 
					            Some(piece!(White King))
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn danger_squares() {
 | 
					    fn rook_for_castle() {
 | 
				
			||||||
        let pos = test_position!(Black, [
 | 
					        let pos = test_position![
 | 
				
			||||||
            White King on E1,
 | 
					            White King on E1,
 | 
				
			||||||
            Black King on E7,
 | 
					            White Rook on H1,
 | 
				
			||||||
            White Rook on E4,
 | 
					        ];
 | 
				
			||||||
        ]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let danger_squares = pos.king_danger(Color::Black);
 | 
					        let kingside_parameters = pos.board.castling_parameters(Wing::KingSide);
 | 
				
			||||||
        let expected = bitboard![D1 F1 D2 E2 F2 E3 A4 B4 C4 D4 F4 G4 H4 E5 E6 E7 E8];
 | 
					        assert_eq!(
 | 
				
			||||||
        assert_eq_bitboards!(danger_squares, expected);
 | 
					            pos.castling_rook(kingside_parameters.origin.rook),
 | 
				
			||||||
 | 
					            Some(piece!(White Rook))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pos = test_position![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let queenside_parameters = pos.board.castling_parameters(Wing::QueenSide);
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.castling_rook(queenside_parameters.origin.rook),
 | 
				
			||||||
 | 
					            Some(piece!(White Rook))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_can_castle() {
 | 
				
			||||||
 | 
					        let pos = test_position![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(pos.active_color_can_castle(Wing::KingSide), Ok(()));
 | 
				
			||||||
 | 
					        assert_eq!(pos.active_color_can_castle(Wing::QueenSide), Ok(()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_cannot_castle_missing_king() {
 | 
				
			||||||
 | 
					        let pos = test_position![
 | 
				
			||||||
 | 
					            White King on E2,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::KingSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::NoKing)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::QueenSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::NoKing)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_cannot_castle_missing_rook() {
 | 
				
			||||||
 | 
					        let pos = test_position![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::KingSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::NoRook)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pos = test_position![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::QueenSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::NoRook)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_cannot_castle_obstructing_piece() {
 | 
				
			||||||
 | 
					        let pos = test_position![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Bishop on F1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::KingSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::ObstructingPieces)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(pos.active_color_can_castle(Wing::QueenSide), Ok(()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn white_cannot_castle_checking_pieces() {
 | 
				
			||||||
 | 
					        let pos = test_position![
 | 
				
			||||||
 | 
					            White King on E1,
 | 
				
			||||||
 | 
					            White Rook on H1,
 | 
				
			||||||
 | 
					            White Rook on A1,
 | 
				
			||||||
 | 
					            Black Queen on C6,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(pos.active_color_can_castle(Wing::KingSide), Ok(()));
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            pos.active_color_can_castle(Wing::QueenSide),
 | 
				
			||||||
 | 
					            Err(CastleEvaluationError::CheckingPieces)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
// Eryn Wells <eryn@erynwells.me>
 | 
					// Eryn Wells <eryn@erynwells.me>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::MakeMoveError;
 | 
					use crate::position::MakeMoveError;
 | 
				
			||||||
use chessfriend_moves::BuildMoveError;
 | 
					use chessfriend_moves::BuildMoveError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[macro_export]
 | 
					#[macro_export]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue