This commit is contained in:
Eryn Wells 2025-05-08 17:37:51 -07:00
parent d5cdf273c8
commit 091cc99cb3
42 changed files with 805 additions and 1662 deletions

View file

@ -1,16 +1,22 @@
// Eryn Wells <eryn@erynwells.me>
use crate::{castle, display::DiagramFormatter, Castle, Clock, PieceSet};
use crate::{
castle,
display::DiagramFormatter,
piece_sets::{PlacePieceError, PlacePieceStrategy},
PieceSet,
};
use chessfriend_bitboard::BitBoard;
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
use std::iter::Iterator;
use chessfriend_core::{Color, Piece, Shape, Square};
#[derive(Clone, Debug, Default, Eq)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Board {
pub clock: Clock,
pub active_color: Color,
pub pieces: PieceSet,
pub castling_rights: castle::Rights,
pub en_passant_target: Option<Square>,
pub half_move_clock: u32,
pub full_move_number: u32,
}
impl Board {
@ -46,12 +52,44 @@ impl Board {
..Default::default()
}
}
}
impl Board {
#[must_use]
pub fn player_to_move(&self) -> Color {
self.clock.active_color()
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)
}
pub fn remove_piece(&mut self, square: Square) -> Option<Piece> {
self.pieces.remove(square)
}
}
impl Board {
#[must_use]
pub fn display(&self) -> DiagramFormatter<'_> {
DiagramFormatter::new(self)
}
}
/*
impl Board {
/// The rook to use for a castling move.
#[must_use]
pub fn rook_for_castle(&self, player: Color, castle: Castle) -> Option<PlacedPiece> {
@ -71,73 +109,26 @@ impl Board {
/// through a square that an enemy piece can see
#[must_use]
pub fn player_can_castle(&self, player: Color, castle: Castle) -> bool {
if !self.castling_rights.is_set(player, castle.into()) {
if !self.castling_rights.is_set(player, castle) {
return false;
}
let castling_parameters = castle.parameters(player);
let all_pieces = self.all_pieces_bitboard();
let all_pieces = self.pieces.all_pieces();
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;
}
// TODO: Reimplement king_danger here or in Position.
// let danger_squares = self.king_danger(player);
// if !(danger_squares & castling_parameters.check_squares()).is_empty() {
// return false;
// }
true
}
/// A [`BitBoard`] representing the set of squares containing a piece. This
/// set is the inverse of [`Board::empty_squares`].
#[must_use]
pub fn occupied_squares(&self) -> BitBoard {
self.pieces.all_pieces()
}
#[must_use]
pub fn friendly_pieces_bitboard(&self) -> BitBoard {
self.pieces.all_pieces_of_color(self.clock.active_color())
}
#[must_use]
pub fn opposing_pieces_bitboard(&self) -> BitBoard {
self.pieces
.all_pieces_of_color(self.clock.active_color().other())
}
#[must_use]
pub fn all_pieces(&self) -> (BitBoard, BitBoard) {
(
self.friendly_pieces_bitboard(),
self.opposing_pieces_bitboard(),
)
}
pub fn all_pieces_bitboard(&self) -> BitBoard {
self.pieces.all_pieces()
}
pub fn all_pieces_of_color_bitboard(&self, color: Color) -> BitBoard {
self.pieces.all_pieces_of_color(color)
}
/// A [`BitBoard`] representing the set of squares containing a piece. This
/// set is the inverse of [`Board::occupied_squares`].
#[must_use]
pub fn empty_squares(&self) -> BitBoard {
!self.occupied_squares()
}
#[must_use]
pub fn piece_on_square(&self, square: Square) -> Option<PlacedPiece> {
self.pieces
.get(square)
.map(|piece| PlacedPiece::new(piece, square))
}
pub fn iter_all_pieces(&self) -> impl Iterator<Item = PlacedPiece> + '_ {
self.pieces.iter()
}
@ -147,58 +138,8 @@ impl Board {
.iter()
.filter(move |piece| piece.color() == color)
}
#[must_use]
pub fn en_passant_target(&self) -> Option<Square> {
self.en_passant_target
}
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).try_into().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)
}
/// 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 board_without_king = {
let mut cloned_board = self.clone();
cloned_board.pieces.remove(self.king_square(color));
cloned_board
};
BitBoard::full()
}
}
impl PartialEq for Board {
fn eq(&self, other: &Self) -> bool {
self.pieces == other.pieces
&& self.castling_rights == other.castling_rights
&& self.en_passant_target == other.en_passant_target
&& self.clock == other.clock
}
}
*/
#[cfg(test)]
mod tests {
@ -207,26 +148,11 @@ mod tests {
use chessfriend_core::piece;
#[test]
fn piece_on_square() {
let pos = test_board![
fn get_piece_on_square() {
let board = 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))
);
assert_eq!(board.get_piece(Square::F7), Some(piece!(Black Bishop)));
}
}

