From 1cf05b5f55c1b18833e008b82bc1ca2e2a803758 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Mon, 1 Apr 2024 21:19:55 +0100 Subject: [PATCH] Add 'ChessBoardBuilder' --- src/board/chess_board/builder.rs | 161 +++++++++++++++++++++++++++++++ src/board/chess_board/mod.rs | 3 + 2 files changed, 164 insertions(+) create mode 100644 src/board/chess_board/builder.rs diff --git a/src/board/chess_board/builder.rs b/src/board/chess_board/builder.rs new file mode 100644 index 0000000..8221d92 --- /dev/null +++ b/src/board/chess_board/builder.rs @@ -0,0 +1,161 @@ +use crate::board::{Bitboard, CastleRights, ChessBoard, Color, InvalidError, Piece, Square}; + +/// Build a [ChessBoard] one piece at a time. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct ChessBoardBuilder { + /// The list of [Piece] on the board. Indexed by [Square::index]. + pieces: [Option<(Piece, Color)>; 64], + // Same fields as [ChessBoard]. + castle_rights: [CastleRights; Color::NUM_VARIANTS], + en_passant: Option, + half_move_clock: u8, + total_plies: u32, + side: Color, +} + +impl ChessBoardBuilder { + pub fn new() -> Self { + Self { + pieces: [None; 64], + castle_rights: [CastleRights::NoSide; 2], + en_passant: Default::default(), + half_move_clock: Default::default(), + total_plies: Default::default(), + side: Color::White, + } + } + + pub fn with_castle_rights(&mut self, rights: CastleRights, color: Color) -> &mut Self { + self.castle_rights[color.index()] = rights; + self + } + + pub fn with_en_passant(&mut self, square: Square) -> &mut Self { + self.en_passant = Some(square); + self + } + + pub fn without_en_passant(&mut self) -> &mut Self { + self.en_passant = None; + self + } + + pub fn with_half_move_clock(&mut self, clock: u8) -> &mut Self { + self.half_move_clock = clock; + self + } + + pub fn with_total_plies(&mut self, plies: u32) -> &mut Self { + self.total_plies = plies; + self + } + + pub fn with_current_player(&mut self, color: Color) -> &mut Self { + self.side = color; + self + } +} + +impl Default for ChessBoardBuilder { + fn default() -> Self { + Self::new() + } +} + +/// Index a [ChessBoardBuilder] with a [Square] to access its pieces. +impl std::ops::Index for ChessBoardBuilder { + type Output = Option<(Piece, Color)>; + + fn index(&self, square: Square) -> &Self::Output { + &self.pieces[square.index()] + } +} + +/// Index a [ChessBoardBuilder] with a [Square] to access its pieces. +impl std::ops::IndexMut for ChessBoardBuilder { + fn index_mut(&mut self, square: Square) -> &mut Self::Output { + &mut self.pieces[square.index()] + } +} + +impl TryFrom for ChessBoard { + type Error = InvalidError; + + fn try_from(builder: ChessBoardBuilder) -> Result { + let mut piece_occupancy: [Bitboard; Piece::NUM_VARIANTS] = Default::default(); + let mut color_occupancy: [Bitboard; Color::NUM_VARIANTS] = Default::default(); + let mut combined_occupancy: Bitboard = Default::default(); + let ChessBoardBuilder { + pieces, + castle_rights, + en_passant, + half_move_clock, + total_plies, + side, + } = builder; + + for square in Square::iter() { + let Some((piece, color)) = pieces[square.index()] else { + continue; + }; + piece_occupancy[piece.index()] |= square; + color_occupancy[color.index()] |= square; + combined_occupancy |= square; + } + + let board = ChessBoard { + piece_occupancy, + color_occupancy, + combined_occupancy, + castle_rights, + en_passant, + half_move_clock, + total_plies, + side, + }; + + board.validate()?; + Ok(board) + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn from_board(board: &ChessBoard) -> ChessBoardBuilder { + let mut builder = ChessBoardBuilder::new(); + + for piece in Piece::iter() { + for color in Color::iter() { + for square in board.occupancy(piece, color) { + builder[square] = Some((piece, color)); + } + } + } + + for color in Color::iter() { + builder.with_castle_rights(board.castle_rights(color), color); + } + + if let Some(square) = board.en_passant() { + builder.with_en_passant(square); + } else { + builder.without_en_passant(); + } + + builder + .with_half_move_clock(board.half_move_clock()) + .with_total_plies(board.total_plies()) + .with_current_player(board.current_player()); + + builder + } + + #[test] + fn default_board() { + let board = ChessBoard::default(); + let builder = from_board(&board); + assert_eq!(board, builder.try_into().unwrap()) + } +} diff --git a/src/board/chess_board/mod.rs b/src/board/chess_board/mod.rs index 1f0b293..d04fdec 100644 --- a/src/board/chess_board/mod.rs +++ b/src/board/chess_board/mod.rs @@ -5,6 +5,9 @@ use crate::{ use super::{Bitboard, CastleRights, Color, File, Move, Piece, Rank, Square}; +mod builder; +pub use builder::*; + mod error; pub use error::*;