[core,board] Move board::piece to core

Break up types in core into finer grained modules.
Update all the imports.
This commit is contained in:
Eryn Wells 2024-01-24 17:08:27 -08:00
parent 6f85305912
commit 8b2a3926b3
25 changed files with 235 additions and 227 deletions

62
core/src/colors.rs Normal file
View file

@ -0,0 +1,62 @@
// Eryn Wells <eryn@erynwells.me>
use crate::try_from_string;
use std::fmt;
#[derive(Debug, Eq, PartialEq)]
pub enum TryFromError {
InvalidCharacter,
ZeroLengthString,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Color {
White = 0,
Black = 1,
}
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,
}
}
}
impl Default for Color {
fn default() -> Self {
Color::White
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Color::White => "White",
Color::Black => "Black",
},
)
}
}
impl TryFrom<char> for Color {
type Error = TryFromError;
fn try_from(value: char) -> Result<Self, Self::Error> {
match value {
'w' | 'W' => Ok(Color::White),
'b' | 'B' => Ok(Color::Black),
_ => Err(TryFromError::InvalidCharacter),
}
}
}
try_from_string!(Color);

7
core/src/errors.rs Normal file
View file

@ -0,0 +1,7 @@
// Eryn Wells <eryn@erynwells.me>
#[derive(Debug, Eq, PartialEq)]
pub struct TryFromCharError;
#[derive(Debug, Eq, PartialEq)]
pub struct TryFromStrError;

View file

@ -1,3 +1,12 @@
mod coordinates;
// Eryn Wells <eryn@erynwells.me>
pub mod colors;
pub mod coordinates;
pub mod errors;
pub mod pieces;
mod macros;
pub use colors::Color;
pub use coordinates::{Direction, File, Rank, Square};
pub use pieces::{Piece, PlacedPiece, Shape};

32
core/src/macros.rs Normal file
View file

@ -0,0 +1,32 @@
// Eryn Wells <eryn@erynwells.me>
#[macro_export]
macro_rules! try_from_string {
($type:ty) => {
try_from_string!($type, &str);
try_from_string!($type, &String);
};
($type:ty, $from_type:ty) => {
impl TryFrom<$from_type> for $type {
type Error = $crate::errors::TryFromStrError;
fn try_from(value: $from_type) -> Result<Self, Self::Error> {
let first_char = value
.chars()
.nth(0)
.ok_or($crate::errors::TryFromStrError)?;
Self::try_from(first_char).map_err(|_| $crate::errors::TryFromStrError)
}
}
};
}
#[macro_export]
macro_rules! piece {
($color:ident $shape:ident) => {
$crate::Piece::new($crate::Color::$color, $crate::Shape::$shape)
};
($color:ident $shape:ident on $square:ident) => {
$crate::PlacedPiece::new(piece!($color $shape), $crate::Square::$square)
}
}

255
core/src/pieces.rs Normal file
View file

