chessfriend/board/src/position/builders/position_builder.rs

141 lines
3.7 KiB
Rust

// Eryn Wells <eryn@erynwells.me>
use crate::{
bitboard::BitBoardBuilder,
piece::{PlacedPiece, Shape},
position::{flags::Flags, piece_sets::PieceBitBoards},
r#move::Castle,
BitBoard, Color, MakeMoveError, Move, Piece, Position,
};
use chess_core::{Rank, Square};
use std::collections::BTreeMap;
#[derive(Clone)]
pub struct Builder {
player_to_move: Color,
flags: Flags,
pieces: BTreeMap<Square, Piece>,
kings: [Square; 2],
ply_counter: u16,
move_number: u16,
}
impl Builder {
pub fn new() -> Self {
Self::default()
}
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: [white_king, black_king],
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 place_piece(&mut self, piece: PlacedPiece) -> &mut Self {
let square = piece.square();
if piece.shape() == Shape::King {
let color_index: usize = piece.color() as usize;
self.pieces.remove(&self.kings[color_index]);
self.kings[color_index] = square;
}
self.pieces.insert(square, *piece.piece());
self
}
pub fn build(&self) -> Position {
let pieces = PieceBitBoards::from_iter(
self.pieces
.iter()
.map(PlacedPiece::from)
.filter(Self::is_piece_placement_valid),
);
Position::new(
self.player_to_move,
self.flags,
pieces,
None,
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::king_starting_square(Color::White);
let black_king_square = Square::king_starting_square(Color::Black);
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: [white_king_square, black_king_square],
ply_counter: 0,
move_number: 1,
}
}
}
#[cfg(test)]
mod tests {
use crate::PositionBuilder;
#[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));
}
}