Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
0d8653894a 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.
2024-01-14 10:23:35 -08:00
19 changed files with 499 additions and 320 deletions

View file

@ -1,7 +1,7 @@
// Eryn Wells <eryn@erynwells.me>
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};

9
board/src/display.rs Normal file
View file

@ -0,0 +1,9 @@
// Eryn Wells <eryn@erynwells.me>
pub trait ASCIIDisplay {
fn fmt(&self, &mut f: fmt::Formatter) -> fmt::Result;
}
pub trait UnicodeDisplay {
fn fmt(&self, &mut f: fmt::Formatter) -> fmt::Result;
}

View file

@ -1,13 +1,15 @@
// Eryn Wells <eryn@erynwells.me>
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};

View file

@ -1,10 +1,8 @@
// Eryn Wells <eryn@erynwells.me>
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);

View file

@ -0,0 +1 @@
// Eryn Wells <eryn@erynwells.me>

View file

@ -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;

View file

@ -1,6 +1,7 @@
// Eryn Wells <eryn@erynwells.me>
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,

View file

@ -1,41 +1,86 @@
// Eryn Wells <eryn@erynwells.me>
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<PlacedPiece>,
promoting_to: Option<Shape>,
/// A move that transfers a piece from one square to another.
trait Move<C: Color, S: Shape>: Sized {
fn piece(&self) -> Piece<C, S>;
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<C: Color, S: Shape, CapturedS: Shape>: Move<C, S> {
type CaptureMove;
fn capturing(self, capturing: CapturedS) -> Self::CaptureMove;
fn captured_piece(&self) -> Piece<C::Other, CapturedS>;
}
/// A move that promotes a pawn to another piece.
trait Promoting<C: Color, S: Shape, PromotingS: Shape>: Move<C, Pawn> {
type PromotionMove;
fn promoting_to(self, shape: PromotingS) -> Self::PromotionMove;
fn promoting_piece(&self) -> Piece<C, S>;
}
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
pub struct SimpleMove<C, S> {
piece: Piece<C, S>,
from_square: Square,
to_square: Square,
}
impl<C: Color, S: Shape> SimpleMove<C, S> {
fn new(piece: Piece<C, S>, 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<CapturedS: Shape>(
self,
captured_piece: Piece<C::Other, CapturedS>,
) -> Capture<C, S, CapturedS> {
Capture {
r#move: self,
captured_piece,
}
}
}
impl<C, S> Move<C, S> for Piece<C, S> {
fn piece(&self) -> Piece<C, S> {
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<C: Color, S: Shape, CapturedS: Shape> {
r#move: dyn Move<C, S>,
captured_piece: Piece<C::Other, CapturedS>,
}
pub struct Promotion<C: Color, PromS: Shape> {
r#move: dyn Move<C, Pawn>,
promoting_to_shape: PromS,
}
pub struct CapturingMove<C: Color, S: Shape, CapturingShape: Shape> {
capturing: PlacedPiece<C::Other, CapturingShape>,
}
impl<C: Color, S: Shape> Move<C, S> {
pub fn is_castle(&self) -> bool {
let color = self.piece.color();
self.piece.shape() == Shape::King

View file

@ -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>,

View file

@ -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<Move>,
captures: Vec<Move>,
}
/// A set of moves for a piece on the board.
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct MoveSet {
piece: PlacedPiece,
bitboards: BitBoardSet,

View file

@ -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,

View file

@ -1,10 +1,8 @@
// Eryn Wells <eryn@erynwells.me>
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;

View file

@ -1,10 +1,8 @@
// Eryn Wells <eryn@erynwells.me>
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;

165
board/src/moves/sight.rs Normal file
View file

@ -0,0 +1,165 @@
// Eryn Wells <eryn@erynwells.me>
use crate::{piece::*, square::Direction, BitBoard, Position, Square};
pub(crate) trait Sight {
fn sight_on_empty_board(self, square: Square) -> BitBoard;
fn sight<PosC>(self, square: Square, position: &Position<PosC>) -> BitBoard;
}
impl Sight for Piece<White, Pawn> {
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<PosC>(self, square: Square, position: &Position<PosC>) -> 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<Black, Pawn> {
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<PosC>(self, square: Square, position: &Position<PosC>) -> 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<C> Sight for Piece<C, Knight> {
fn sight_on_empty_board(self, square: Square) -> BitBoard {
BitBoard::knight_moves(square)
}
fn sight<PosC>(self, square: Square, position: &Position<PosC>) -> BitBoard {
self.sight_on_empty_board(square) & !position.friendly_pieces()
}
}
impl<C> Sight for Piece<C, Bishop> {
fn sight_on_empty_board(self, square: Square) -> BitBoard {
BitBoard::bishop_moves(square)
}
fn sight<PosC>(self, square: Square, position: &Position<PosC>) -> 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<C> Sight for Piece<C, Rook> {
fn sight_on_empty_board(self, square: Square) -> BitBoard {
BitBoard::rook_moves(square)
}
fn sight<PosC>(self, square: Square, position: &Position<PosC>) -> 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<C> Sight for Piece<C, Queen> {
fn sight_on_empty_board(self, square: Square) -> BitBoard {
BitBoard::queen_moves(square)
}
fn sight<PosC>(self, square: Square, position: &Position<PosC>) -> 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<C> Sight for Piece<C, King> {
fn sight_on_empty_board(self, square: Square) -> BitBoard {
BitBoard::king_moves(square)
}
fn sight<PosC>(self, square: Square, position: &Position<PosC>) -> BitBoard {
self.sight_on_empty_board(square) & !position.friendly_pieces()
}
}

View file

@ -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<Item = Color> {
[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<char> 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<char> for Shape {
type Error = TryFromError;
fn try_from(value: char) -> Result<Self, Self::Error> {
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<Self, Self::Error> {
let first_char = value.chars().nth(0).ok_or(TryFromError)?;
Shape::try_from(first_char)
}
}
impl Into<char> for &Shape {
fn into(self) -> char {
self._ascii_representation()
}
}
impl Into<char> 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<C: Color, S: Shape> {
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<C, S> {
Piece {
color,
shape: Shape::$type,
shape: $shape,
}
}
};
}
impl Piece {
pub fn new(color: Color, shape: Shape) -> Piece {
impl<C, S> Piece<C, S>
where
C: Color,
S: Shape,
{
pub fn new(color: C, shape: S) -> Piece<C, S> {
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<C, S> {
piece: Piece<C, S>,
square: Square,
}
impl PlacedPiece {
pub const fn new(piece: Piece, square: Square) -> PlacedPiece {
impl<C, S> PlacedPiece<C, S> {
pub const fn new(piece: Piece<C, S>, square: Square) -> PlacedPiece<C, S> {
PlacedPiece { piece, square }
}
#[inline]
pub fn piece(&self) -> Piece {
pub fn piece(&self) -> Piece<C, S> {
self.piece
}

View file

@ -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<PosC>);
impl<'a> DiagramFormatter<'a> {
pub fn new(position: &'a Position) -> DiagramFormatter {
impl<'a, PosC> DiagramFormatter<'a, PosC> {
pub fn new(position: &'a Position<PosC>) -> DiagramFormatter<PosC> {
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();

View file

@ -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<C: Color>(
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<C: Color>(&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<C: Color>(
&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<C: Color>(
&mut self,
color: C,
side: BoardSide,
) {
self.0 &= !(1 << Self::player_has_right_to_castle_flag_offset(color, side));
}
}

View file

@ -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<PosC>,
current_shape: Option<Shape>,
current_shape: Option<dyn Shape>,
shape_iterator: Box<dyn Iterator<Item = &'static Shape>>,
shape_iterator: std::array::IntoIter<dyn Shape, 6>,
square_iterator: Option<Box<dyn Iterator<Item = Square>>>,
}
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<PosC>, color: C) -> Pieces<PosC, C> {
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<C, S>;
fn next(&mut self) -> Option<Self::Item> {
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<PosC, C, S>(
pos: &mut Position<PosC>,
piece: Piece<C, S>,
sq: Square,
) {
pos.place_piece(piece, sq)
.expect("Unable to place {piece:?} queen on {sq}");
}

View file

@ -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<ColorToMove> {
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<Square>,
}
impl Position {
pub fn empty() -> Position {
impl<ColorToMove> Position<ColorToMove> {
pub fn empty() -> Position<ColorToMove> {
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<ColorToMove> {
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<C>(&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<C, S>(
&mut self,
piece: Piece<C, S>,
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<C>(&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<C, S>(&self, piece: Piece<C, S>) -> 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<C, S>(&mut self, piece: Piece<C, S>) -> &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<C>(&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<C>(&mut self, color: C) -> &mut BitBoard {
&mut self.pieces_per_color[color as usize]
}
pub fn piece_on_square(&self, sq: Square) -> Option<PlacedPiece> {
pub fn piece_on_square<C, S>(&self, sq: Square) -> Option<PlacedPiece<C, S>> {
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<C>(&self, color: C) -> Pieces<ColorToMove, C> {
Pieces::new(&self, color)
}
pub fn king<C>(&self, color: C) -> PlacedPiece<C, crate::piece::King> {
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<ColorToMove> Position<ColorToMove> {
/// 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<Square> {
self.en_passant_square
}
}
impl<ColorToMove> Position<ColorToMove> {
fn sight_of_pieces_of_color<C>(&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<C, S>(&self, placed_piece: PlacedPiece<C, S>) -> BitBoard {
placed_piece.piece().sight()
}
}
impl<ColorToMove> Default for Position<ColorToMove> {
fn default() -> Self {
Self::empty()
}
}
impl FromIterator<PlacedPiece> for Position {
fn from_iter<T: IntoIterator<Item = PlacedPiece>>(iter: T) -> Self {
impl<ColorToMove, C, S> FromIterator<PlacedPiece<C, S>> for Position<ColorToMove> {
fn from_iter<T: IntoIterator<Item = PlacedPiece<C, S>>>(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<PlacedPiece> for Position {
}
}
impl fmt::Debug for Position {
impl<ColorToMove> fmt::Debug for Position<ColorToMove> {
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());
}
}