2024-04-25 13:28:24 -07:00
|
|
|
// Eryn Wells <eryn@erynwells.me>
|
|
|
|
|
2025-05-08 17:37:51 -07:00
|
|
|
use crate::{
|
|
|
|
castle,
|
|
|
|
display::DiagramFormatter,
|
|
|
|
piece_sets::{PlacePieceError, PlacePieceStrategy},
|
|
|
|
PieceSet,
|
|
|
|
};
|
2024-04-25 13:28:24 -07:00
|
|
|
use chessfriend_bitboard::BitBoard;
|
2025-05-19 16:50:30 -07:00
|
|
|
use chessfriend_core::{Color, Piece, Shape, Square, Wing};
|
2024-04-25 13:28:24 -07:00
|
|
|
|
2025-05-23 09:53:29 -07:00
|
|
|
pub type HalfMoveClock = u32;
|
|
|
|
pub type FullMoveClock = u32;
|
|
|
|
|
2025-05-08 17:37:51 -07:00
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
2024-04-25 13:28:24 -07:00
|
|
|
pub struct Board {
|
2025-05-08 17:37:51 -07:00
|
|
|
pub active_color: Color,
|
2025-05-03 16:02:56 -07:00
|
|
|
pub pieces: PieceSet,
|
2025-05-02 15:41:45 -07:00
|
|
|
pub castling_rights: castle::Rights,
|
2025-05-03 16:02:56 -07:00
|
|
|
pub en_passant_target: Option<Square>,
|
2025-05-23 09:53:29 -07:00
|
|
|
pub half_move_clock: HalfMoveClock,
|
|
|
|
pub full_move_number: FullMoveClock,
|
2024-04-25 13:28:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Board {
|
|
|
|
/// An empty board
|
|
|
|
#[must_use]
|
|
|
|
pub fn empty() -> Self {
|
|
|
|
Board::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The starting position
|
|
|
|
#[must_use]
|
|
|
|
pub fn starting() -> Self {
|
|
|
|
const BLACK_PIECES: [BitBoard; Shape::NUM] = [
|
|
|
|
BitBoard::new(0b0000_0000_1111_1111 << 48),
|
|
|
|
BitBoard::new(0b0100_0010_0000_0000 << 48),
|
|
|
|
BitBoard::new(0b0010_0100_0000_0000 << 48),
|
|
|
|
BitBoard::new(0b1000_0001_0000_0000 << 48),
|
|
|
|
BitBoard::new(0b0000_1000_0000_0000 << 48),
|
|
|
|
BitBoard::new(0b0001_0000_0000_0000 << 48),
|
|
|
|
];
|
|
|
|
|
|
|
|
const WHITE_PIECES: [BitBoard; Shape::NUM] = [
|
|
|
|
BitBoard::new(0b1111_1111_0000_0000),
|
|
|
|
BitBoard::new(0b0000_0000_0100_0010),
|
|
|
|
BitBoard::new(0b0000_0000_0010_0100),
|
|
|
|
BitBoard::new(0b0000_0000_1000_0001),
|
|
|
|
BitBoard::new(0b0000_0000_0000_1000),
|
|
|
|
BitBoard::new(0b0000_0000_0001_0000),
|
|
|
|
];
|
|
|
|
|
|
|
|
Self {
|
2024-07-13 08:15:14 -07:00
|
|
|
pieces: PieceSet::new([WHITE_PIECES, BLACK_PIECES]),
|
2024-04-25 13:28:24 -07:00
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
}
|
2025-05-08 17:37:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Board {
|
|
|
|
#[must_use]
|
|
|
|
pub fn get_piece(&self, square: Square) -> Option<Piece> {
|
|
|
|
self.pieces.get(square)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Place a piece on the board.
|
|
|
|
///
|
|
|
|
/// ## Errors
|
|
|
|
///
|
|
|
|
/// When is called with [`PlacePieceStrategy::PreserveExisting`], and a piece already exists on
|
|
|
|
/// `square`, this method returns a [`PlacePieceError::ExistingPiece`] error.
|
|
|
|
///
|
|
|
|
pub fn place_piece(
|
|
|
|
&mut self,
|
|
|
|
piece: Piece,
|
|
|
|
square: Square,
|
|
|
|
strategy: PlacePieceStrategy,
|
|
|
|
) -> Result<(), PlacePieceError> {
|
|
|
|
self.pieces.place(piece, square, strategy)
|
|
|
|
}
|
2024-04-25 13:28:24 -07:00
|
|
|
|
2025-05-08 17:37:51 -07:00
|
|
|
pub fn remove_piece(&mut self, square: Square) -> Option<Piece> {
|
|
|
|
self.pieces.remove(square)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Board {
|
2025-05-19 16:50:30 -07:00
|
|
|
/// A [`BitBoard`] of squares occupied by pieces of all colors.
|
2025-05-16 07:47:28 -07:00
|
|
|
pub fn occupancy(&self) -> BitBoard {
|
|
|
|
self.pieces.occpuancy()
|
|
|
|
}
|
|
|
|
|
2025-05-19 16:50:30 -07:00
|
|
|
/// A [`BitBoard`] of squares that are vacant.
|
2025-05-16 07:47:28 -07:00
|
|
|
pub fn vacancy(&self) -> BitBoard {
|
|
|
|
!self.occupancy()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn friendly_occupancy(&self, color: Color) -> BitBoard {
|
|
|
|
self.pieces.friendly_occupancy(color)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn opposing_occupancy(&self, color: Color) -> BitBoard {
|
|
|
|
self.pieces.opposing_occupancy(color)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-19 16:50:30 -07:00
|
|
|
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]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-16 07:47:28 -07:00
|
|
|
impl Board {
|
2025-05-08 17:37:51 -07:00
|
|
|
pub fn display(&self) -> DiagramFormatter<'_> {
|
|
|
|
DiagramFormatter::new(self)
|
2024-04-25 13:28:24 -07:00
|
|
|
}
|
2025-05-08 17:37:51 -07:00
|
|
|
}
|
2024-04-25 13:28:24 -07:00
|
|
|
|
2025-05-23 18:37:13 -07:00
|
|
|
impl Board {
|
|
|
|
#[must_use]
|
|
|
|
pub fn unwrap_color(&self, color: Option<Color>) -> Color {
|
|
|
|
color.unwrap_or(self.active_color)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-25 13:28:24 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::test_board;
|
|
|
|
use chessfriend_core::piece;
|
|
|
|
|
|
|
|
#[test]
|
2025-05-08 17:37:51 -07:00
|
|
|
fn get_piece_on_square() {
|
|
|
|
let board = test_board![
|
2024-04-25 13:28:24 -07:00
|
|
|
Black Bishop on F7,
|
|
|
|
];
|
|
|
|
|
2025-05-08 17:37:51 -07:00
|
|
|
assert_eq!(board.get_piece(Square::F7), Some(piece!(Black Bishop)));
|
2024-04-25 13:28:24 -07:00
|
|
|
}
|
|
|
|
}
|