From 0d8653894a26ad58d14268a5a19ced635ec977c8 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 14 Jan 2024 10:23:35 -0800 Subject: [PATCH] An attempt at making unit structs for Color and piece Shape My idea was to implement traits on Piece that return sight lines, etc. This has turned out to be much more complex than I thought it would be. Ultimately, I don't think it's worth the effort. --- board/src/bitboard/bitboard.rs | 2 +- board/src/display.rs | 9 + board/src/lib.rs | 6 +- board/src/moves/bishop.rs | 33 +--- board/src/moves/classical.rs | 1 + board/src/moves/king.rs | 4 +- board/src/moves/mod.rs | 4 +- board/src/moves/move.rs | 95 ++++++++--- board/src/moves/move_generator.rs | 1 + board/src/moves/move_set.rs | 3 + board/src/moves/pawn.rs | 3 +- board/src/moves/queen.rs | 30 +--- board/src/moves/rook.rs | 26 +-- board/src/moves/sight.rs | 165 ++++++++++++++++++ board/src/piece.rs | 214 +++++++++--------------- board/src/position/diagram_formatter.rs | 8 +- board/src/position/flags.rs | 21 ++- board/src/position/pieces.rs | 32 ++-- board/src/position/position.rs | 162 ++++++++++++------ 19 files changed, 499 insertions(+), 320 deletions(-) create mode 100644 board/src/display.rs create mode 100644 board/src/moves/classical.rs create mode 100644 board/src/moves/sight.rs diff --git a/board/src/bitboard/bitboard.rs b/board/src/bitboard/bitboard.rs index 3a31a0e..23c2126 100644 --- a/board/src/bitboard/bitboard.rs +++ b/board/src/bitboard/bitboard.rs @@ -1,7 +1,7 @@ // Eryn Wells use super::library::{library, FILES, RANKS}; -use super::{LeadingBitScanner, TrailingBitScanner}; +use super::LeadingBitScanner; use crate::{square::Direction, Square}; use std::fmt; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not}; diff --git a/board/src/display.rs b/board/src/display.rs new file mode 100644 index 0000000..31757c6 --- /dev/null +++ b/board/src/display.rs @@ -0,0 +1,9 @@ +// Eryn Wells + +pub trait ASCIIDisplay { + fn fmt(&self, &mut f: fmt::Formatter) -> fmt::Result; +} + +pub trait UnicodeDisplay { + fn fmt(&self, &mut f: fmt::Formatter) -> fmt::Result; +} diff --git a/board/src/lib.rs b/board/src/lib.rs index 34f38df..bcc682a 100644 --- a/board/src/lib.rs +++ b/board/src/lib.rs @@ -1,13 +1,15 @@ // Eryn Wells mod bitboard; -mod moves; +//mod moves; #[macro_use] pub mod piece; #[macro_use] pub mod position; mod square; -pub use moves::Move; +pub(crate) use bitboard::BitBoard; +//pub use moves::Move; +pub use piece::Color; pub use position::Position; pub use square::{File, Rank, Square}; diff --git a/board/src/moves/bishop.rs b/board/src/moves/bishop.rs index 40c5bea..6aec9e4 100644 --- a/board/src/moves/bishop.rs +++ b/board/src/moves/bishop.rs @@ -1,10 +1,8 @@ // Eryn Wells -use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet}; +use super::{classical, move_generator_declaration, MoveGeneratorInternal, MoveSet, PieceSight}; use crate::{ - bitboard::BitBoard, piece::{Color, Piece, PlacedPiece}, - square::Direction, Move, Position, }; @@ -20,35 +18,14 @@ impl<'pos> MoveGeneratorInternal for ClassicalMoveGenerator<'pos> { let color = piece.color(); let square = placed_piece.square(); - let blockers = position.occupied_squares(); - let empty_squares = !blockers; + let empty_squares = position.empty_squares(); let friendly_pieces = position.bitboard_for_color(color); let opposing_pieces = position.bitboard_for_color(color.other()); - let mut all_moves = BitBoard::empty(); + let sight = classical::BishopSight.sight(square, position); - macro_rules! update_moves_with_ray { - ($direction:ident, $occupied_squares:tt) => { - let ray = BitBoard::ray(square, Direction::$direction); - if let Some(first_occupied_square) = - BitBoard::$occupied_squares(&(ray & blockers)).next() - { - let remainder = BitBoard::ray(first_occupied_square, Direction::$direction); - let attack_ray = ray & !remainder; - all_moves |= attack_ray; - } else { - all_moves |= ray; - } - }; - } - - update_moves_with_ray!(NorthEast, occupied_squares_trailing); - update_moves_with_ray!(NorthWest, occupied_squares_trailing); - update_moves_with_ray!(SouthEast, occupied_squares); - update_moves_with_ray!(SouthWest, occupied_squares); - - let quiet_moves_bb = all_moves & (empty_squares | !friendly_pieces); - let capture_moves_bb = all_moves & opposing_pieces; + let quiet_moves_bb = sight & (empty_squares | !friendly_pieces); + let capture_moves_bb = sight & opposing_pieces; let map_to_move = |sq| Move::new(piece, square, sq); let quiet_moves = quiet_moves_bb.occupied_squares().map(map_to_move); diff --git a/board/src/moves/classical.rs b/board/src/moves/classical.rs new file mode 100644 index 0000000..8aa971d --- /dev/null +++ b/board/src/moves/classical.rs @@ -0,0 +1 @@ +// Eryn Wells diff --git a/board/src/moves/king.rs b/board/src/moves/king.rs index 07eda95..6cf0b77 100644 --- a/board/src/moves/king.rs +++ b/board/src/moves/king.rs @@ -3,7 +3,7 @@ //! Declares the KingMoveGenerator type. This struct is responsible for //! generating the possible moves for the king in the given position. -use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet}; +use super::{classical, move_generator_declaration, MoveGeneratorInternal, MoveSet, PieceSight}; use crate::{ bitboard::BitBoard, piece::{Color, Piece, PlacedPiece}, @@ -50,7 +50,7 @@ impl<'pos> MoveGeneratorInternal for KingMoveGenerator<'pos> { let empty_squares = position.empty_squares(); let opposing_pieces = position.bitboard_for_color(color.other()); - let all_moves = BitBoard::king_moves(square); + let all_moves = classical::KingSight.sight(square, position); let quiet_moves_bb = all_moves & empty_squares; let capture_moves_bb = all_moves & opposing_pieces; diff --git a/board/src/moves/mod.rs b/board/src/moves/mod.rs index bee7170..0d30d3f 100644 --- a/board/src/moves/mod.rs +++ b/board/src/moves/mod.rs @@ -1,6 +1,7 @@ // Eryn Wells mod bishop; +mod classical; mod king; mod knight; mod r#move; @@ -9,6 +10,7 @@ mod move_set; mod pawn; mod queen; mod rook; +pub(crate) mod sight; pub use move_generator::Moves; pub use r#move::Move; @@ -16,7 +18,6 @@ pub use r#move::Move; pub(self) use move_set::MoveSet; use crate::{ - bitboard::BitBoard, piece::{Color, Piece, PlacedPiece}, Position, Square, }; @@ -35,6 +36,7 @@ macro_rules! move_generator_declaration { move_generator_declaration!($name, getters); }; ($name:ident, struct) => { + #[derive(Clone, Debug, Eq, PartialEq)] pub(super) struct $name<'pos> { position: &'pos crate::Position, color: crate::piece::Color, diff --git a/board/src/moves/move.rs b/board/src/moves/move.rs index d952bec..0f88aaf 100644 --- a/board/src/moves/move.rs +++ b/board/src/moves/move.rs @@ -1,41 +1,86 @@ // Eryn Wells -use crate::{ - piece::{Piece, PlacedPiece, Shape}, - position::BoardSide, - Square, -}; +use crate::{piece::*, position::BoardSide, Square}; -#[derive(Debug, Clone, Eq, Hash, PartialEq)] -pub struct Move { - piece: Piece, - from: Square, - to: Square, - capturing: Option, - promoting_to: Option, +/// A move that transfers a piece from one square to another. +trait Move: Sized { + fn piece(&self) -> Piece; + fn from_square(&self) -> Square; + fn to_square(&self) -> Square; } -impl Move { - pub fn new(piece: Piece, from: Square, to: Square) -> Move { - Move { +/// A move that captures an opposing piece. +trait Capturing: Move { + type CaptureMove; + + fn capturing(self, capturing: CapturedS) -> Self::CaptureMove; + fn captured_piece(&self) -> Piece; +} + +/// A move that promotes a pawn to another piece. +trait Promoting: Move { + type PromotionMove; + + fn promoting_to(self, shape: PromotingS) -> Self::PromotionMove; + fn promoting_piece(&self) -> Piece; +} + +#[derive(Debug, Clone, Eq, Hash, PartialEq)] +pub struct SimpleMove { + piece: Piece, + from_square: Square, + to_square: Square, +} + +impl SimpleMove { + fn new(piece: Piece, from_square: Square, to_square: Square) -> Self { + SimpleMove { piece, - from, - to, - capturing: None, - promoting_to: None, + from_square, + to_square, } } - pub fn capturing(mut self, piece: PlacedPiece) -> Move { - self.capturing = Some(piece); - self + fn capturing( + self, + captured_piece: Piece, + ) -> Capture { + Capture { + r#move: self, + captured_piece, + } + } +} + +impl Move for Piece { + fn piece(&self) -> Piece { + self.piece } - pub fn promoting_to(mut self, shape: Shape) -> Move { - self.promoting_to = Some(shape); - self + fn from_square(&self) -> Square { + self.from_square } + fn to_square(&self) -> Square { + self.to_square + } +} + +pub struct Capture { + r#move: dyn Move, + captured_piece: Piece, +} + +pub struct Promotion { + r#move: dyn Move, + promoting_to_shape: PromS, +} + +pub struct CapturingMove { + capturing: PlacedPiece, +} + +impl Move { pub fn is_castle(&self) -> bool { let color = self.piece.color(); self.piece.shape() == Shape::King diff --git a/board/src/moves/move_generator.rs b/board/src/moves/move_generator.rs index b4962db..de61d3d 100644 --- a/board/src/moves/move_generator.rs +++ b/board/src/moves/move_generator.rs @@ -9,6 +9,7 @@ use super::{ use crate::piece::Color; use crate::Position; +#[derive(Clone, Eq, PartialEq)] pub struct Moves<'pos> { pawn_moves: PawnMoveGenerator<'pos>, knight_moves: KnightMoveGenerator<'pos>, diff --git a/board/src/moves/move_set.rs b/board/src/moves/move_set.rs index b3330b9..683907c 100644 --- a/board/src/moves/move_set.rs +++ b/board/src/moves/move_set.rs @@ -1,16 +1,19 @@ use crate::{bitboard::BitBoard, piece::PlacedPiece, Move}; +#[derive(Clone, Debug, Eq, PartialEq)] struct BitBoardSet { quiet: BitBoard, captures: BitBoard, } +#[derive(Clone, Debug, Eq, PartialEq)] struct MoveListSet { quiet: Vec, captures: Vec, } /// A set of moves for a piece on the board. +#[derive(Clone, Debug, Eq, PartialEq)] pub(super) struct MoveSet { piece: PlacedPiece, bitboards: BitBoardSet, diff --git a/board/src/moves/pawn.rs b/board/src/moves/pawn.rs index 118ef8b..a8f3313 100644 --- a/board/src/moves/pawn.rs +++ b/board/src/moves/pawn.rs @@ -12,7 +12,7 @@ enum MoveList { Captures = 2, } -#[derive(Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] struct MoveIterator(usize, usize); struct MoveGenerationParameters { @@ -23,6 +23,7 @@ struct MoveGenerationParameters { right_capture_shift: fn(BitBoard) -> BitBoard, } +#[derive(Clone, Eq, PartialEq)] pub(super) struct PawnMoveGenerator<'pos> { color: Color, position: &'pos Position, diff --git a/board/src/moves/queen.rs b/board/src/moves/queen.rs index 770a9ae..cdefb69 100644 --- a/board/src/moves/queen.rs +++ b/board/src/moves/queen.rs @@ -1,10 +1,8 @@ // Eryn Wells -use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet}; +use super::{classical, move_generator_declaration, MoveGeneratorInternal, MoveSet, PieceSight}; use crate::{ - bitboard::BitBoard, piece::{Color, Piece, PlacedPiece}, - square::Direction, Move, Position, }; @@ -25,31 +23,7 @@ impl<'pos> MoveGeneratorInternal for ClassicalMoveGenerator<'pos> { let friendly_pieces = position.bitboard_for_color(color); let opposing_pieces = position.bitboard_for_color(color.other()); - let mut all_moves = BitBoard::empty(); - - macro_rules! update_moves_with_ray { - ($direction:ident, $occupied_squares:tt) => { - let ray = BitBoard::ray(square, Direction::$direction); - if let Some(first_occupied_square) = - BitBoard::$occupied_squares(&(ray & blockers)).next() - { - let remainder = BitBoard::ray(first_occupied_square, Direction::$direction); - let attack_ray = ray & !remainder; - all_moves |= attack_ray; - } else { - all_moves |= ray; - } - }; - } - - update_moves_with_ray!(NorthWest, occupied_squares_trailing); - update_moves_with_ray!(North, occupied_squares_trailing); - update_moves_with_ray!(NorthEast, occupied_squares_trailing); - update_moves_with_ray!(East, occupied_squares_trailing); - update_moves_with_ray!(SouthEast, occupied_squares); - update_moves_with_ray!(South, occupied_squares); - update_moves_with_ray!(SouthWest, occupied_squares); - update_moves_with_ray!(West, occupied_squares); + let mut all_moves = classical::QueenSight.sight(square, position); let quiet_moves_bb = all_moves & (empty_squares | !friendly_pieces); let capture_moves_bb = all_moves & opposing_pieces; diff --git a/board/src/moves/rook.rs b/board/src/moves/rook.rs index 82b8c76..783ed47 100644 --- a/board/src/moves/rook.rs +++ b/board/src/moves/rook.rs @@ -1,10 +1,8 @@ // Eryn Wells -use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet}; +use super::{classical, move_generator_declaration, MoveGeneratorInternal, MoveSet, PieceSight}; use crate::{ - bitboard::BitBoard, piece::{Color, Piece, PlacedPiece}, - square::Direction, Move, Position, }; @@ -25,27 +23,7 @@ impl<'pos> MoveGeneratorInternal for ClassicalMoveGenerator<'pos> { let friendly_pieces = position.bitboard_for_color(color); let opposing_pieces = position.bitboard_for_color(color.other()); - let mut all_moves = BitBoard::empty(); - - macro_rules! update_moves_with_ray { - ($direction:ident, $occupied_squares:tt) => { - let ray = BitBoard::ray(square, Direction::$direction); - if let Some(first_occupied_square) = - BitBoard::$occupied_squares(&(ray & blockers)).next() - { - let remainder = BitBoard::ray(first_occupied_square, Direction::$direction); - let attack_ray = ray & !remainder; - all_moves |= attack_ray; - } else { - all_moves |= ray; - } - }; - } - - update_moves_with_ray!(North, occupied_squares_trailing); - update_moves_with_ray!(East, occupied_squares_trailing); - update_moves_with_ray!(South, occupied_squares); - update_moves_with_ray!(West, occupied_squares); + let all_moves = classical::RookSight.sight(square, position); let quiet_moves_bb = all_moves & (empty_squares | !friendly_pieces); let capture_moves_bb = all_moves & opposing_pieces; diff --git a/board/src/moves/sight.rs b/board/src/moves/sight.rs new file mode 100644 index 0000000..771a524 --- /dev/null +++ b/board/src/moves/sight.rs @@ -0,0 +1,165 @@ +// Eryn Wells + +use crate::{piece::*, square::Direction, BitBoard, Position, Square}; + +pub(crate) trait Sight { + fn sight_on_empty_board(self, square: Square) -> BitBoard; + fn sight(self, square: Square, position: &Position) -> BitBoard; +} + +impl Sight for Piece { + fn sight_on_empty_board(self, square: Square) -> BitBoard { + let pawn: BitBoard = square.into(); + pawn.shift_north_west_one() | pawn.shift_north_east_one() + } + + fn sight(self, square: Square, position: &Position) -> BitBoard { + let mut possible_squares = position.empty_squares() | position.opposing_pieces(); + if let Some(en_passant) = position.en_passant_square() { + possible_squares |= en_passant.into(); + } + + self.sight_on_empty_board(square) & possible_squares + } +} + +impl Sight for Piece { + fn sight_on_empty_board(self, square: Square) -> BitBoard { + let pawn: BitBoard = square.into(); + pawn.shift_south_west_one() | pawn.shift_south_east_one() + } + + fn sight(self, square: Square, position: &Position) -> BitBoard { + let mut possible_squares = position.empty_squares() | position.opposing_pieces(); + if let Some(en_passant) = position.en_passant_square() { + possible_squares |= en_passant.into(); + } + + self.sight_on_empty_board(square) & possible_squares + } +} + +impl Sight for Piece { + fn sight_on_empty_board(self, square: Square) -> BitBoard { + BitBoard::knight_moves(square) + } + + fn sight(self, square: Square, position: &Position) -> BitBoard { + self.sight_on_empty_board(square) & !position.friendly_pieces() + } +} + +impl Sight for Piece { + fn sight_on_empty_board(self, square: Square) -> BitBoard { + BitBoard::bishop_moves(square) + } + + fn sight(self, square: Square, position: &Position) -> BitBoard { + let mut sight = BitBoard::empty(); + + let blockers = position.occupied_squares(); + macro_rules! update_moves_with_ray { + ($direction:ident, $occupied_squares:tt) => { + let ray = BitBoard::ray(square, Direction::$direction); + if let Some(first_occupied_square) = + BitBoard::$occupied_squares(&(ray & blockers)).next() + { + let remainder = BitBoard::ray(first_occupied_square, Direction::$direction); + let attack_ray = ray & !remainder; + sight |= attack_ray; + } else { + sight |= ray; + } + }; + } + + update_moves_with_ray!(NorthEast, occupied_squares_trailing); + update_moves_with_ray!(NorthWest, occupied_squares_trailing); + update_moves_with_ray!(SouthEast, occupied_squares); + update_moves_with_ray!(SouthWest, occupied_squares); + + sight + } +} + +impl Sight for Piece { + fn sight_on_empty_board(self, square: Square) -> BitBoard { + BitBoard::rook_moves(square) + } + + fn sight(self, square: Square, position: &Position) -> BitBoard { + let mut sight = BitBoard::empty(); + + let blockers = position.occupied_squares(); + + macro_rules! update_moves_with_ray { + ($direction:ident, $occupied_squares:tt) => { + let ray = BitBoard::ray(square, Direction::$direction); + if let Some(first_occupied_square) = + BitBoard::$occupied_squares(&(ray & blockers)).next() + { + let remainder = BitBoard::ray(first_occupied_square, Direction::$direction); + let attack_ray = ray & !remainder; + sight |= attack_ray; + } else { + sight |= ray; + } + }; + } + + update_moves_with_ray!(North, occupied_squares_trailing); + update_moves_with_ray!(East, occupied_squares_trailing); + update_moves_with_ray!(South, occupied_squares); + update_moves_with_ray!(West, occupied_squares); + + sight + } +} + +impl Sight for Piece { + fn sight_on_empty_board(self, square: Square) -> BitBoard { + BitBoard::queen_moves(square) + } + + fn sight(self, square: Square, position: &Position) -> BitBoard { + let mut sight = BitBoard::empty(); + + let blockers = position.occupied_squares(); + + macro_rules! update_moves_with_ray { + ($direction:ident, $occupied_squares:tt) => { + let ray = BitBoard::ray(square, Direction::$direction); + if let Some(first_occupied_square) = + BitBoard::$occupied_squares(&(ray & blockers)).next() + { + let remainder = BitBoard::ray(first_occupied_square, Direction::$direction); + let attack_ray = ray & !remainder; + sight |= attack_ray; + } else { + sight |= ray; + } + }; + } + + update_moves_with_ray!(NorthWest, occupied_squares_trailing); + update_moves_with_ray!(North, occupied_squares_trailing); + update_moves_with_ray!(NorthEast, occupied_squares_trailing); + update_moves_with_ray!(East, occupied_squares_trailing); + update_moves_with_ray!(SouthEast, occupied_squares); + update_moves_with_ray!(South, occupied_squares); + update_moves_with_ray!(SouthWest, occupied_squares); + update_moves_with_ray!(West, occupied_squares); + + sight + } +} + +impl Sight for Piece { + fn sight_on_empty_board(self, square: Square) -> BitBoard { + BitBoard::king_moves(square) + } + + fn sight(self, square: Square, position: &Position) -> BitBoard { + self.sight_on_empty_board(square) & !position.friendly_pieces() + } +} diff --git a/board/src/piece.rs b/board/src/piece.rs index dc9b5da..0646f65 100644 --- a/board/src/piece.rs +++ b/board/src/piece.rs @@ -2,117 +2,80 @@ use crate::{bitboard::BitBoard, Square}; use std::fmt; -use std::slice::Iter; -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum Color { - White = 0, - Black = 1, +pub trait Color: Sized { + type Other: Color; } -impl Color { - pub fn iter() -> impl Iterator { - [Color::White, Color::Black].into_iter() - } - - pub fn other(&self) -> Color { - match self { - Color::White => Color::Black, - Color::Black => Color::White, +macro_rules! into_int_type { + ($name:ident, $int_type:ident, $value:expr) => { + impl Into<$int_type> for $name { + fn into(self) -> $int_type { + $value + } } - } + }; } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum Shape { - Pawn = 0, - Knight = 1, - Bishop = 2, - Rook = 3, - Queen = 4, - King = 5, -} +macro_rules! color_struct { + ($name:ident, $index:expr, $other_color:ident) => { + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct $name; -impl Shape { - pub fn iter() -> Iter<'static, Shape> { - const ALL_SHAPES: [Shape; 6] = [ - Shape::Pawn, - Shape::Knight, - Shape::Bishop, - Shape::Rook, - Shape::Queen, - Shape::King, - ]; - - ALL_SHAPES.iter() - } - - pub fn promotable() -> Iter<'static, Shape> { - const PROMOTABLE_SHAPES: [Shape; 4] = - [Shape::Queen, Shape::Rook, Shape::Bishop, Shape::Knight]; - - PROMOTABLE_SHAPES.iter() - } - - fn _ascii_representation(&self) -> char { - match self { - Shape::Pawn => 'p', - Shape::Knight => 'N', - Shape::Bishop => 'B', - Shape::Rook => 'R', - Shape::Queen => 'Q', - Shape::King => 'K', + impl Color for $name { + type Other = $other_color; } - } + + into_int_type!($name, u8, $index); + into_int_type!($name, usize, $index); + }; } +color_struct!(White, 0, Black); +color_struct!(Black, 1, White); + +macro_rules! shape_struct { + ($name:ident, $index:expr, $char:expr, $white_char:expr, $black_char:expr) => { + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct $name; + + impl Shape for $name {} + + into_int_type!($name, u8, $index); + into_int_type!($name, usize, $index); + + impl Into for $name { + fn into(self) -> char { + $char + } + } + + impl fmt::Display for Piece<$name, White> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", $white_char) + } + } + + impl fmt::Display for Piece<$name, Black> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", $black_char) + } + } + }; +} + +pub trait Shape: Sized {} + +shape_struct!(Pawn, 0, 'P', '♙', '♟'); +shape_struct!(Knight, 1, 'N', '♘', '♞'); +shape_struct!(Bishop, 2, 'B', '♗', '♝'); +shape_struct!(Rook, 3, 'R', '♖', '♜'); +shape_struct!(Queen, 4, 'Q', '♕', '♛'); +shape_struct!(King, 5, 'K', '♔', '♚'); + #[derive(Debug, Eq, PartialEq)] pub struct TryFromError; -impl TryFrom for Shape { - type Error = TryFromError; - - fn try_from(value: char) -> Result { - match value { - 'p' => Ok(Shape::Pawn), - 'N' => Ok(Shape::Knight), - 'B' => Ok(Shape::Bishop), - 'R' => Ok(Shape::Rook), - 'Q' => Ok(Shape::Queen), - 'K' => Ok(Shape::King), - _ => Err(TryFromError), - } - } -} - -impl TryFrom<&str> for Shape { - type Error = TryFromError; - - fn try_from(value: &str) -> Result { - let first_char = value.chars().nth(0).ok_or(TryFromError)?; - Shape::try_from(first_char) - } -} - -impl Into for &Shape { - fn into(self) -> char { - self._ascii_representation() - } -} - -impl Into for Shape { - fn into(self) -> char { - self._ascii_representation() - } -} - -impl fmt::Display for Shape { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let self_char: char = self.into(); - write!(f, "{}", self_char) - } -} - #[derive(Debug, Eq, PartialEq)] pub enum PiecePlacementError { ExistsOnSquare, @@ -129,24 +92,28 @@ macro_rules! piece { } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct Piece { - color: Color, - shape: Shape, +pub struct Piece { + color: C, + shape: S, } macro_rules! piece_constructor { - ($func_name:ident, $type:tt) => { - pub fn $func_name(color: Color) -> Piece { + ($func_name:ident, $shape:tt) => { + pub fn $func_name(color: C) -> Piece { Piece { color, - shape: Shape::$type, + shape: $shape, } } }; } -impl Piece { - pub fn new(color: Color, shape: Shape) -> Piece { +impl Piece +where + C: Color, + S: Shape, +{ + pub fn new(color: C, shape: S) -> Piece { Piece { color, shape } } @@ -157,49 +124,28 @@ impl Piece { piece_constructor!(queen, Queen); piece_constructor!(king, King); - pub fn color(&self) -> Color { + pub fn color(&self) -> C { self.color } - pub fn shape(&self) -> Shape { + pub fn shape(&self) -> S { self.shape } } -impl fmt::Display for Piece { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let char = match (self.color, self.shape) { - (Color::White, Shape::Pawn) => '♟', - (Color::White, Shape::Knight) => '♞', - (Color::White, Shape::Bishop) => '♝', - (Color::White, Shape::Rook) => '♜', - (Color::White, Shape::Queen) => '♛', - (Color::White, Shape::King) => '♚', - (Color::Black, Shape::Pawn) => '♙', - (Color::Black, Shape::Knight) => '♘', - (Color::Black, Shape::Bishop) => '♗', - (Color::Black, Shape::Rook) => '♖', - (Color::Black, Shape::Queen) => '♕', - (Color::Black, Shape::King) => '♔', - }; - - write!(f, "{}", char) - } -} - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct PlacedPiece { - piece: Piece, +pub struct PlacedPiece { + piece: Piece, square: Square, } -impl PlacedPiece { - pub const fn new(piece: Piece, square: Square) -> PlacedPiece { +impl PlacedPiece { + pub const fn new(piece: Piece, square: Square) -> PlacedPiece { PlacedPiece { piece, square } } #[inline] - pub fn piece(&self) -> Piece { + pub fn piece(&self) -> Piece { self.piece } diff --git a/board/src/position/diagram_formatter.rs b/board/src/position/diagram_formatter.rs index 5187ace..731830a 100644 --- a/board/src/position/diagram_formatter.rs +++ b/board/src/position/diagram_formatter.rs @@ -3,15 +3,15 @@ use crate::{File, Position, Rank, Square}; use std::{fmt, fmt::Write}; -pub struct DiagramFormatter<'a>(&'a Position); +pub struct DiagramFormatter<'a, PosC>(&'a Position); -impl<'a> DiagramFormatter<'a> { - pub fn new(position: &'a Position) -> DiagramFormatter { +impl<'a, PosC> DiagramFormatter<'a, PosC> { + pub fn new(position: &'a Position) -> DiagramFormatter { DiagramFormatter(position) } } -impl<'a> fmt::Display for DiagramFormatter<'a> { +impl<'a, PosC> fmt::Display for DiagramFormatter<'a, PosC> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut output = String::new(); diff --git a/board/src/position/flags.rs b/board/src/position/flags.rs index 2aa389d..f8b05ec 100644 --- a/board/src/position/flags.rs +++ b/board/src/position/flags.rs @@ -8,19 +8,32 @@ pub(super) struct Flags(u8); impl Flags { #[inline] - pub(super) fn player_has_right_to_castle_flag_offset(color: Color, side: BoardSide) -> u8 { + pub(super) fn player_has_right_to_castle_flag_offset( + color: C, + side: BoardSide, + ) -> u8 { ((color as u8) << 1) + side as u8 } - pub(super) fn player_has_right_to_castle(&self, color: Color, side: BoardSide) -> bool { + pub(super) fn player_has_right_to_castle(&self, color: C, side: BoardSide) -> bool { (self.0 & (1 << Self::player_has_right_to_castle_flag_offset(color, side))) != 0 } - pub(super) fn set_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) { + #[cfg(test)] + pub(super) fn set_player_has_right_to_castle_flag( + &mut self, + color: C, + side: BoardSide, + ) { self.0 |= 1 << Self::player_has_right_to_castle_flag_offset(color, side); } - pub(super) fn clear_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) { + #[cfg(test)] + pub(super) fn clear_player_has_right_to_castle_flag( + &mut self, + color: C, + side: BoardSide, + ) { self.0 &= !(1 << Self::player_has_right_to_castle_flag_offset(color, side)); } } diff --git a/board/src/position/pieces.rs b/board/src/position/pieces.rs index e3cb87e..15f6695 100644 --- a/board/src/position/pieces.rs +++ b/board/src/position/pieces.rs @@ -2,33 +2,37 @@ use super::Position; use crate::bitboard::BitBoard; -use crate::piece::{Color, Piece, PlacedPiece, Shape}; +use crate::piece::*; use crate::Square; -pub struct Pieces<'a> { - color: Color, - position: &'a Position, +pub struct Pieces<'a, PosC, C: Color> { + color: C, + position: &'a Position, - current_shape: Option, + current_shape: Option, - shape_iterator: Box>, + shape_iterator: std::array::IntoIter, square_iterator: Option>>, } -impl<'a> Pieces<'a> { - pub(crate) fn new(position: &Position, color: Color) -> Pieces { +impl<'a, PosC, C> Pieces<'a, PosC, C> { + pub(crate) fn new(position: &Position, color: C) -> Pieces { Pieces { color, position, current_shape: None, - shape_iterator: Box::new(Shape::iter()), + shape_iterator: [Pawn, Knight, Bishop, Rook, Queen, King].into_iter(), square_iterator: None, } } } -impl<'a> Iterator for Pieces<'a> { - type Item = PlacedPiece; +impl<'a, PosC, C, S> Iterator for Pieces<'a, PosC, C> +where + C: Color, + S: Shape, +{ + type Item = PlacedPiece; fn next(&mut self) -> Option { if let Some(square_iterator) = &mut self.square_iterator { @@ -80,7 +84,11 @@ mod tests { use crate::Square; use std::collections::HashSet; - fn place_piece_in_position(pos: &mut Position, piece: Piece, sq: Square) { + fn place_piece_in_position( + pos: &mut Position, + piece: Piece, + sq: Square, + ) { pos.place_piece(piece, sq) .expect("Unable to place {piece:?} queen on {sq}"); } diff --git a/board/src/position/position.rs b/board/src/position/position.rs index 3ff2db6..73de794 100644 --- a/board/src/position/position.rs +++ b/board/src/position/position.rs @@ -2,9 +2,9 @@ use super::{flags::Flags, Pieces}; use crate::{ - bitboard::BitBoard, - moves::Moves, + //moves::Moves, piece::{Color, Piece, PiecePlacementError, PlacedPiece, Shape}, + BitBoard, Square, }; use std::fmt; @@ -28,16 +28,9 @@ macro_rules! position { } #[derive(Clone, Eq, Hash, PartialEq)] -pub struct Position { - color_to_move: Color, +pub struct Position { + color_to_move: ColorToMove, - /// Position flags indicating various bits of game state. The flags are as - /// follows: - /// - /// 0. white can castle king-side - /// 1. white can castle queen-side - /// 2. black can castle king-side - /// 3. black can castle queen-side flags: Flags, /// Composite bitboards for all the pieces of a particular color. @@ -45,12 +38,14 @@ pub struct Position { /// Bitboards representing positions of particular piece types per color. pieces_per_type: [[BitBoard; 6]; 2], + + en_passant_square: Option, } -impl Position { - pub fn empty() -> Position { +impl Position { + pub fn empty() -> Position { Position { - color_to_move: Color::White, + color_to_move: crate::piece::White, flags: Default::default(), pieces_per_color: [BitBoard::empty(); 2], pieces_per_type: [ @@ -71,11 +66,12 @@ impl Position { BitBoard::empty(), ], ], + en_passant_square: None, } } /// Return a starting position. - pub fn starting() -> Position { + pub fn starting() -> Position { let white_pieces = [ BitBoard::new(0x00FF000000000000), BitBoard::new(0x4200000000000000), @@ -95,13 +91,14 @@ impl Position { ]; Position { - color_to_move: Color::White, + color_to_move: crate::piece::White, flags: Default::default(), pieces_per_color: [ white_pieces.iter().fold(BitBoard::empty(), |a, b| a | *b), black_pieces.iter().fold(BitBoard::empty(), |a, b| a | *b), ], pieces_per_type: [white_pieces, black_pieces], + en_passant_square: None, } } @@ -118,20 +115,15 @@ impl Position { /// 2. The king must not be in check /// 3. In the course of castling on that side, the king must not pass /// through a square that an enemy piece can see - pub(crate) fn player_has_right_to_castle(&self, color: Color, side: BoardSide) -> bool { + pub(crate) fn player_has_right_to_castle(&self, color: C, side: BoardSide) -> bool { self.flags.player_has_right_to_castle(color, side) } - fn set_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) { - self.flags.set_player_has_right_to_castle_flag(color, side); - } - - fn clear_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) { - self.flags - .clear_player_has_right_to_castle_flag(color, side); - } - - pub fn place_piece(&mut self, piece: Piece, square: Square) -> Result<(), PiecePlacementError> { + pub fn place_piece( + &mut self, + piece: Piece, + square: Square, + ) -> Result<(), PiecePlacementError> { let type_bb = self.bitboard_for_piece_mut(piece); if type_bb.has_piece_at(square) { @@ -146,40 +138,27 @@ impl Position { Ok(()) } - pub fn move_generator(&self, color: Color) -> Moves { - Moves::new(self, color) - } + // pub fn move_generator(&self, color: C) -> Moves { + // Moves::new(self, color) + // } - /// Return a BitBoard representing the set of squares containing a piece. - #[inline] - pub(crate) fn occupied_squares(&self) -> BitBoard { - self.pieces_per_color[Color::White as usize] | self.pieces_per_color[Color::Black as usize] - } - - /// Return a BitBoard representing the set of squares containing a piece. - /// This set is the inverse of `occupied_squares`. - #[inline] - pub(crate) fn empty_squares(&self) -> BitBoard { - !self.occupied_squares() - } - - pub(crate) fn bitboard_for_piece(&self, piece: Piece) -> BitBoard { + pub(crate) fn bitboard_for_piece(&self, piece: Piece) -> BitBoard { self.pieces_per_type[piece.color() as usize][piece.shape() as usize] } - fn bitboard_for_piece_mut(&mut self, piece: Piece) -> &mut BitBoard { + fn bitboard_for_piece_mut(&mut self, piece: Piece) -> &mut BitBoard { &mut self.pieces_per_type[piece.color() as usize][piece.shape() as usize] } - pub(crate) fn bitboard_for_color(&self, color: Color) -> BitBoard { + pub(crate) fn bitboard_for_color(&self, color: C) -> BitBoard { self.pieces_per_color[color as usize] } - fn bitboard_for_color_mut(&mut self, color: Color) -> &mut BitBoard { + fn bitboard_for_color_mut(&mut self, color: C) -> &mut BitBoard { &mut self.pieces_per_color[color as usize] } - pub fn piece_on_square(&self, sq: Square) -> Option { + pub fn piece_on_square(&self, sq: Square) -> Option> { for color in Color::iter() { for shape in Shape::iter() { let piece = Piece::new(color, *shape); @@ -193,19 +172,76 @@ impl Position { None } - pub fn pieces(&self, color: Color) -> Pieces { + pub fn pieces(&self, color: C) -> Pieces { Pieces::new(&self, color) } + + pub fn king(&self, color: C) -> PlacedPiece { + let king = Piece::king(color); + let bitboard = self.bitboard_for_piece(king); + let square = bitboard.occupied_squares().next().unwrap(); + PlacedPiece::new(king, square) + } + + pub fn is_king_in_check(&self) -> bool { + false + } } -impl Default for Position { +impl Position { + /// Return a BitBoard representing the set of squares containing a piece. + #[inline] + pub(crate) fn occupied_squares(&self) -> BitBoard { + self.pieces_per_color[Color::White as usize] | self.pieces_per_color[Color::Black as usize] + } + + /// A BitBoard representing the set of empty squares. This set is the + /// inverse of `occupied_squares`. + #[inline] + pub(crate) fn empty_squares(&self) -> BitBoard { + !self.occupied_squares() + } + + /// A BitBoard representing squares occupied by the current player's pieces. + pub(crate) fn friendly_pieces(&self) -> BitBoard { + self.bitboard_for_color(self.color_to_move) + } + + /// A BitBoard representing squares occupied by the opposing player's pieces. + pub(crate) fn opposing_pieces(&self) -> BitBoard { + self.bitboard_for_color(self.color_to_move.other()) + } + + /// If the previous move create an en passant elibile square, return `Some`. + /// Otherwise, return `None`. + pub(crate) fn en_passant_square(&self) -> Option { + self.en_passant_square + } +} + +impl Position { + fn sight_of_pieces_of_color(&self, color: C) -> BitBoard { + self.pieces(color) + .map(|placed_piece| { + self.sight_of_piece(placed_piece) + .sight(placed_piece.square(), self) + }) + .fold(BitBoard::empty(), std::ops::BitOr::bitor) + } + + fn sight_of_piece(&self, placed_piece: PlacedPiece) -> BitBoard { + placed_piece.piece().sight() + } +} + +impl Default for Position { fn default() -> Self { Self::empty() } } -impl FromIterator for Position { - fn from_iter>(iter: T) -> Self { +impl FromIterator> for Position { + fn from_iter>>(iter: T) -> Self { let mut position = Position::empty(); for placed_piece in iter { _ = position.place_piece(placed_piece.piece(), placed_piece.square()); @@ -215,7 +251,7 @@ impl FromIterator for Position { } } -impl fmt::Debug for Position { +impl fmt::Debug for Position { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut output = String::new(); @@ -245,7 +281,7 @@ impl fmt::Debug for Position { #[cfg(test)] mod tests { use super::*; - use crate::piece::Shape; + use crate::{piece::Shape, position::DiagramFormatter}; #[test] fn place_piece() { @@ -273,6 +309,24 @@ mod tests { } #[test] + fn king_is_not_in_check() { + let position = position![ + White King on E4, + Black Rook on D8, + ]; + println!("{}", DiagramFormatter::new(&position)); + assert!(!position.is_king_in_check()); + } + + #[test] + fn king_is_in_check() { + let position = position![ + White King on E4, + Black Rook on E8, + ]; + println!("{}", DiagramFormatter::new(&position)); + + assert!(position.is_king_in_check()); } }