View file

@ -13,22 +13,25 @@ impl Rights {
/// as long as they have not moved their king, or the rook on that side of
/// the board.
#[must_use]
pub fn is_set(self, color: Color, castle: Castle) -> bool {
pub fn color_has_right(self, color: Color, castle: Castle) -> bool {
(self.0 & (1 << Self::flag_offset(color, castle))) != 0
}
pub fn set(&mut self, color: Color, castle: Castle) {
pub fn grant(&mut self, color: Color, castle: Castle) {
self.0 |= 1 << Self::flag_offset(color, castle);
}
pub fn clear(&mut self, color: Color, castle: Castle) {
pub fn revoke(&mut self, color: Color, castle: Castle) {
self.0 &= !(1 << Self::flag_offset(color, castle));
}
pub fn clear_all(&mut self) {
/// Revoke castling rights for all colors and all sides of the board.
pub fn revoke_all(&mut self) {
self.0 = 0;
}
}
impl Rights {
fn flag_offset(color: Color, castle: Castle) -> usize {
((color as usize) << 1) + castle as usize
}
@ -61,21 +64,21 @@ mod tests {
#[test]
fn default_rights() {
let mut rights = Rights::default();
assert!(rights.is_set(Color::White, Castle::KingSide));
assert!(rights.is_set(Color::White, Castle::QueenSide));
assert!(rights.is_set(Color::Black, Castle::KingSide));
assert!(rights.is_set(Color::Black, Castle::QueenSide));
assert!(rights.color_has_right(Color::White, Castle::KingSide));
assert!(rights.color_has_right(Color::White, Castle::QueenSide));
assert!(rights.color_has_right(Color::Black, Castle::KingSide));
assert!(rights.color_has_right(Color::Black, Castle::QueenSide));
rights.clear(Color::White, Castle::QueenSide);
assert!(rights.is_set(Color::White, Castle::KingSide));
assert!(!rights.is_set(Color::White, Castle::QueenSide));
assert!(rights.is_set(Color::Black, Castle::KingSide));
assert!(rights.is_set(Color::Black, Castle::QueenSide));
rights.revoke(Color::White, Castle::QueenSide);
assert!(rights.color_has_right(Color::White, Castle::KingSide));
assert!(!rights.color_has_right(Color::White, Castle::QueenSide));
assert!(rights.color_has_right(Color::Black, Castle::KingSide));
assert!(rights.color_has_right(Color::Black, Castle::QueenSide));
rights.set(Color::White, Castle::QueenSide);
assert!(rights.is_set(Color::White, Castle::KingSide));
assert!(rights.is_set(Color::White, Castle::QueenSide));
assert!(rights.is_set(Color::Black, Castle::KingSide));
assert!(rights.is_set(Color::Black, Castle::QueenSide));
rights.grant(Color::White, Castle::QueenSide);
assert!(rights.color_has_right(Color::White, Castle::KingSide));
assert!(rights.color_has_right(Color::White, Castle::QueenSide));
assert!(rights.color_has_right(Color::Black, Castle::KingSide));
assert!(rights.color_has_right(Color::Black, Castle::QueenSide));
}
}

View file

@ -4,6 +4,7 @@ use crate::Board;
use chessfriend_core::{File, Rank, Square};
use std::fmt;
#[must_use]
pub struct DiagramFormatter<'a>(&'a Board);
impl<'a> DiagramFormatter<'a> {
@ -21,8 +22,8 @@ impl<'a> fmt::Display for DiagramFormatter<'a> {
for file in File::ALL {
let square = Square::from_file_rank(file, rank);
match self.0.piece_on_square(square) {
Some(placed_piece) => write!(f, "{} ", placed_piece.piece())?,
match self.0.get_piece(square) {
Some(piece) => write!(f, "{piece} ")?,
None => write!(f, "· ")?,
}
}

View file

@ -1,6 +1,6 @@
// Eryn Wells <eryn@erynwells.me>
use crate::{piece_sets::PlacePieceStrategy, Board, Castle, EnPassant};
use crate::{piece_sets::PlacePieceStrategy, Board, Castle};
use chessfriend_core::{
coordinates::ParseSquareError, piece, Color, File, Piece, PlacedPiece, Rank, Square,
};
@ -69,7 +69,7 @@ impl ToFenStr for Board {
for rank in Rank::ALL.into_iter().rev() {
for file in File::ALL {
let square = Square::from_file_rank(file, rank);
match self.piece_on_square(square) {
match self.get_piece(square) {
Some(piece) => {
if empty_squares > 0 {
write!(fen_string, "{empty_squares}")
@ -93,7 +93,7 @@ impl ToFenStr for Board {
}
}
write!(fen_string, " {}", self.player_to_move().to_fen_str()?)
write!(fen_string, " {}", self.active_color.to_fen_str()?)
.map_err(ToFenStrError::FmtError)?;
let castling = [
@ -103,7 +103,7 @@ impl ToFenStr for Board {
(Color::Black, Castle::QueenSide),
]
.map(|(color, castle)| {
if !self.castling_rights.is_set(color, castle) {
if !self.castling_rights.color_has_right(color, castle) {
return "";
}
@ -131,10 +131,8 @@ impl ToFenStr for Board {
)
.map_err(ToFenStrError::FmtError)?;
write!(fen_string, " {}", self.clock.half_move_number())
.map_err(ToFenStrError::FmtError)?;
write!(fen_string, " {}", self.clock.full_move_number())
.map_err(ToFenStrError::FmtError)?;
write!(fen_string, " {}", self.half_move_clock).map_err(ToFenStrError::FmtError)?;
write!(fen_string, " {}", self.full_move_number).map_err(ToFenStrError::FmtError)?;
Ok(fen_string)
}
@ -156,7 +154,7 @@ impl ToFenStr for Piece {
fn to_fen_str(&self) -> Result<String, Self::Error> {
let ascii: char = self.to_ascii();
Ok(String::from(match self.color() {
Ok(String::from(match self.color {
Color::White => ascii.to_ascii_uppercase(),
Color::Black => ascii.to_ascii_lowercase(),
}))
@ -214,20 +212,20 @@ impl FromFenStr for Board {
.next()
.ok_or(FromFenStrError::MissingField(Field::PlayerToMove))?,
)?;
board.clock.active_color = active_color;
board.active_color = active_color;
let castling_rights = fields
.next()
.ok_or(FromFenStrError::MissingField(Field::CastlingRights))?;
if castling_rights == "-" {
board.castling_rights.clear_all();
board.castling_rights.revoke_all();
} else {
for ch in castling_rights.chars() {
match ch {
'K' => board.castling_rights.set(Color::White, Castle::KingSide),
'Q' => board.castling_rights.set(Color::White, Castle::QueenSide),
'k' => board.castling_rights.set(Color::Black, Castle::KingSide),
'q' => board.castling_rights.set(Color::Black, Castle::QueenSide),
'K' => board.castling_rights.grant(Color::White, Castle::KingSide),
'Q' => board.castling_rights.grant(Color::White, Castle::QueenSide),
'k' => board.castling_rights.grant(Color::Black, Castle::KingSide),
'q' => board.castling_rights.grant(Color::Black, Castle::QueenSide),
_ => return Err(FromFenStrError::InvalidValue),
};
}
@ -245,18 +243,18 @@ impl FromFenStr for Board {
let half_move_clock = fields
.next()
.ok_or(FromFenStrError::MissingField(Field::HalfMoveClock))?;
let half_move_clock: u16 = half_move_clock
let half_move_clock: u32 = half_move_clock
.parse()
.map_err(FromFenStrError::ParseIntError)?;
board.clock.half_move_number = half_move_clock;
board.half_move_clock = half_move_clock;
let full_move_counter = fields
.next()
.ok_or(FromFenStrError::MissingField(Field::FullMoveCounter))?;
let full_move_counter: u16 = full_move_counter
let full_move_counter: u32 = full_move_counter
.parse()
.map_err(FromFenStrError::ParseIntError)?;
board.clock.full_move_number = full_move_counter;
board.full_move_number = full_move_counter;
debug_assert_eq!(fields.next(), None);

View file

@ -7,12 +7,14 @@ pub mod fen;
pub mod macros;
mod board;
mod move_counter;
mod piece_sets;
pub use board::Board;
pub use move_counter::Clock;
use castle::Castle;
use en_passant::EnPassant;
use piece_sets::{PieceSet, PlacePieceError, PlacePieceStrategy};
use piece_sets::PieceSet;
// Used by macros.
#[allow(unused_imports)]
use piece_sets::{PlacePieceError, PlacePieceStrategy};

View file

@ -1,34 +1,19 @@
// Eryn Wells <eryn@erynwells.me>
#[macro_export]
macro_rules! board {
[$($color:ident $shape:ident on $square:ident),* $(,)?] => {
$crate::Builder::new()
$(.place_piece(
chessfriend_core::PlacedPiece::new(
chessfriend_core::Piece::new(
chessfriend_core::Color::$color,
chessfriend_core::Shape::$shape),
chessfriend_core::Square::$square
)
))*
.build()
};
}
#[macro_export]
macro_rules! test_board {
($to_move:ident, [ $($color:ident $shape:ident on $square:ident),* $(,)? ], $en_passant:ident) => {
{
let mut board = $crate::Board::empty();
$(let _ = board.pieces.place(
$(let _ = board.place_piece(
chessfriend_core::Piece::new(
chessfriend_core::Color::$color,
chessfriend_core::Shape::$shape
),
chessfriend_core::Square::$square);
chessfriend_core::Square::$square,
$crate::PlacePieceStrategy::default());
)*
board.clock.active_color = chessfriend_core::Color::$to_move;
board.active_color = chessfriend_core::Color::$to_move;
board.en_passant_target = Some(chessfriend_core::Square::$en_passant);
println!("{}", board.display());
@ -39,7 +24,7 @@ macro_rules! test_board {
($to_move:ident, [ $($color:ident $shape:ident on $square:ident),* $(,)? ]) => {
{
let mut board = $crate::Board::empty();
$(let _ = board.pieces.place(
$(let _ = board.place_piece(
chessfriend_core::Piece::new(
chessfriend_core::Color::$color,
chessfriend_core::Shape::$shape
@ -47,7 +32,7 @@ macro_rules! test_board {
chessfriend_core::Square::$square,
$crate::PlacePieceStrategy::default());
)*
board.clock.active_color = chessfriend_core::Color::$to_move;
board.active_color = chessfriend_core::Color::$to_move;
println!("{}", board.display());
@ -57,7 +42,7 @@ macro_rules! test_board {
($($color:ident $shape:ident on $square:ident),* $(,)?) => {
{
let mut board = $crate::Board::empty();
$(let _ = board.pieces.place(
$(let _ = board.place_piece(
chessfriend_core::Piece::new(
chessfriend_core::Color::$color,
chessfriend_core::Shape::$shape

View file

@ -9,32 +9,14 @@ pub enum AdvanceHalfMove {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Clock {
/// The player who's turn it is to move.
pub(crate) active_color: Color,
/// The number of completed turns. A turn finishes when every player has moved.
pub(crate) full_move_number: u16,
pub full_move_number: u16,
/// The number of moves by all players since the last pawn advance or capture.
pub(crate) half_move_number: u16,
pub half_move_number: u16,
}
impl Clock {
#[must_use]
pub fn active_color(&self) -> Color {
self.active_color
}
#[must_use]
pub fn full_move_number(&self) -> u16 {
self.full_move_number
}
#[must_use]
pub fn half_move_number(&self) -> u16 {
self.half_move_number
}
pub fn advance(&mut self, advance_half_move: &AdvanceHalfMove) {
let next_color = self.active_color.next();

View file

@ -1,16 +1,15 @@
// Eryn Wells <eryn@erynwells.me>
mod bitboards;
mod mailbox;
pub(crate) use mailbox::Mailbox;
use bitboards::{ByColor, ByColorAndShape};
use self::mailbox::Mailbox;
use chessfriend_bitboard::{BitBoard, IterationDirection};
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
use std::ops::BitOr;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum PlacePieceStrategy {
#[default]
Replace,
PreserveExisting,
}
@ -20,73 +19,54 @@ pub enum PlacePieceError {
ExisitingPiece(PlacedPiece),
}
impl Default for PlacePieceStrategy {
fn default() -> Self {
Self::Replace
}
}
/// The internal data structure of a [Board] that efficiently manages the
/// placement of pieces on the board.
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct PieceSet {
mailbox: Mailbox,
by_color: ByColor,
by_color_and_shape: ByColorAndShape,
color_occupancy: [BitBoard; Color::NUM],
shape_occupancy: [BitBoard; Shape::NUM],
}
impl PieceSet {
pub(crate) fn new(pieces: [[BitBoard; Shape::NUM]; Color::NUM]) -> 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;
let mut mailbox = Mailbox::default();
for c in Color::into_iter() {
for s in Shape::into_iter() {
let bitboard = pieces[c as usize][s as usize];
let mut color_occupancy: [BitBoard; Color::NUM] = Default::default();
let mut shape_occupancy: [BitBoard; Shape::NUM] = Default::default();
for (color_index, color) in Color::iter().enumerate() {
for (shape_index, shape) in Shape::into_iter().enumerate() {
let bitboard = pieces[color_index][shape_index];
color_occupancy[color_index] |= bitboard;
shape_occupancy[shape_index] |= bitboard;
for square in bitboard.occupied_squares(&IterationDirection::default()) {
mailbox.set(Piece::new(c, s), square);
let piece = Piece::new(*color, shape);
mailbox.set(piece, square);
}
}
}
Self {
by_color: ByColor::new(all_pieces, [white_pieces, black_pieces]),
by_color_and_shape: ByColorAndShape::new(pieces),
mailbox,
color_occupancy,
shape_occupancy,
}
}
/// 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 fn all_pieces(&self) -> BitBoard {
self.color_occupancy
.iter()
.fold(BitBoard::empty(), BitOr::bitor)
}
pub(crate) fn iter(&self) -> impl Iterator<Item = PlacedPiece> {
self.mailbox.iter()
}
pub(super) fn bitboard_for_color(&self, color: Color) -> BitBoard {
self.by_color.bitboard(color)
}
pub(crate) fn bitboard_for_piece(&self, piece: Piece) -> BitBoard {
self.by_color_and_shape.bitboard_for_piece(piece)
}
pub(crate) fn get(&self, square: Square) -> Option<Piece> {
self.mailbox.get(square)
}
@ -96,9 +76,7 @@ impl PieceSet {
piece: Piece,
square: Square,
strategy: PlacePieceStrategy,
) -> Result<PlacedPiece, PlacePieceError> {
let color = piece.color();
) -> Result<(), PlacePieceError> {
if strategy == PlacePieceStrategy::PreserveExisting {
if let Some(existing_piece) = self.mailbox.get(square) {
return Err(PlacePieceError::ExisitingPiece(PlacedPiece::new(
@ -108,35 +86,78 @@ impl PieceSet {
}
}
let piece: Piece = piece.into();
self.by_color_and_shape.set_square(square, piece);
self.by_color.set_square(square, color);
let color = piece.color;
let shape = piece.shape;
self.color_occupancy[color as usize].set(square);
self.shape_occupancy[shape as usize].set(square);
self.mailbox.set(piece, square);
Ok(PlacedPiece::new(piece, square))
Ok(())
}
pub(crate) fn remove(&mut self, square: Square) -> Option<PlacedPiece> {
pub(crate) fn remove(&mut self, square: Square) -> Option<Piece> {
if let Some(piece) = self.mailbox.get(square) {
self.by_color_and_shape.clear_square(square, piece.into());
self.by_color.clear_square(square, piece.color());
self.color_occupancy[piece.color as usize].clear(square);
self.shape_occupancy[piece.shape as usize].clear(square);
self.mailbox.remove(square);
Some(PlacedPiece::new(piece, square))
Some(piece)
} else {
None
}
}
}
impl PieceSet {
pub fn color_bitboard(&self, color: Color) -> BitBoard {
self.color_occupancy[color as usize]
}
pub fn piece_bitboard(&self, piece: Piece) -> BitBoard {
let color_occupancy = self.color_occupancy[piece.color as usize];
let shape_occupancy = self.shape_occupancy[piece.shape as usize];
color_occupancy & shape_occupancy
}
}
impl FromIterator<PlacedPiece> for PieceSet {
fn from_iter<T: IntoIterator<Item = PlacedPiece>>(iter: T) -> Self {
let mut pieces: Self = Self::default();
for piece in iter {
let _ = pieces.place(piece.piece(), piece.square(), PlacePieceStrategy::default());
let _ = pieces.place(piece.piece, piece.square, PlacePieceStrategy::default());
}
pieces
}
}
#[cfg(test)]
mod tests {
use super::*;
use chessfriend_bitboard::bitboard;
#[test]
fn place_piece() -> Result<(), PlacePieceError> {
let mut pieces = PieceSet::default();
pieces.place(
Piece::king(Color::White),
Square::F5,
PlacePieceStrategy::default(),
)?;
assert_eq!(
pieces.mailbox.get(Square::F5),
Some(Piece::king(Color::White))
);
assert_eq!(pieces.color_bitboard(Color::White), bitboard![F5]);
assert_eq!(
pieces.piece_bitboard(Piece::king(Color::White)),
bitboard![F5]
);
Ok(())
}
}

View file

@ -39,11 +39,11 @@ impl ByColorAndShape {
}
pub(super) fn bitboard_for_piece(&self, piece: Piece) -> BitBoard {
self.0[piece.color() as usize][piece.shape() as usize]
self.0[piece.color as usize][piece.shape as usize]
}
pub(super) fn bitboard_for_piece_mut(&mut self, piece: Piece) -> &mut BitBoard {
&mut self.0[piece.color() as usize][piece.shape() as usize]
&mut self.0[piece.color as usize][piece.shape as usize]
}
pub(super) fn set_square(&mut self, square: Square, piece: Piece) {

View file

@ -7,23 +7,23 @@ use std::iter::FromIterator;
pub(crate) struct Mailbox([Option<Piece>; Square::NUM]);
impl Mailbox {
pub(crate) fn new() -> Self {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn get(&self, square: Square) -> Option<Piece> {
pub fn get(&self, square: Square) -> Option<Piece> {
self.0[square as usize]
}
pub(crate) fn set(&mut self, piece: Piece, square: Square) {
pub fn set(&mut self, piece: Piece, square: Square) {
self.0[square as usize] = Some(piece);
}
pub(crate) fn remove(&mut self, square: Square) {
pub fn remove(&mut self, square: Square) {
self.0[square as usize] = None;
}
pub(crate) fn iter(&self) -> impl Iterator<Item = PlacedPiece> {
pub fn iter(&self) -> impl Iterator<Item = PlacedPiece> {
self.0
.into_iter()
.flatten() // Remove the Nones
@ -50,7 +50,7 @@ impl FromIterator<PlacedPiece> for Mailbox {
fn from_iter<T: IntoIterator<Item = PlacedPiece>>(iter: T) -> Self {
iter.into_iter()
.fold(Self::new(), |mut mailbox, placed_piece| {
mailbox.set(placed_piece.piece(), placed_piece.square());
mailbox.set(placed_piece.piece(), placed_piece.square);
mailbox
})
}