@ -0,0 +1,255 @@
// Eryn Wells <eryn@erynwells.me>
use crate::{errors::TryFromCharError, try_from_string, Color, Square};
use std::{fmt, slice::Iter};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Shape {
Pawn = 0,
Knight = 1,
Bishop = 2,
Rook = 3,
Queen = 4,
King = 5,
}
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 to_ascii(&self) -> char {
match self {
Shape::Pawn => 'P',
Shape::Knight => 'N',
Shape::Bishop => 'B',
Shape::Rook => 'R',
Shape::Queen => 'Q',
Shape::King => 'K',
}
}
}
impl TryFrom<char> for Shape {
type Error = TryFromCharError;
fn try_from(value: char) -> Result<Self, Self::Error> {
match value {
'P' | 'p' => Ok(Shape::Pawn),
'N' | 'n' => Ok(Shape::Knight),
'B' | 'b' => Ok(Shape::Bishop),
'R' | 'r' => Ok(Shape::Rook),
'Q' | 'q' => Ok(Shape::Queen),
'K' | 'k' => Ok(Shape::King),
_ => Err(TryFromCharError),
}
}
}
try_from_string!(Shape);
impl Into<char> for &Shape {
fn into(self) -> char {
self.to_ascii()
}
}
impl Into<char> for Shape {
fn into(self) -> char {
self.to_ascii()
}
}
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(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Piece {
color: Color,
shape: Shape,
}
macro_rules! piece_constructor {
($func_name:ident, $type:tt) => {
pub fn $func_name(color: Color) -> Piece {
Piece {
color,
shape: Shape::$type,
}
}
};
}
macro_rules! is_shape {
($func_name:ident, $shape:ident) => {
pub fn $func_name(&self) -> bool {
self.shape == Shape::$shape
}
};
}
impl Piece {
pub fn new(color: Color, shape: Shape) -> Piece {
Piece { color, shape }
}
piece_constructor!(pawn, Pawn);
piece_constructor!(knight, Knight);
piece_constructor!(bishop, Bishop);
piece_constructor!(rook, Rook);
piece_constructor!(queen, Queen);
piece_constructor!(king, King);
pub fn color(&self) -> Color {
self.color
}
pub fn shape(&self) -> Shape {
self.shape
}
is_shape!(is_pawn, Pawn);
is_shape!(is_knight, Knight);
is_shape!(is_bishop, Bishop);
is_shape!(is_rook, Rook);
is_shape!(is_queen, Queen);
is_shape!(is_king, King);
pub fn to_ascii(&self) -> char {
self.shape.to_ascii()
}
}
impl fmt::Display for Piece {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match (self.color, self.shape) {
(Color::Black, Shape::Pawn) => '♟',
(Color::Black, Shape::Knight) => '♞',
(Color::Black, Shape::Bishop) => '♝',
(Color::Black, Shape::Rook) => '♜',
(Color::Black, Shape::Queen) => '♛',
(Color::Black, Shape::King) => '♚',
(Color::White, Shape::Pawn) => '♙',
(Color::White, Shape::Knight) => '♘',
(Color::White, Shape::Bishop) => '♗',
(Color::White, Shape::Rook) => '♖',
(Color::White, Shape::Queen) => '♕',
(Color::White, Shape::King) => '♔',
}
)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct PlacedPiece {
piece: Piece,
square: Square,
}
macro_rules! is_shape {
($func_name:ident, $shape:ident) => {
pub fn $func_name(&self) -> bool {
self.piece().shape == Shape::$shape
}
};
}
impl PlacedPiece {
pub const fn new(piece: Piece, square: Square) -> PlacedPiece {
PlacedPiece { piece, square }
}
#[inline]
pub fn piece(&self) -> &Piece {
&self.piece
}
#[inline]
pub fn square(&self) -> Square {
self.square
}
#[inline]
pub fn color(&self) -> Color {
self.piece.color
}
#[inline]
pub fn shape(&self) -> Shape {
self.piece.shape
}
is_shape!(is_pawn, Pawn);
is_shape!(is_knight, Knight);
is_shape!(is_bishop, Bishop);
is_shape!(is_rook, Rook);
is_shape!(is_queen, Queen);
is_shape!(is_king, King);
pub fn is_kingside_rook(&self) -> bool {
self.is_rook()
&& match self.color() {
Color::White => self.square == Square::H1,
Color::Black => self.square == Square::H8,
}
}
pub fn is_queenside_rook(&self) -> bool {
self.is_rook()
&& match self.color() {
Color::White => self.square == Square::A1,
Color::Black => self.square == Square::A8,
}
}
}
impl fmt::Display for PlacedPiece {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.piece, self.square)
}
}
impl From<(&Square, &Piece)> for PlacedPiece {
fn from(value: (&Square, &Piece)) -> Self {
PlacedPiece::new(*value.1, *value.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shape_try_from() {
assert_eq!(Shape::try_from('p'), Ok(Shape::Pawn));
assert_eq!(Shape::try_from("p"), Ok(Shape::Pawn));
}
#[test]
fn shape_into_char() {
assert_eq!(<Shape as Into<char>>::into(Shape::Pawn) as char, 'P');
}
}