Move a whole bunch of stuff to the new chessfriend_board package
This commit is contained in:
parent
797606785e
commit
1d82d27f84
12 changed files with 1130 additions and 41 deletions
290
board/src/board.rs
Normal file
290
board/src/board.rs
Normal file
|
@ -0,0 +1,290 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::{display::DiagramFormatter, Castle, EnPassant, Flags, PieceBitBoards, Pieces};
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
|
||||
|
||||
#[derive(Clone, Debug, Eq)]
|
||||
pub struct Board {
|
||||
player_to_move: Color,
|
||||
flags: Flags,
|
||||
pieces: PieceBitBoards,
|
||||
en_passant: Option<EnPassant>,
|
||||
half_move_counter: u16,
|
||||
full_move_number: u16,
|
||||
}
|
||||
|
||||
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 {
|
||||
player_to_move: Color::White,
|
||||
pieces: PieceBitBoards::new([WHITE_PIECES, BLACK_PIECES]),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
player_to_move: Color,
|
||||
flags: Flags,
|
||||
pieces: PieceBitBoards,
|
||||
en_passant: Option<EnPassant>,
|
||||
half_move_counter: u16,
|
||||
full_move_number: u16,
|
||||
) -> Self {
|
||||
Self {
|
||||
player_to_move,
|
||||
flags,
|
||||
pieces,
|
||||
en_passant,
|
||||
half_move_counter,
|
||||
full_move_number,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn player_to_move(&self) -> Color {
|
||||
self.player_to_move
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn move_number(&self) -> u16 {
|
||||
self.full_move_number
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn ply_counter(&self) -> u16 {
|
||||
self.half_move_counter
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn flags(&self) -> &Flags {
|
||||
&self.flags
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[must_use]
|
||||
pub fn player_has_right_to_castle(&self, color: Color, castle: Castle) -> bool {
|
||||
self.flags.player_has_right_to_castle(color, castle)
|
||||
}
|
||||
|
||||
/// The rook to use for a castling move.
|
||||
#[must_use]
|
||||
pub fn rook_for_castle(&self, player: Color, castle: Castle) -> Option<PlacedPiece> {
|
||||
let square = castle.parameters(player).rook_origin_square();
|
||||
self.piece_on_square(square)
|
||||
}
|
||||
|
||||
/// A [`BitBoard`] representing the set of squares containing a piece.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn occupied_squares(&self) -> &BitBoard {
|
||||
self.pieces.all_pieces()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn friendly_pieces(&self) -> &BitBoard {
|
||||
self.pieces.all_pieces_of_color(self.player_to_move)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn opposing_pieces(&self) -> &BitBoard {
|
||||
self.pieces.all_pieces_of_color(self.player_to_move.other())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn all_pieces(&self) -> (&BitBoard, &BitBoard) {
|
||||
(self.friendly_pieces(), self.opposing_pieces())
|
||||
}
|
||||
|
||||
/// A [`BitBoard`] representing the set of squares containing a piece. This set is the inverse of
|
||||
/// `Board::occupied_squares`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn empty_squares(&self) -> BitBoard {
|
||||
!self.occupied_squares()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
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
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn pieces(&self, color: Color) -> Pieces {
|
||||
Pieces::new(self, color)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn has_en_passant_square(&self) -> bool {
|
||||
self.en_passant.is_some()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn en_passant(&self) -> Option<EnPassant> {
|
||||
self.en_passant
|
||||
}
|
||||
|
||||
fn king_bitboard(&self, player: Color) -> &BitBoard {
|
||||
self.pieces.bitboard_for_piece(&Piece::king(player))
|
||||
}
|
||||
|
||||
pub(crate) fn king_square(&self, player: Color) -> Square {
|
||||
self.king_bitboard(player)
|
||||
.occupied_squares()
|
||||
.next()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn display(&self) -> DiagramFormatter<'_> {
|
||||
DiagramFormatter::new(self)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn bitboard_for_color(&self, color: Color) -> &BitBoard {
|
||||
self.pieces.bitboard_for_color(color)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn bitboard_for_piece(&self, piece: Piece) -> &BitBoard {
|
||||
self.pieces.bitboard_for_piece(&piece)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Board {
|
||||
pub(crate) fn test_set_en_passant(&mut self, en_passant: EnPassant) {
|
||||
self.en_passant = Some(en_passant);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Board {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
player_to_move: Color::White,
|
||||
flags: Flags::default(),
|
||||
pieces: PieceBitBoards::default(),
|
||||
en_passant: None,
|
||||
half_move_counter: 0,
|
||||
full_move_number: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Board {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.pieces == other.pieces
|
||||
&& self.player_to_move == other.player_to_move
|
||||
&& self.flags == other.flags
|
||||
&& self.en_passant == other.en_passant
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_board;
|
||||
use chessfriend_core::piece;
|
||||
|
||||
#[test]
|
||||
fn piece_on_square() {
|
||||
let pos = test_board![
|
||||
Black Bishop on F7,
|
||||
];
|
||||
|
||||
let piece = pos.piece_on_square(Square::F7);
|
||||
assert_eq!(piece, Some(piece!(Black Bishop on F7)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn piece_in_starting_position() {
|
||||
let board = test_board!(starting);
|
||||
|
||||
assert_eq!(
|
||||
board.piece_on_square(Square::H1),
|
||||
Some(piece!(White Rook on H1))
|
||||
);
|
||||
assert_eq!(
|
||||
board.piece_on_square(Square::A8),
|
||||
Some(piece!(Black Rook on A8))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn king_not_on_starting_square_cannot_castle() {
|
||||
let board = test_board!(White King on E4);
|
||||
assert!(!board.player_has_right_to_castle(Color::White, Castle::KingSide));
|
||||
assert!(!board.player_has_right_to_castle(Color::White, Castle::QueenSide));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn king_on_starting_square_can_castle() {
|
||||
let board = test_board!(
|
||||
White King on E1,
|
||||
White Rook on A1,
|
||||
White Rook on H1
|
||||
);
|
||||
|
||||
assert!(board.player_has_right_to_castle(Color::White, Castle::KingSide));
|
||||
assert!(board.player_has_right_to_castle(Color::White, Castle::QueenSide));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rook_for_castle() {
|
||||
let board = test_board![
|
||||
White King on E1,
|
||||
White Rook on H1,
|
||||
White Rook on A1,
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
board.rook_for_castle(Color::White, Castle::KingSide),
|
||||
Some(piece!(White Rook on H1))
|
||||
);
|
||||
assert_eq!(
|
||||
board.rook_for_castle(Color::White, Castle::QueenSide),
|
||||
Some(piece!(White Rook on A1))
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue