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()); } }