[board] Implement a u16 based Move
Replace building a Move with the Move struct itself with a MoveBuilder that builds a Move and returns it. Update the tests and move formatter.
This commit is contained in:
parent
96b04379a4
commit
177a4e32da
5 changed files with 398 additions and 213 deletions
|
@ -4,6 +4,7 @@
|
|||
mod bitboard;
|
||||
mod display;
|
||||
mod moves;
|
||||
mod r#move;
|
||||
#[macro_use]
|
||||
pub mod piece;
|
||||
#[macro_use]
|
||||
|
@ -11,8 +12,8 @@ pub mod position;
|
|||
mod sight;
|
||||
mod square;
|
||||
|
||||
pub use moves::Move;
|
||||
pub use position::Position;
|
||||
pub use r#move::{Move, MoveBuilder};
|
||||
pub use square::{File, Rank, Square};
|
||||
|
||||
pub(crate) use bitboard::BitBoard;
|
||||
|
|
395
board/src/move.rs
Normal file
395
board/src/move.rs
Normal file
|
@ -0,0 +1,395 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::{
|
||||
piece::{Piece, PlacedPiece, Shape},
|
||||
position::BoardSide,
|
||||
square::Rank,
|
||||
Square,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Castle {
|
||||
KingSide = 0b10,
|
||||
QueenSide = 0b11,
|
||||
}
|
||||
|
||||
impl From<BoardSide> for Castle {
|
||||
fn from(value: BoardSide) -> Self {
|
||||
match value {
|
||||
BoardSide::King => Castle::KingSide,
|
||||
BoardSide::Queen => Castle::QueenSide,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum PromotableShape {
|
||||
Knight = 0b00,
|
||||
Bishop = 0b01,
|
||||
Rook = 0b10,
|
||||
Queen = 0b11,
|
||||
}
|
||||
|
||||
impl TryFrom<Shape> for PromotableShape {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: Shape) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Shape::Knight => Ok(PromotableShape::Knight),
|
||||
Shape::Bishop => Ok(PromotableShape::Bishop),
|
||||
Shape::Rook => Ok(PromotableShape::Rook),
|
||||
Shape::Queen => Ok(PromotableShape::Queen),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
enum Kind {
|
||||
Quiet = 0b00,
|
||||
DoublePush = 0b01,
|
||||
Castle(Castle),
|
||||
Capture(PlacedPiece) = 0b0100,
|
||||
EnPassantCapture(PlacedPiece) = 0b0101,
|
||||
Promotion(PromotableShape) = 0b1000,
|
||||
CapturePromotion(PlacedPiece, PromotableShape) = 0b1100,
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
fn bits(&self) -> u16 {
|
||||
match self {
|
||||
Self::Promotion(shape) => self.discriminant() | *shape as u16,
|
||||
Self::CapturePromotion(_, shape) => self.discriminant() | *shape as u16,
|
||||
Self::Castle(castle) => *castle as u16,
|
||||
_ => self.discriminant(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the discriminant value. This implementation is copied from the Rust docs.
|
||||
/// See https://doc.rust-lang.org/std/mem/fn.discriminant.html
|
||||
fn discriminant(&self) -> u16 {
|
||||
unsafe { *<*const _>::from(self).cast::<u16>() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Kind {
|
||||
fn default() -> Self {
|
||||
Self::Quiet
|
||||
}
|
||||
}
|
||||
|
||||
/// A single player's move. In chess parlance, this is a "ply".
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
pub struct Move(u16);
|
||||
|
||||
impl Move {
|
||||
pub fn from_square(&self) -> Square {
|
||||
((self.0 >> 4) & 0b111111).try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn to_square(&self) -> Square {
|
||||
(self.0 >> 10).try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn is_quiet(&self) -> bool {
|
||||
self.special() == 0b00
|
||||
}
|
||||
|
||||
pub fn is_double_push(&self) -> bool {
|
||||
self.special() == 0b01
|
||||
}
|
||||
|
||||
pub fn is_castle(&self) -> bool {
|
||||
self.castle().is_some()
|
||||
}
|
||||
|
||||
pub fn castle(&self) -> Option<Castle> {
|
||||
match self.special() {
|
||||
0b0010 => Some(Castle::KingSide),
|
||||
0b0011 => Some(Castle::QueenSide),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_capture(&self) -> bool {
|
||||
(self.0 & 0b0100) != 0
|
||||
}
|
||||
|
||||
pub fn is_en_passant(&self) -> bool {
|
||||
(self.0 & 0b0101) != 0
|
||||
}
|
||||
|
||||
pub fn is_promotion(&self) -> bool {
|
||||
(self.0 & 0b1000) != 0
|
||||
}
|
||||
|
||||
pub fn promotion(&self) -> Option<Shape> {
|
||||
if !self.is_promotion() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(match self.special() {
|
||||
0b00 => Shape::Knight,
|
||||
0b01 => Shape::Bishop,
|
||||
0b10 => Shape::Rook,
|
||||
0b11 => Shape::Queen,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn special(&self) -> u16 {
|
||||
self.0 & 0b11
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Move {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("Move")
|
||||
.field(&format_args!("{:08b}", &self.0))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MoveBuilder {
|
||||
piece: Piece,
|
||||
from: Square,
|
||||
to: Square,
|
||||
kind: Kind,
|
||||
}
|
||||
|
||||
impl MoveBuilder {
|
||||
pub fn new(piece: Piece, from: Square, to: Square) -> Self {
|
||||
let kind = match piece.shape() {
|
||||
Shape::Pawn => {
|
||||
let is_white_double_push = from.rank() == Rank::Two && to.rank() == Rank::Four;
|
||||
let is_black_double_push = from.rank() == Rank::Seven && to.rank() == Rank::Five;
|
||||
if is_white_double_push || is_black_double_push {
|
||||
Kind::DoublePush
|
||||
} else {
|
||||
Kind::Quiet
|
||||
}
|
||||
}
|
||||
_ => Kind::Quiet,
|
||||
};
|
||||
|
||||
Self {
|
||||
piece,
|
||||
from,
|
||||
to,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn castle(mut self, castle: Castle) -> Self {
|
||||
self.kind = Kind::Castle(castle);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn capturing(mut self, captured_piece: PlacedPiece) -> Self {
|
||||
self.kind = match self.kind {
|
||||
Kind::Promotion(shape) => Kind::CapturePromotion(captured_piece, shape),
|
||||
_ => Kind::Capture(captured_piece),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn promoting_to(mut self, shape: Shape) -> Self {
|
||||
if let Some(shape) = PromotableShape::try_from(shape).ok() {
|
||||
self.kind = match self.kind {
|
||||
Kind::Capture(piece) => Kind::CapturePromotion(piece, shape),
|
||||
Kind::CapturePromotion(piece, _) => Kind::CapturePromotion(piece, shape),
|
||||
_ => Kind::Promotion(shape),
|
||||
};
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Move {
|
||||
Move(
|
||||
self.kind.bits()
|
||||
| ((self.from as u16 & 0b111111) << 4)
|
||||
| ((self.to as u16 & 0b111111) << 10),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
mod move_formatter {
|
||||
use super::{Castle, Move};
|
||||
use crate::{piece::Shape, Position};
|
||||
use std::fmt;
|
||||
|
||||
enum Style {
|
||||
Short,
|
||||
Long,
|
||||
}
|
||||
|
||||
pub(crate) struct AlgebraicMoveFormatter<'m, 'pos> {
|
||||
position: &'pos Position,
|
||||
r#move: &'m Move,
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl<'pos, 'm> AlgebraicMoveFormatter<'m, 'pos> {
|
||||
pub(crate) fn new(
|
||||
mv: &'m Move,
|
||||
position: &'pos Position,
|
||||
) -> AlgebraicMoveFormatter<'m, 'pos> {
|
||||
AlgebraicMoveFormatter {
|
||||
position,
|
||||
r#move: mv,
|
||||
style: Style::Short,
|
||||
}
|
||||
}
|
||||
|
||||
fn style(mut self, style: Style) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
fn fmt_kingside_castle(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "0-0")
|
||||
}
|
||||
|
||||
fn fmt_queenside_castle(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "0-0-0")
|
||||
}
|
||||
|
||||
fn fmt_short(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn fmt_long(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO: Figure out how to write the short algebraic form, where a
|
||||
// disambiguating coordiate is specified when two of the same piece
|
||||
// cam move to the same square.
|
||||
|
||||
// TODO: Write better pawn moves.
|
||||
|
||||
let mv = self.r#move;
|
||||
let from_square = mv.from_square();
|
||||
let to_square = mv.to_square();
|
||||
|
||||
let piece = self
|
||||
.position
|
||||
.piece_on_square(from_square)
|
||||
.expect(&format!("No piece on {}", from_square));
|
||||
if piece.shape() != Shape::Pawn {
|
||||
write!(f, "{}", piece.shape())?;
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{}{}{}",
|
||||
from_square,
|
||||
if mv.is_capture() { 'x' } else { '-' },
|
||||
to_square,
|
||||
)?;
|
||||
|
||||
if let Some(promotion) = mv.promotion() {
|
||||
write!(f, "={}", promotion)?;
|
||||
}
|
||||
|
||||
// TODO: Write check (+) and checkmate (#) symbols
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'pos, 'mv> fmt::Display for AlgebraicMoveFormatter<'mv, 'pos> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mv = self.r#move;
|
||||
match mv.castle() {
|
||||
Some(Castle::KingSide) => return self.fmt_kingside_castle(f),
|
||||
Some(Castle::QueenSide) => return self.fmt_queenside_castle(f),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match self.style {
|
||||
Style::Short => self.fmt_short(f),
|
||||
Style::Long => self.fmt_long(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AlgebraicMoveFormatter, Style};
|
||||
use crate::{piece, position};
|
||||
|
||||
macro_rules! chess_move {
|
||||
($color:ident $shape:ident $from_square:ident - $to_square:ident) => {
|
||||
$crate::MoveBuilder::new(
|
||||
$crate::piece::Piece::new(
|
||||
$crate::piece::Color::$color,
|
||||
$crate::piece::Shape::$shape,
|
||||
),
|
||||
$crate::Square::$from_square,
|
||||
$crate::Square::$to_square,
|
||||
)
|
||||
.build()
|
||||
};
|
||||
($color:ident $shape:ident $from_square:ident x $to_square:ident, $captured_color:ident $captured_shape:ident) => {
|
||||
$crate::MoveBuilder::new(
|
||||
$crate::piece::Piece::new(
|
||||
$crate::piece::Color::$color,
|
||||
$crate::piece::Shape::$shape,
|
||||
),
|
||||
$crate::Square::$from_square,
|
||||
$crate::Square::$to_square,
|
||||
)
|
||||
.capturing($crate::piece::PlacedPiece::new(
|
||||
$crate::piece::Piece::new(
|
||||
$crate::piece::Color::$captured_color,
|
||||
$crate::piece::Shape::$captured_shape,
|
||||
),
|
||||
$crate::Square::$to_square,
|
||||
))
|
||||
.build()
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! test_algebraic_formatter {
|
||||
($test_name:ident, $style:ident, $color:ident $shape:ident $from_square:ident x $to_square:ident, $captured_color:ident $captured_shape:ident, $output:expr) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
let pos = position![
|
||||
$color $shape on $from_square,
|
||||
$captured_color $captured_shape on $to_square,
|
||||
];
|
||||
let mv = chess_move!(
|
||||
$color $shape $from_square x $to_square,
|
||||
$captured_color $captured_shape
|
||||
);
|
||||
|
||||
println!("{:?}", &mv);
|
||||
|
||||
let formatter = AlgebraicMoveFormatter::new(&mv, &pos).style(Style::$style);
|
||||
assert_eq!(format!("{}", formatter), $output);
|
||||
}
|
||||
};
|
||||
($test_name:ident, $style:ident, $color:ident $shape:ident $from_square:ident - $to_square:ident, $output:expr) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
let pos = position![
|
||||
$color $shape on $from_square,
|
||||
];
|
||||
|
||||
let mv = chess_move!($color $shape $from_square-$to_square);
|
||||
println!("{:?}", &mv);
|
||||
|
||||
let formatter = AlgebraicMoveFormatter::new(&mv, &pos).style(Style::$style);
|
||||
assert_eq!(format!("{}", formatter), $output);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_algebraic_formatter!(long_pawn_move, Long, White Pawn E4 - E5, "e4-e5");
|
||||
test_algebraic_formatter!(long_bishop_move, Long, White Bishop A4 - D7, "Ba4-d7");
|
||||
test_algebraic_formatter!(long_bishop_capture, Long, White Bishop A2 x E6, Black Knight, "Ba2xe6");
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
mod bishop;
|
||||
mod king;
|
||||
mod knight;
|
||||
mod r#move;
|
||||
mod move_generator;
|
||||
mod move_set;
|
||||
mod pawn;
|
||||
|
@ -11,7 +10,6 @@ mod queen;
|
|||
mod rook;
|
||||
|
||||
pub use move_generator::Moves;
|
||||
pub use r#move::Move;
|
||||
|
||||
pub(self) use move_set::MoveSet;
|
||||
|
||||
|
|
|
@ -1,210 +0,0 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::{
|
||||
piece::{Piece, PlacedPiece, Shape},
|
||||
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>,
|
||||
}
|
||||
|
||||
impl Move {
|
||||
pub fn new(piece: Piece, from: Square, to: Square) -> Move {
|
||||
Move {
|
||||
piece,
|
||||
from,
|
||||
to,
|
||||
capturing: None,
|
||||
promoting_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn capturing(mut self, piece: PlacedPiece) -> Move {
|
||||
self.capturing = Some(piece);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn promoting_to(mut self, shape: Shape) -> Move {
|
||||
self.promoting_to = Some(shape);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_castle(&self) -> bool {
|
||||
let color = self.piece.color();
|
||||
self.piece.shape() == Shape::King
|
||||
&& self.from == Square::KING_STARTING_SQUARES[color as usize]
|
||||
&& Square::KING_CASTLE_TARGET_SQUARES[color as usize].contains(&self.to)
|
||||
}
|
||||
|
||||
pub fn is_kingside_castle(&self) -> bool {
|
||||
let color = self.piece.color();
|
||||
self.piece.shape() == Shape::King
|
||||
&& self.from == Square::KING_STARTING_SQUARES[color as usize]
|
||||
&& self.to
|
||||
== Square::KING_CASTLE_TARGET_SQUARES[color as usize][BoardSide::King as usize]
|
||||
}
|
||||
|
||||
pub fn is_queenside_castle(&self) -> bool {
|
||||
let color = self.piece.color();
|
||||
self.piece.shape() == Shape::King
|
||||
&& self.from == Square::KING_STARTING_SQUARES[color as usize]
|
||||
&& self.to
|
||||
== Square::KING_CASTLE_TARGET_SQUARES[color as usize][BoardSide::Queen as usize]
|
||||
}
|
||||
|
||||
pub fn is_capture(&self) -> bool {
|
||||
self.capturing.is_some()
|
||||
}
|
||||
|
||||
pub fn is_promotion(&self) -> bool {
|
||||
self.promoting_to.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
mod move_formatter {
|
||||
use super::Move;
|
||||
use crate::{piece::Shape, Position};
|
||||
use std::fmt;
|
||||
|
||||
enum Style {
|
||||
Short,
|
||||
Long,
|
||||
}
|
||||
|
||||
pub(crate) struct AlgebraicMoveFormatter<'m> {
|
||||
r#move: &'m Move,
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl<'pos, 'm> AlgebraicMoveFormatter<'m> {
|
||||
pub(crate) fn new(mv: &'m Move) -> AlgebraicMoveFormatter<'m> {
|
||||
AlgebraicMoveFormatter {
|
||||
r#move: mv,
|
||||
style: Style::Short,
|
||||
}
|
||||
}
|
||||
|
||||
fn style(mut self, style: Style) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
fn fmt_kingside_castle(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "0-0")
|
||||
}
|
||||
|
||||
fn fmt_queenside_castle(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "0-0-0")
|
||||
}
|
||||
|
||||
fn fmt_short(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn fmt_long(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO: Figure out how to write the short algebraic form, where a
|
||||
// disambiguating coordiate is specified when two of the same piece
|
||||
// cam move to the same square.
|
||||
|
||||
// TODO: Write better pawn moves.
|
||||
|
||||
let mv = self.r#move;
|
||||
|
||||
let shape = mv.piece.shape();
|
||||
if shape != Shape::Pawn {
|
||||
write!(f, "{}", shape)?;
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{}{}{}",
|
||||
mv.from,
|
||||
if mv.is_capture() { 'x' } else { '-' },
|
||||
mv.to,
|
||||
)?;
|
||||
|
||||
if let Some(promoting_to) = mv.promoting_to {
|
||||
write!(f, "={}", promoting_to)?;
|
||||
}
|
||||
|
||||
// TODO: Write check (+) and checkmate (#) symbols
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'pos, 'mv> fmt::Display for AlgebraicMoveFormatter<'mv> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.r#move.is_kingside_castle() {
|
||||
return self.fmt_kingside_castle(f);
|
||||
} else if self.r#move.is_queenside_castle() {
|
||||
return self.fmt_queenside_castle(f);
|
||||
}
|
||||
|
||||
match self.style {
|
||||
Style::Short => self.fmt_short(f),
|
||||
Style::Long => self.fmt_long(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AlgebraicMoveFormatter, Style};
|
||||
|
||||
macro_rules! chess_move {
|
||||
($color:ident $shape:ident $from_square:ident - $to_square:ident) => {
|
||||
crate::Move::new(
|
||||
crate::piece::Piece::new(crate::piece::Color::$color, crate::piece::Shape::$shape),
|
||||
crate::Square::$from_square,
|
||||
crate::Square::$to_square,
|
||||
)
|
||||
};
|
||||
($color:ident $shape:ident $from_square:ident x $to_square:ident, $captured_color:ident $captured_shape:ident) => {
|
||||
chess_move!($color $shape $from_square - $to_square)
|
||||
.capturing(crate::piece::PlacedPiece::new(
|
||||
crate::piece::Piece::new(
|
||||
crate::piece::Color::$captured_color,
|
||||
crate::piece::Shape::$captured_shape,
|
||||
),
|
||||
crate::Square::$to_square,
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! test_algebraic_formatter {
|
||||
($test_name:ident, $style:ident, $color:ident $shape:ident $from_square:ident x $to_square:ident, $captured_color:ident $captured_shape:ident, $output:expr) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
let mv = chess_move!(
|
||||
$color $shape $from_square x $to_square,
|
||||
$captured_color $captured_shape
|
||||
);
|
||||
|
||||
let formatter = AlgebraicMoveFormatter::new(&mv).style(Style::$style);
|
||||
assert_eq!(format!("{}", formatter), $output);
|
||||
}
|
||||
};
|
||||
($test_name:ident, $style:ident, $color:ident $shape:ident $from_square:ident - $to_square:ident, $output:expr) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
let mv = chess_move!($color $shape $from_square-$to_square);
|
||||
|
||||
let formatter = AlgebraicMoveFormatter::new(&mv).style(Style::$style);
|
||||
assert_eq!(format!("{}", formatter), $output);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_algebraic_formatter!(long_pawn_move, Long, White Pawn E4 - E5, "e4-e5");
|
||||
test_algebraic_formatter!(long_bishop_move, Long, White Bishop A4 - D7, "Ba4-d7");
|
||||
test_algebraic_formatter!(long_bishop_capture, Long, White Bishop A2 x E6, Black Knight, "Ba2xe6");
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
mod diagram_formatter;
|
||||
mod flags;
|
||||
mod pieces;
|
||||
#[macro_use]
|
||||
mod position;
|
||||
|
||||
pub use diagram_formatter::DiagramFormatter;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue