WIP
This commit is contained in:
parent
d5cdf273c8
commit
091cc99cb3
42 changed files with 805 additions and 1662 deletions
|
|
@ -1,7 +1,5 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
mod move_builder;
|
||||
mod position_builder;
|
||||
|
||||
pub use move_builder::{Builder as MoveBuilder, MakeMoveError};
|
||||
pub use position_builder::Builder as PositionBuilder;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::{position::flags::Flags, Position};
|
||||
use crate::Position;
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_board::{castle, castle::Castle, en_passant::EnPassant};
|
||||
use chessfriend_core::{Color, Direction, Piece, PlacedPiece, Shape, Square};
|
||||
use chessfriend_moves::{Castle, EnPassant, Move};
|
||||
use chessfriend_moves::Move;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum MakeMoveError {
|
||||
|
|
@ -33,15 +34,15 @@ pub enum ValidatedMove {
|
|||
moving_piece: PlacedPiece,
|
||||
captured_piece: Option<PlacedPiece>,
|
||||
promotion: Option<Shape>,
|
||||
flags: Flags,
|
||||
castling_rights: castle::Rights,
|
||||
en_passant: Option<EnPassant>,
|
||||
increment_ply: bool,
|
||||
should_increment_ply: bool,
|
||||
},
|
||||
Castle {
|
||||
castle: Castle,
|
||||
king: PlacedPiece,
|
||||
rook: PlacedPiece,
|
||||
flags: Flags,
|
||||
castling_rights: castle::Rights,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +50,7 @@ impl MoveToMake for NoMove {}
|
|||
impl MoveToMake for ValidatedMove {}
|
||||
|
||||
impl<'p> Builder<'p, NoMove> {
|
||||
pub fn new(position: &'p Position) -> Builder<'p, NoMove> {
|
||||
pub fn new(position: &'p Position) -> Self {
|
||||
Builder {
|
||||
position,
|
||||
move_to_make: NoMove,
|
||||
|
|
@ -66,6 +67,7 @@ where
|
|||
|
||||
let piece = self
|
||||
.position
|
||||
.board
|
||||
.piece_on_square(origin_square)
|
||||
.ok_or(MakeMoveError::NoPiece)?;
|
||||
|
||||
|
|
@ -101,12 +103,14 @@ where
|
|||
|
||||
Some(
|
||||
self.position
|
||||
.board
|
||||
.piece_on_square(capture_square)
|
||||
.ok_or(MakeMoveError::NoCapturedPiece)?,
|
||||
)
|
||||
} else if mv.is_capture() {
|
||||
Some(
|
||||
self.position
|
||||
.board
|
||||
.piece_on_square(target_square)
|
||||
.ok_or(MakeMoveError::NoCapturedPiece)?,
|
||||
)
|
||||
|
|
@ -117,15 +121,15 @@ where
|
|||
// TODO: Check whether the move is legal.
|
||||
|
||||
let piece_is_king = piece.is_king();
|
||||
let mut flags = self.position.flags().clone();
|
||||
let mut castling_rights = self.position.board.castling_rights;
|
||||
|
||||
if piece_is_king {
|
||||
flags.clear_player_has_right_to_castle_flag(player, Castle::KingSide);
|
||||
flags.clear_player_has_right_to_castle_flag(player, Castle::QueenSide);
|
||||
castling_rights.clear_player_has_right_to_castle_flag(player, Castle::KingSide);
|
||||
castling_rights.clear_player_has_right_to_castle_flag(player, Castle::QueenSide);
|
||||
} else if piece.is_kingside_rook() {
|
||||
flags.clear_player_has_right_to_castle_flag(player, Castle::KingSide);
|
||||
castling_rights.clear_player_has_right_to_castle_flag(player, Castle::KingSide);
|
||||
} else if piece.is_queenside_rook() {
|
||||
flags.clear_player_has_right_to_castle_flag(player, Castle::QueenSide);
|
||||
castling_rights.clear_player_has_right_to_castle_flag(player, Castle::QueenSide);
|
||||
}
|
||||
|
||||
if let Some(castle) = mv.castle() {
|
||||
|
|
@ -145,7 +149,7 @@ where
|
|||
castle,
|
||||
king: piece,
|
||||
rook,
|
||||
flags,
|
||||
castling_rights,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
|
|
@ -167,9 +171,9 @@ where
|
|||
moving_piece: piece,
|
||||
captured_piece,
|
||||
promotion: mv.promotion(),
|
||||
flags,
|
||||
castling_rights,
|
||||
en_passant,
|
||||
increment_ply: !(mv.is_capture() || piece.is_pawn()),
|
||||
should_increment_ply: !(mv.is_capture() || piece.is_pawn()),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
@ -180,8 +184,8 @@ impl<'p> Builder<'p, ValidatedMove> {
|
|||
pub fn build(&self) -> Position {
|
||||
let player = self.position.player_to_move();
|
||||
|
||||
let updated_move_number =
|
||||
self.position.move_number() + if player == Color::Black { 1 } else { 0 };
|
||||
let updated_move_number = self.position.board.move_counter.fullmove_number
|
||||
+ if player == Color::Black { 1 } else { 0 };
|
||||
|
||||
match self.move_to_make {
|
||||
ValidatedMove::RegularMove {
|
||||
|
|
@ -190,69 +194,57 @@ impl<'p> Builder<'p, ValidatedMove> {
|
|||
moving_piece,
|
||||
captured_piece,
|
||||
promotion,
|
||||
flags,
|
||||
castling_rights,
|
||||
en_passant,
|
||||
increment_ply,
|
||||
should_increment_ply: increment_ply,
|
||||
} => {
|
||||
let mut pieces = self.position.piece_bitboards().clone();
|
||||
let mut board = self.position.board.clone();
|
||||
|
||||
board.castling_rights = castling_rights;
|
||||
board.en_passant = en_passant;
|
||||
|
||||
if let Some(captured_piece) = captured_piece {
|
||||
pieces.remove_piece(&captured_piece);
|
||||
board.remove_piece_from_square(captured_piece.square());
|
||||
}
|
||||
|
||||
if let Some(promotion) = promotion {
|
||||
pieces.remove_piece(&moving_piece);
|
||||
let _ = pieces
|
||||
.place_piece(&PlacedPiece::new(Piece::new(player, promotion), to_square));
|
||||
board.remove_piece_from_square(moving_piece.square());
|
||||
board.place_piece_on_square(Piece::new(player, promotion), to_square);
|
||||
} else {
|
||||
pieces.move_piece(moving_piece.piece(), from_square, to_square);
|
||||
board.remove_piece_from_square(from_square);
|
||||
board.place_piece_on_square(moving_piece.piece(), to_square);
|
||||
}
|
||||
|
||||
let ply = if increment_ply {
|
||||
self.position.ply_counter() + 1
|
||||
self.position.board.move_counter.halfmove_number + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
Position::new(
|
||||
self.position.player_to_move().other(),
|
||||
flags,
|
||||
pieces,
|
||||
en_passant,
|
||||
ply,
|
||||
updated_move_number,
|
||||
)
|
||||
Position::new(board)
|
||||
}
|
||||
ValidatedMove::Castle {
|
||||
castle,
|
||||
king,
|
||||
rook,
|
||||
flags,
|
||||
castling_rights,
|
||||
} => {
|
||||
let mut pieces = self.position.piece_bitboards().clone();
|
||||
let mut board = self.position.board.clone();
|
||||
|
||||
board.castling_rights = castling_rights;
|
||||
|
||||
let next_active_color = board.move_counter.active_color.next();
|
||||
board.move_counter.active_color = next_active_color;
|
||||
|
||||
let parameters = castle.parameters(player);
|
||||
|
||||
let king_origin_square: BitBoard = king.square().into();
|
||||
let king_target_square: BitBoard = parameters.king_target_square().into();
|
||||
*pieces.bitboard_for_piece_mut(king.piece()) ^=
|
||||
king_origin_square | king_target_square;
|
||||
board.remove_piece_from_square(king.square());
|
||||
board.place_piece_on_square(king.piece(), parameters.king_target_square());
|
||||
|
||||
let rook_from: BitBoard = rook.square().into();
|
||||
let rook_to: BitBoard = parameters.rook_target_square().into();
|
||||
*pieces.bitboard_for_piece_mut(rook.piece()) ^= rook_from | rook_to;
|
||||
board.remove_piece_from_square(rook.square());
|
||||
board.place_piece_on_square(rook.piece(), parameters.rook_target_square());
|
||||
|
||||
*pieces.bitboard_for_color_mut(player) &=
|
||||
!(king_origin_square | rook_from) | (king_target_square | rook_to);
|
||||
|
||||
Position::new(
|
||||
player.other(),
|
||||
flags,
|
||||
pieces,
|
||||
None,
|
||||
self.position.ply_counter() + 1,
|
||||
updated_move_number,
|
||||
)
|
||||
Position::new(board)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -291,7 +283,7 @@ mod tests {
|
|||
println!("{}", &new_position);
|
||||
|
||||
assert_eq!(
|
||||
new_position.piece_on_square(Square::E3),
|
||||
new_position.board.piece_on_square(Square::E3),
|
||||
Some(piece!(White Pawn on E3))
|
||||
);
|
||||
|
||||
|
|
@ -308,16 +300,13 @@ mod tests {
|
|||
println!("{}", &new_position);
|
||||
|
||||
assert_eq!(
|
||||
new_position.piece_on_square(Square::E4),
|
||||
new_position.board.piece_on_square(Square::E4),
|
||||
Some(piece!(White Pawn on E4))
|
||||
);
|
||||
|
||||
let en_passant = new_position.en_passant();
|
||||
assert!(en_passant.is_some());
|
||||
assert_eq!(
|
||||
en_passant.as_ref().map(EnPassant::target_square),
|
||||
Some(Square::E3)
|
||||
);
|
||||
assert_eq!(en_passant.map(EnPassant::target_square), Some(Square::E3));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -339,11 +328,11 @@ mod tests {
|
|||
println!("{}", &new_position);
|
||||
|
||||
assert_eq!(
|
||||
new_position.piece_on_square(Square::G1),
|
||||
new_position.board.piece_on_square(Square::G1),
|
||||
Some(piece!(White King on G1))
|
||||
);
|
||||
assert_eq!(
|
||||
new_position.piece_on_square(Square::F1),
|
||||
new_position.board.piece_on_square(Square::F1),
|
||||
Some(piece!(White Rook on F1))
|
||||
);
|
||||
|
||||
|
|
@ -366,11 +355,11 @@ mod tests {
|
|||
println!("{en_passant_position}");
|
||||
|
||||
assert_eq!(
|
||||
en_passant_position.piece_on_square(Square::A5),
|
||||
en_passant_position.board.piece_on_square(Square::A5),
|
||||
Some(piece!(Black Pawn on A5))
|
||||
);
|
||||
assert_eq!(
|
||||
en_passant_position.piece_on_square(Square::B5),
|
||||
en_passant_position.board.piece_on_square(Square::B5),
|
||||
Some(piece!(White Pawn on B5))
|
||||
);
|
||||
|
||||
|
|
@ -382,10 +371,10 @@ mod tests {
|
|||
.build();
|
||||
println!("{en_passant_capture}");
|
||||
|
||||
assert_eq!(en_passant_capture.piece_on_square(Square::A5), None);
|
||||
assert_eq!(en_passant_capture.piece_on_square(Square::B5), None);
|
||||
assert_eq!(en_passant_capture.board.piece_on_square(Square::A5), None);
|
||||
assert_eq!(en_passant_capture.board.piece_on_square(Square::B5), None);
|
||||
assert_eq!(
|
||||
en_passant_capture.piece_on_square(Square::A6),
|
||||
en_passant_capture.board.piece_on_square(Square::A6),
|
||||
Some(piece!(White Pawn on A6))
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,194 +0,0 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::{
|
||||
position::{flags::Flags, piece_sets::PieceBitBoards},
|
||||
Position,
|
||||
};
|
||||
use chessfriend_core::{piece, Color, Piece, PlacedPiece, Rank, Shape, Square};
|
||||
use chessfriend_moves::{Castle, EnPassant};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Builder {
|
||||
player_to_move: Color,
|
||||
flags: Flags,
|
||||
pieces: BTreeMap<Square, Piece>,
|
||||
kings: [Option<Square>; 2],
|
||||
en_passant: Option<EnPassant>,
|
||||
ply_counter: u16,
|
||||
move_number: u16,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub(crate) fn empty() -> Self {
|
||||
Self {
|
||||
player_to_move: Color::default(),
|
||||
flags: Flags::default(),
|
||||
pieces: BTreeMap::default(),
|
||||
kings: [None, None],
|
||||
en_passant: None,
|
||||
ply_counter: 0,
|
||||
move_number: 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_position(position: &Position) -> Self {
|
||||
let pieces = BTreeMap::from_iter(
|
||||
position
|
||||
.pieces(Color::White)
|
||||
.chain(position.pieces(Color::Black))
|
||||
.map(|placed_piece| (placed_piece.square(), *placed_piece.piece())),
|
||||
);
|
||||
|
||||
let white_king = position.king_square(Color::White);
|
||||
let black_king = position.king_square(Color::Black);
|
||||
|
||||
Self {
|
||||
player_to_move: position.player_to_move(),
|
||||
flags: position.flags(),
|
||||
pieces,
|
||||
kings: [Some(white_king), Some(black_king)],
|
||||
en_passant: position.en_passant(),
|
||||
ply_counter: position.ply_counter(),
|
||||
move_number: position.move_number(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_move(&mut self, player: Color) -> &mut Self {
|
||||
self.player_to_move = player;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ply_counter(&mut self, num: u16) -> &mut Self {
|
||||
self.ply_counter = num;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn move_number(&mut self, num: u16) -> &mut Self {
|
||||
self.move_number = num;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn en_passant(&mut self, en_passant: Option<EnPassant>) -> &mut Self {
|
||||
self.en_passant = en_passant;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn place_piece(&mut self, piece: PlacedPiece) -> &mut Self {
|
||||
let square = piece.square();
|
||||
let shape = piece.shape();
|
||||
|
||||
if shape == Shape::King {
|
||||
let color = piece.color();
|
||||
let color_index: usize = color as usize;
|
||||
|
||||
if let Some(king_square) = self.kings[color_index] {
|
||||
self.pieces.remove(&king_square);
|
||||
}
|
||||
self.kings[color_index] = Some(square);
|
||||
}
|
||||
|
||||
self.pieces.insert(square, *piece.piece());
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn player_can_castle(&mut self, color: Color, castle: Castle) -> &mut Self {
|
||||
self.flags
|
||||
.set_player_has_right_to_castle_flag(color, castle);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn no_castling_rights(&mut self) -> &mut Self {
|
||||
self.flags.clear_all_castling_rights();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Position {
|
||||
let pieces = PieceBitBoards::from_iter(
|
||||
self.pieces
|
||||
.iter()
|
||||
.map(PlacedPiece::from)
|
||||
.filter(Self::is_piece_placement_valid),
|
||||
);
|
||||
|
||||
let mut flags = self.flags;
|
||||
|
||||
for color in Color::ALL {
|
||||
for castle in Castle::ALL {
|
||||
let parameters = castle.parameters(color);
|
||||
let has_rook_on_starting_square = self
|
||||
.pieces
|
||||
.get(¶meters.rook_origin_square())
|
||||
.is_some_and(|piece| piece.shape() == Shape::Rook);
|
||||
let king_is_on_starting_square =
|
||||
self.kings[color as usize] == Some(parameters.king_origin_square());
|
||||
|
||||
if !king_is_on_starting_square || !has_rook_on_starting_square {
|
||||
flags.clear_player_has_right_to_castle_flag(color, castle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Position::new(
|
||||
self.player_to_move,
|
||||
flags,
|
||||
pieces,
|
||||
self.en_passant,
|
||||
self.ply_counter,
|
||||
self.move_number,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
fn is_piece_placement_valid(piece: &PlacedPiece) -> bool {
|
||||
if piece.shape() == Shape::Pawn {
|
||||
// Pawns cannot be placed on the first (back) rank of their side,
|
||||
// and cannot be placed on the final rank without a promotion.
|
||||
let rank = piece.square().rank();
|
||||
return rank != Rank::ONE && rank != Rank::EIGHT;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
let white_king_square = Square::E1;
|
||||
let black_king_square = Square::E8;
|
||||
|
||||
let pieces = BTreeMap::from_iter([
|
||||
(white_king_square, piece!(White King)),
|
||||
(black_king_square, piece!(Black King)),
|
||||
]);
|
||||
|
||||
Self {
|
||||
player_to_move: Color::White,
|
||||
flags: Flags::default(),
|
||||
pieces: pieces,
|
||||
kings: [Some(white_king_square), Some(black_king_square)],
|
||||
en_passant: None,
|
||||
ply_counter: 0,
|
||||
move_number: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::PositionBuilder;
|
||||
use chessfriend_core::piece;
|
||||
|
||||
#[test]
|
||||
fn place_piece() {
|
||||
let piece = piece!(White Queen on E4);
|
||||
let builder = PositionBuilder::new().place_piece(piece).build();
|
||||
assert_eq!(builder.piece_on_square(piece.square()), Some(piece));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::Position;
|
||||
use chessfriend_core::{File, Rank, Square};
|
||||
use std::fmt;
|
||||
|
||||
pub struct DiagramFormatter<'a>(&'a Position);
|
||||
|
||||
impl<'a> DiagramFormatter<'a> {
|
||||
pub fn new(position: &'a Position) -> DiagramFormatter {
|
||||
DiagramFormatter(position)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for DiagramFormatter<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, " ╔═════════════════╗\n")?;
|
||||
|
||||
for rank in Rank::ALL.iter().rev() {
|
||||
write!(f, "{rank} ║ ")?;
|
||||
|
||||
for file in File::ALL.iter() {
|
||||
let square = Square::from_file_rank(*file, *rank);
|
||||
match self.0.piece_on_square(square) {
|
||||
Some(placed_piece) => write!(f, "{} ", placed_piece.piece())?,
|
||||
None => write!(f, "· ")?,
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "║\n")?;
|
||||
}
|
||||
|
||||
write!(f, " ╚═════════════════╝\n")?;
|
||||
write!(f, " a b c d e f g h\n")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{position, Position};
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn empty() {
|
||||
let pos = Position::empty();
|
||||
let diagram = DiagramFormatter(&pos);
|
||||
println!("{}", diagram);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn one_king() {
|
||||
let pos = position![Black King on H3];
|
||||
let diagram = DiagramFormatter(&pos);
|
||||
println!("{}", diagram);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn starting() {
|
||||
let pos = Position::starting();
|
||||
let diagram = DiagramFormatter(&pos);
|
||||
println!("{}", diagram);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use chessfriend_core::Color;
|
||||
use chessfriend_moves::Castle;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub struct Flags(u8);
|
||||
|
||||
impl Flags {
|
||||
#[inline]
|
||||
pub(super) fn player_has_right_to_castle_flag_offset(color: Color, castle: Castle) -> usize {
|
||||
((color as usize) << 1) + castle as usize
|
||||
}
|
||||
|
||||
pub(super) fn player_has_right_to_castle(&self, color: Color, castle: Castle) -> bool {
|
||||
(self.0 & (1 << Self::player_has_right_to_castle_flag_offset(color, castle))) != 0
|
||||
}
|
||||
|
||||
pub(super) fn set_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) {
|
||||
self.0 |= 1 << Self::player_has_right_to_castle_flag_offset(color, castle);
|
||||
}
|
||||
|
||||
pub(super) fn clear_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) {
|
||||
self.0 &= !(1 << Self::player_has_right_to_castle_flag_offset(color, castle));
|
||||
}
|
||||
|
||||
pub(super) fn clear_all_castling_rights(&mut self) {
|
||||
self.0 &= 0b11111100;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Flags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Flags({:08b})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Flags {
|
||||
fn default() -> Self {
|
||||
Flags(0b00001111)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn castle_flags() {
|
||||
assert_eq!(
|
||||
Flags::player_has_right_to_castle_flag_offset(Color::White, Castle::KingSide),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
Flags::player_has_right_to_castle_flag_offset(Color::White, Castle::QueenSide),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
Flags::player_has_right_to_castle_flag_offset(Color::Black, Castle::KingSide),
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
Flags::player_has_right_to_castle_flag_offset(Color::Black, Castle::QueenSide),
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn defaults() {
|
||||
let mut flags: Flags = Default::default();
|
||||
assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide));
|
||||
assert!(flags.player_has_right_to_castle(Color::White, Castle::QueenSide));
|
||||
assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide));
|
||||
assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide));
|
||||
|
||||
flags.clear_player_has_right_to_castle_flag(Color::White, Castle::QueenSide);
|
||||
assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide));
|
||||
assert!(!flags.player_has_right_to_castle(Color::White, Castle::QueenSide));
|
||||
assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide));
|
||||
assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide));
|
||||
|
||||
flags.set_player_has_right_to_castle_flag(Color::White, Castle::QueenSide);
|
||||
assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide));
|
||||
assert!(flags.player_has_right_to_castle(Color::White, Castle::QueenSide));
|
||||
assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide));
|
||||
assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,9 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
pub mod piece_sets;
|
||||
|
||||
mod builders;
|
||||
mod diagram_formatter;
|
||||
mod flags;
|
||||
mod pieces;
|
||||
mod position;
|
||||
|
||||
pub use {
|
||||
builders::{MakeMoveError, MoveBuilder, PositionBuilder},
|
||||
diagram_formatter::DiagramFormatter,
|
||||
pieces::Pieces,
|
||||
builders::{MakeMoveError, MoveBuilder},
|
||||
position::Position,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_core::{Color, Piece, PlacedPiece, Square};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum PlacePieceStrategy {
|
||||
Replace,
|
||||
PreserveExisting,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum PlacePieceError {
|
||||
ExisitingPiece,
|
||||
}
|
||||
|
||||
impl Default for PlacePieceStrategy {
|
||||
fn default() -> Self {
|
||||
Self::Replace
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub(crate) struct PieceBitBoards {
|
||||
by_color: ByColor,
|
||||
by_color_and_shape: ByColorAndShape,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
struct ByColor(BitBoard, [BitBoard; 2]);
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
struct ByColorAndShape([[BitBoard; 6]; 2]);
|
||||
|
||||
impl PieceBitBoards {
|
||||
pub(super) fn new(pieces: [[BitBoard; 6]; 2]) -> Self {
|
||||
use std::ops::BitOr;
|
||||
|
||||
let white_pieces = pieces[Color::White as usize]
|
||||
.iter()
|
||||
.fold(BitBoard::empty(), BitOr::bitor);
|
||||
let black_pieces = pieces[Color::Black as usize]
|
||||
.iter()
|
||||
.fold(BitBoard::empty(), BitOr::bitor);
|
||||
|
||||
let all_pieces = white_pieces | black_pieces;
|
||||
|
||||
Self {
|
||||
by_color: ByColor(all_pieces, [white_pieces, black_pieces]),
|
||||
by_color_and_shape: ByColorAndShape(pieces),
|
||||
}
|
||||
}
|
||||
|
||||
/// A BitBoard representing all the pieces currently on the board. Other
|
||||
/// engines might refer to this concept as 'occupancy'.
|
||||
pub(crate) fn all_pieces(&self) -> &BitBoard {
|
||||
self.by_color.all()
|
||||
}
|
||||
|
||||
pub(crate) fn all_pieces_of_color(&self, color: Color) -> &BitBoard {
|
||||
self.by_color.bitboard(color)
|
||||
}
|
||||
|
||||
pub(super) fn bitboard_for_color(&self, color: Color) -> &BitBoard {
|
||||
self.by_color.bitboard(color)
|
||||
}
|
||||
|
||||
pub(super) fn bitboard_for_color_mut(&mut self, color: Color) -> &mut BitBoard {
|
||||
self.by_color.bitboard_mut(color)
|
||||
}
|
||||
|
||||
pub(super) fn bitboard_for_piece(&self, piece: &Piece) -> &BitBoard {
|
||||
self.by_color_and_shape.bitboard_for_piece(piece)
|
||||
}
|
||||
|
||||
pub(super) fn bitboard_for_piece_mut(&mut self, piece: &Piece) -> &mut BitBoard {
|
||||
self.by_color_and_shape.bitboard_for_piece_mut(piece)
|
||||
}
|
||||
|
||||
pub(super) fn place_piece(&mut self, piece: &PlacedPiece) -> Result<(), PlacePieceError> {
|
||||
self.place_piece_with_strategy(piece, PlacePieceStrategy::default())
|
||||
}
|
||||
|
||||
pub(super) fn place_piece_with_strategy(
|
||||
&mut self,
|
||||
piece: &PlacedPiece,
|
||||
strategy: PlacePieceStrategy,
|
||||
) -> Result<(), PlacePieceError> {
|
||||
let color = piece.color();
|
||||
let square = piece.square();
|
||||
|
||||
if strategy == PlacePieceStrategy::PreserveExisting
|
||||
&& self.by_color.bitboard(color).is_set(piece.square())
|
||||
{
|
||||
return Err(PlacePieceError::ExisitingPiece);
|
||||
}
|
||||
|
||||
self.by_color_and_shape.set_square(square, piece.piece());
|
||||
self.by_color.set_square(square, color);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn remove_piece(&mut self, piece: &PlacedPiece) {
|
||||
let color = piece.color();
|
||||
let square = piece.square();
|
||||
|
||||
self.by_color_and_shape.clear_square(square, piece.piece());
|
||||
self.by_color.clear_square(square, color);
|
||||
}
|
||||
|
||||
pub(super) fn move_piece(&mut self, piece: &Piece, from_square: Square, to_square: Square) {
|
||||
let color = piece.color();
|
||||
|
||||
self.by_color_and_shape.clear_square(from_square, piece);
|
||||
self.by_color.clear_square(from_square, color);
|
||||
self.by_color_and_shape.set_square(to_square, piece);
|
||||
self.by_color.set_square(to_square, color);
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<PlacedPiece> for PieceBitBoards {
|
||||
fn from_iter<T: IntoIterator<Item = PlacedPiece>>(iter: T) -> Self {
|
||||
let mut pieces: Self = Default::default();
|
||||
|
||||
for piece in iter {
|
||||
let _ = pieces.place_piece(&piece);
|
||||
}
|
||||
|
||||
pieces
|
||||
}
|
||||
}
|
||||
|
||||
impl ByColor {
|
||||
fn all(&self) -> &BitBoard {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub(super) fn bitboard(&self, color: Color) -> &BitBoard {
|
||||
&self.1[color as usize]
|
||||
}
|
||||
|
||||
pub(super) fn bitboard_mut(&mut self, color: Color) -> &mut BitBoard {
|
||||
&mut self.1[color as usize]
|
||||
}
|
||||
|
||||
fn set_square(&mut self, square: Square, color: Color) {
|
||||
self.0.set_square(square);
|
||||
self.1[color as usize].set_square(square)
|
||||
}
|
||||
|
||||
fn clear_square(&mut self, square: Square, color: Color) {
|
||||
self.0.clear_square(square);
|
||||
self.1[color as usize].clear_square(square);
|
||||
}
|
||||
}
|
||||
|
||||
impl ByColorAndShape {
|
||||
fn bitboard_for_piece(&self, piece: &Piece) -> &BitBoard {
|
||||
&self.0[piece.color() as usize][piece.shape() as usize]
|
||||
}
|
||||
|
||||
fn bitboard_for_piece_mut(&mut self, piece: &Piece) -> &mut BitBoard {
|
||||
&mut self.0[piece.color() as usize][piece.shape() as usize]
|
||||
}
|
||||
|
||||
fn set_square(&mut self, square: Square, piece: &Piece) {
|
||||
self.bitboard_for_piece_mut(piece).set_square(square);
|
||||
}
|
||||
|
||||
fn clear_square(&mut self, square: Square, piece: &Piece) {
|
||||
self.bitboard_for_piece_mut(piece).clear_square(square);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use super::Position;
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
|
||||
|
||||
pub struct Pieces<'a> {
|
||||
color: Color,
|
||||
position: &'a Position,
|
||||
|
||||
current_shape: Option<Shape>,
|
||||
|
||||
shape_iterator: Box<dyn Iterator<Item = &'static Shape>>,
|
||||
square_iterator: Option<Box<dyn Iterator<Item = Square>>>,
|
||||
}
|
||||
|
||||
impl<'a> Pieces<'a> {
|
||||
pub(crate) fn new(position: &Position, color: Color) -> Pieces {
|
||||
Pieces {
|
||||
color,
|
||||
position,
|
||||
current_shape: None,
|
||||
shape_iterator: Box::new(Shape::iter()),
|
||||
square_iterator: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Pieces<'a> {
|
||||
type Item = PlacedPiece;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(square_iterator) = &mut self.square_iterator {
|
||||
if let (Some(square), Some(shape)) = (square_iterator.next(), self.current_shape) {
|
||||
return Some(PlacedPiece::new(Piece::new(self.color, shape), square));
|
||||
}
|
||||
}
|
||||
|
||||
let mut current_shape: Option<Shape> = None;
|
||||
let mut next_nonempty_bitboard: Option<&BitBoard> = None;
|
||||
|
||||
while let Some(shape) = self.shape_iterator.next() {
|
||||
let piece = Piece::new(self.color, *shape);
|
||||
|
||||
let bitboard = self.position.bitboard_for_piece(piece);
|
||||
if bitboard.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
next_nonempty_bitboard = Some(bitboard);
|
||||
current_shape = Some(*shape);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if let (Some(bitboard), Some(shape)) = (next_nonempty_bitboard, current_shape) {
|
||||
let mut square_iterator = bitboard.occupied_squares();
|
||||
|
||||
let mut next_placed_piece: Option<PlacedPiece> = None;
|
||||
if let Some(square) = square_iterator.next() {
|
||||
next_placed_piece = Some(PlacedPiece::new(Piece::new(self.color, shape), square));
|
||||
}
|
||||
|
||||
self.square_iterator = Some(Box::new(square_iterator));
|
||||
self.current_shape = Some(shape);
|
||||
|
||||
return next_placed_piece;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Position, PositionBuilder};
|
||||
use chessfriend_core::{piece, Color};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let pos = Position::empty();
|
||||
let mut pieces = pos.pieces(Color::White);
|
||||
assert_eq!(pieces.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one() {
|
||||
let pos = PositionBuilder::new()
|
||||
.place_piece(piece!(White Queen on E4))
|
||||
.build();
|
||||
println!("{:#?}", &pos);
|
||||
|
||||
let mut pieces = pos.pieces(Color::White);
|
||||
assert_eq!(pieces.next(), Some(piece!(White Queen on E4)));
|
||||
assert_eq!(pieces.next(), Some(piece!(White King on E1)));
|
||||
assert_eq!(pieces.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_pieces() {
|
||||
let pos = PositionBuilder::new()
|
||||
.place_piece(piece!(White Queen on E4))
|
||||
.place_piece(piece!(White King on A1))
|
||||
.place_piece(piece!(White Pawn on B2))
|
||||
.place_piece(piece!(White Pawn on C2))
|
||||
.build();
|
||||
println!("{}", crate::position::DiagramFormatter::new(&pos));
|
||||
|
||||
let expected_placed_pieces = HashSet::from([
|
||||
piece!(White Queen on E4),
|
||||
piece!(White King on A1),
|
||||
piece!(White Pawn on B2),
|
||||
piece!(White Pawn on C2),
|
||||
]);
|
||||
|
||||
let placed_pieces = HashSet::from_iter(pos.pieces(Color::White));
|
||||
|
||||
assert_eq!(
|
||||
placed_pieces,
|
||||
expected_placed_pieces,
|
||||
"{:#?}",
|
||||
placed_pieces
|
||||
.symmetric_difference(&expected_placed_pieces)
|
||||
.into_iter()
|
||||
.map(|pp| format!("{}", pp))
|
||||
.collect::<Vec<String>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,112 +1,45 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use super::{flags::Flags, piece_sets::PieceBitBoards, Pieces};
|
||||
use crate::{
|
||||
check::CheckingPieces,
|
||||
move_generator::{MoveSet, Moves},
|
||||
position::DiagramFormatter,
|
||||
sight::SightExt,
|
||||
};
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_board::{castle::Castle, display::DiagramFormatter, en_passant::EnPassant, Board};
|
||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
|
||||
use chessfriend_moves::{Castle, EnPassant};
|
||||
use chessfriend_moves::Move;
|
||||
use std::{cell::OnceCell, fmt};
|
||||
|
||||
#[derive(Clone, Debug, Eq)]
|
||||
pub struct Position {
|
||||
color_to_move: Color,
|
||||
flags: Flags,
|
||||
pieces: PieceBitBoards,
|
||||
en_passant: Option<EnPassant>,
|
||||
pub board: Board,
|
||||
moves: OnceCell<Moves>,
|
||||
half_move_counter: u16,
|
||||
full_move_number: u16,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn empty() -> Position {
|
||||
pub fn empty() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Return a starting position.
|
||||
pub fn starting() -> Self {
|
||||
const BLACK_PIECES: [BitBoard; 6] = [
|
||||
BitBoard::new(0b0000000011111111 << 48),
|
||||
BitBoard::new(0b0100001000000000 << 48),
|
||||
BitBoard::new(0b0010010000000000 << 48),
|
||||
BitBoard::new(0b1000000100000000 << 48),
|
||||
BitBoard::new(0b0000100000000000 << 48),
|
||||
BitBoard::new(0b0001000000000000 << 48),
|
||||
];
|
||||
|
||||
const WHITE_PIECES: [BitBoard; 6] = [
|
||||
BitBoard::new(0b1111111100000000),
|
||||
BitBoard::new(0b0000000001000010),
|
||||
BitBoard::new(0b0000000000100100),
|
||||
BitBoard::new(0b0000000010000001),
|
||||
BitBoard::new(0b0000000000001000),
|
||||
BitBoard::new(0b0000000000010000),
|
||||
];
|
||||
|
||||
Self {
|
||||
color_to_move: Color::White,
|
||||
pieces: PieceBitBoards::new([WHITE_PIECES, BLACK_PIECES]),
|
||||
board: Board::starting(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn player_to_move(&self) -> Color {
|
||||
self.color_to_move
|
||||
}
|
||||
|
||||
pub fn move_number(&self) -> u16 {
|
||||
self.full_move_number
|
||||
}
|
||||
|
||||
pub fn ply_counter(&self) -> u16 {
|
||||
self.half_move_counter
|
||||
}
|
||||
|
||||
/// Returns true if the player has the right to castle on the given side of
|
||||
/// the board.
|
||||
///
|
||||
/// The right to castle on a particular side of the board is retained as
|
||||
/// long as the player has not moved their king, or the rook on that side of
|
||||
/// the board.
|
||||
pub(crate) fn player_has_right_to_castle(&self, color: Color, castle: Castle) -> bool {
|
||||
self.flags.player_has_right_to_castle(color, castle)
|
||||
}
|
||||
|
||||
/// Returns `true` if the player is able to castle on the given side of the board.
|
||||
///
|
||||
/// The following requirements must be met:
|
||||
///
|
||||
/// 1. The player must still have the right to castle on that side of the
|
||||
/// board. The king and rook involved in the castle must not have moved.
|
||||
/// 1. The spaces between the king and rook must be clear
|
||||
/// 2. The king must not be in check
|
||||
/// 3. In the course of castling on that side, the king must not pass
|
||||
/// through a square that an enemy piece can see
|
||||
pub(crate) fn player_can_castle(&self, player: Color, castle: Castle) -> bool {
|
||||
if !self.player_has_right_to_castle(player, castle.into()) {
|
||||
return false;
|
||||
pub fn new(board: Board) -> Self {
|
||||
Self {
|
||||
board,
|
||||
..Default::default()
|
||||
}
|
||||
|
||||
let castling_parameters = castle.parameters(player);
|
||||
|
||||
let all_pieces = self.occupied_squares();
|
||||
if !(all_pieces & castling_parameters.clear_squares()).is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let danger_squares = self.king_danger(player);
|
||||
if !(danger_squares & castling_parameters.check_squares()).is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl Position {
|
||||
/// Return a PlacedPiece representing the rook to use for a castling move.
|
||||
pub(crate) fn rook_for_castle(&self, player: Color, castle: Castle) -> Option<PlacedPiece> {
|
||||
let square = match (player, castle) {
|
||||
|
|
@ -116,103 +49,68 @@ impl Position {
|
|||
(Color::Black, Castle::QueenSide) => Square::A8,
|
||||
};
|
||||
|
||||
self.piece_on_square(square)
|
||||
self.board.piece_on_square(square)
|
||||
}
|
||||
|
||||
pub fn moves(&self) -> &Moves {
|
||||
self.moves.get_or_init(|| {
|
||||
let player_to_move = self.player_to_move();
|
||||
let checking_pieces = self.checking_pieces();
|
||||
match checking_pieces.count() {
|
||||
// Normal, unrestricted move generation
|
||||
0 => Moves::new(self, self.color_to_move, BitBoard::FULL, BitBoard::FULL),
|
||||
0 => Moves::new(
|
||||
&self.board,
|
||||
player_to_move,
|
||||
BitBoard::full(),
|
||||
BitBoard::full(),
|
||||
),
|
||||
1 => {
|
||||
// Calculate push and capture masks for checking piece. Moves are restricted to those that intersect those masks.
|
||||
// Calculate push and capture masks for checking piece. Moves are restricted to
|
||||
// those that intersect those masks.
|
||||
let capture_mask = checking_pieces.capture_mask();
|
||||
let push_mask =
|
||||
checking_pieces.push_mask(self.king_bitboard(self.color_to_move));
|
||||
Moves::new(self, self.color_to_move, capture_mask, push_mask)
|
||||
let push_mask = checking_pieces.push_mask(self.king_bitboard(player_to_move));
|
||||
Moves::new(&self.board, player_to_move, capture_mask, push_mask)
|
||||
}
|
||||
// With more than one checking piece, the only legal moves are king moves.
|
||||
_ => Moves::new(self, self.color_to_move, BitBoard::EMPTY, BitBoard::EMPTY),
|
||||
_ => Moves::new(
|
||||
&self.board,
|
||||
player_to_move,
|
||||
BitBoard::empty(),
|
||||
BitBoard::empty(),
|
||||
),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// A [BitBoard] representing the set of squares containing a piece.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn occupied_squares(&self) -> &BitBoard {
|
||||
&self.pieces.all_pieces()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn friendly_pieces(&self) -> &BitBoard {
|
||||
self.pieces.all_pieces_of_color(self.color_to_move)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn opposing_pieces(&self) -> &BitBoard {
|
||||
self.pieces.all_pieces_of_color(self.color_to_move.other())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn all_pieces(&self) -> (&BitBoard, &BitBoard) {
|
||||
(self.friendly_pieces(), self.opposing_pieces())
|
||||
}
|
||||
|
||||
/// Return a BitBoard representing the set of squares containing a piece.
|
||||
/// This set is the inverse of `occupied_squares`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn empty_squares(&self) -> BitBoard {
|
||||
!self.occupied_squares()
|
||||
}
|
||||
|
||||
pub fn piece_on_square(&self, sq: Square) -> Option<PlacedPiece> {
|
||||
for color in Color::iter() {
|
||||
for shape in Shape::iter() {
|
||||
let piece = Piece::new(*color, *shape);
|
||||
if self.pieces.bitboard_for_piece(&piece).is_set(sq) {
|
||||
return Some(PlacedPiece::new(piece, sq));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn pieces(&self, color: Color) -> Pieces {
|
||||
Pieces::new(&self, color)
|
||||
}
|
||||
|
||||
pub fn has_en_passant_square(&self) -> bool {
|
||||
self.en_passant.is_some()
|
||||
self.board.en_passant().is_some()
|
||||
}
|
||||
|
||||
pub fn en_passant(&self) -> Option<EnPassant> {
|
||||
self.en_passant
|
||||
self.board.en_passant()
|
||||
}
|
||||
|
||||
fn _sight_of_player(&self, player: Color, pieces: &PieceBitBoards) -> BitBoard {
|
||||
let en_passant_target_square = self.en_passant.map(|ep| ep.target_square());
|
||||
fn _en_passant_target_square(&self) -> Option<Square> {
|
||||
self.board.en_passant().map(EnPassant::target_square)
|
||||
}
|
||||
|
||||
fn _sight_of_player(&self, player: Color, board: &Board) -> BitBoard {
|
||||
let en_passant_target_square = self._en_passant_target_square();
|
||||
|
||||
Shape::ALL
|
||||
.iter()
|
||||
.filter_map(|&shape| {
|
||||
let piece = Piece::new(player, shape);
|
||||
let bitboard = pieces.bitboard_for_piece(&piece);
|
||||
let bitboard = board.bitboard_for_piece(piece);
|
||||
if !bitboard.is_empty() {
|
||||
Some((piece, bitboard))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flat_map(|(piece, &bitboard)| {
|
||||
.flat_map(|(piece, bitboard)| {
|
||||
bitboard.occupied_squares().map(move |square| {
|
||||
PlacedPiece::new(piece, square).sight(pieces, en_passant_target_square)
|
||||
PlacedPiece::new(piece, square).sight(board, en_passant_target_square)
|
||||
})
|
||||
})
|
||||
.fold(BitBoard::empty(), |acc, sight| acc | sight)
|
||||
|
|
@ -224,52 +122,37 @@ impl Position {
|
|||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn sight_of_piece(&self, piece: &PlacedPiece) -> BitBoard {
|
||||
piece.sight(&self.pieces, self.en_passant.map(|ep| ep.target_square()))
|
||||
}
|
||||
|
||||
/// A bitboard representing the squares where a king of the given color will
|
||||
/// be in danger of being captured by the opposing player. If the king is on
|
||||
/// one of these squares, it is in check. The king cannot move to these
|
||||
/// squares.
|
||||
pub(crate) fn king_danger(&self, color: Color) -> BitBoard {
|
||||
let pieces_without_king = {
|
||||
let mut cloned_pieces = self.pieces.clone();
|
||||
let placed_king = PlacedPiece::new(Piece::king(color), self.king_square(color));
|
||||
cloned_pieces.remove_piece(&placed_king);
|
||||
|
||||
cloned_pieces
|
||||
};
|
||||
|
||||
self._sight_of_player(color.other(), &pieces_without_king)
|
||||
piece.sight(&self.board, self._en_passant_target_square())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn is_king_in_check(&self) -> bool {
|
||||
let danger_squares = self.king_danger(self.color_to_move);
|
||||
!(danger_squares & self.king_bitboard(self.color_to_move)).is_empty()
|
||||
let danger_squares = self.king_danger(self.player_to_move());
|
||||
!(danger_squares & self.king_bitboard(self.player_to_move())).is_empty()
|
||||
}
|
||||
|
||||
fn king_bitboard(&self, player: Color) -> &BitBoard {
|
||||
self.pieces.bitboard_for_piece(&Piece::king(player))
|
||||
fn king_bitboard(&self, player: Color) -> BitBoard {
|
||||
self.board.pieces.bitboard_for_piece(Piece::king(player))
|
||||
}
|
||||
|
||||
pub(crate) fn king_square(&self, player: Color) -> Square {
|
||||
self.king_bitboard(player)
|
||||
.occupied_squares()
|
||||
.occupied_squares(&IterationDirection::default())
|
||||
.next()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn checking_pieces(&self) -> CheckingPieces {
|
||||
let opponent = self.color_to_move.other();
|
||||
let king_square = self.king_square(self.color_to_move);
|
||||
let opponent = self.player_to_move().other();
|
||||
let king_square = self.king_square(self.player_to_move());
|
||||
|
||||
let checking_pawns = {
|
||||
// The current player's pawn attack moves *from* this square are the
|
||||
// same as the pawn moves for the opposing player attacking this square.
|
||||
let pawn_moves_to_king_square = BitBoard::pawn_attacks(king_square, self.color_to_move);
|
||||
let pawn_moves_to_king_square =
|
||||
BitBoard::pawn_attacks(king_square, self.player_to_move());
|
||||
let opposing_pawn = Piece::pawn(opponent);
|
||||
let opposing_pawns = self.pieces.bitboard_for_piece(&opposing_pawn);
|
||||
let opposing_pawns = self.board.bitboard_for_piece(opposing_pawn);
|
||||
|
||||
pawn_moves_to_king_square & opposing_pawns
|
||||
};
|
||||
|
|
@ -278,7 +161,7 @@ impl Position {
|
|||
($moves_bb_fn:path, $piece_fn:ident) => {{
|
||||
let moves_from_opposing_square = $moves_bb_fn(king_square);
|
||||
let piece = Piece::$piece_fn(opponent);
|
||||
let opposing_pieces = self.pieces.bitboard_for_piece(&piece);
|
||||
let opposing_pieces = self.board.bitboard_for_piece(piece);
|
||||
|
||||
moves_from_opposing_square & opposing_pieces
|
||||
}};
|
||||
|
|
@ -298,88 +181,49 @@ impl Position {
|
|||
)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// crate::position methods
|
||||
impl Position {
|
||||
pub(super) fn new(
|
||||
player_to_move: Color,
|
||||
flags: Flags,
|
||||
pieces: PieceBitBoards,
|
||||
en_passant: Option<EnPassant>,
|
||||
half_move_counter: u16,
|
||||
full_move_number: u16,
|
||||
) -> Self {
|
||||
Self {
|
||||
color_to_move: player_to_move,
|
||||
flags,
|
||||
en_passant,
|
||||
pieces,
|
||||
half_move_counter,
|
||||
full_move_number,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn flags(&self) -> Flags {
|
||||
self.flags
|
||||
}
|
||||
|
||||
pub(super) fn piece_bitboards(&self) -> &PieceBitBoards {
|
||||
&self.pieces
|
||||
pub fn display(&self) -> DiagramFormatter {
|
||||
self.board.display()
|
||||
}
|
||||
}
|
||||
|
||||
// crate methods
|
||||
impl Position {
|
||||
pub(crate) fn bitboard_for_color(&self, color: Color) -> &BitBoard {
|
||||
self.pieces.bitboard_for_color(color)
|
||||
pub fn make_move(&mut self, ply: &Move) -> Result<(), MakeMoveError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn bitboard_for_piece(&self, piece: Piece) -> &BitBoard {
|
||||
self.pieces.bitboard_for_piece(&piece)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Position {
|
||||
pub(crate) fn test_set_en_passant(&mut self, en_passant: EnPassant) {
|
||||
self.en_passant = Some(en_passant);
|
||||
pub fn unmake_move(&mut self, ply: &Move) -> Result<(), UnmakeMoveError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
color_to_move: Color::White,
|
||||
flags: Flags::default(),
|
||||
pieces: PieceBitBoards::default(),
|
||||
en_passant: None,
|
||||
board: Board::default(),
|
||||
moves: OnceCell::new(),
|
||||
half_move_counter: 0,
|
||||
full_move_number: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Position {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.pieces == other.pieces
|
||||
&& self.color_to_move == other.color_to_move
|
||||
&& self.flags == other.flags
|
||||
&& self.en_passant == other.en_passant
|
||||
self.board == other.board
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", DiagramFormatter::new(self))
|
||||
write!(f, "{}", self.board.display())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{assert_eq_bitboards, position, test_position, Position, PositionBuilder};
|
||||
use crate::{assert_eq_bitboards, position, test_position, Position};
|
||||
use chessfriend_bitboard::bitboard;
|
||||
use chessfriend_core::piece;
|
||||
|
||||
|
|
@ -389,7 +233,7 @@ mod tests {
|
|||
Black Bishop on F7,
|
||||
];
|
||||
|
||||
let piece = pos.piece_on_square(Square::F7);
|
||||
let piece = pos.board.piece_on_square(Square::F7);
|
||||
assert_eq!(piece, Some(piece!(Black Bishop on F7)));
|
||||
}
|
||||
|
||||
|
|
@ -398,11 +242,11 @@ mod tests {
|
|||
let pos = test_position!(starting);
|
||||
|
||||
assert_eq!(
|
||||
pos.piece_on_square(Square::H1),
|
||||
pos.board.piece_on_square(Square::H1),
|
||||
Some(piece!(White Rook on H1))
|
||||
);
|
||||
assert_eq!(
|
||||
pos.piece_on_square(Square::A8),
|
||||
pos.board.piece_on_square(Square::A8),
|
||||
Some(piece!(Black Rook on A8))
|
||||
);
|
||||
}
|
||||
|
|
@ -464,16 +308,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn danger_squares() {
|
||||
let pos = PositionBuilder::new()
|
||||
.place_piece(piece!(White King on E1))
|
||||
.place_piece(piece!(Black King on E7))
|
||||
.place_piece(piece!(White Rook on E4))
|
||||
.to_move(Color::Black)
|
||||
.build();
|
||||
let pos = test_position!(Black, [
|
||||
White King on E1,
|
||||
Black King on E7,
|
||||
White Rook on E4,
|
||||
]);
|
||||
|
||||
let danger_squares = pos.king_danger(Color::Black);
|
||||
let expected =
|
||||
bitboard![D1, F1, D2, E2, F2, E3, A4, B4, C4, D4, F4, G4, H4, E5, E6, E7, E8];
|
||||
let expected = bitboard![D1 F1 D2 E2 F2 E3 A4 B4 C4 D4 F4 G4 H4 E5 E6 E7 E8];
|
||||
assert_eq_bitboards!(danger_squares, expected);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue