[board] Reimplement Square as an enum
Replace the implementation of Square as an enum. Add Rank and File enums to support this. Along the way, change implementations of various methods taking Squares and BitBoards to take them by value instead of by reference. I don't think much is gained passing these types by reference because they're such small POD structs.
This commit is contained in:
commit
bcab682bb1
12 changed files with 392 additions and 408 deletions
|
@ -42,24 +42,22 @@ impl BitBoard {
|
||||||
moves_getter!(queen_moves);
|
moves_getter!(queen_moves);
|
||||||
moves_getter!(king_moves);
|
moves_getter!(king_moves);
|
||||||
|
|
||||||
pub fn from_square(sq: Square) -> BitBoard {
|
|
||||||
BitBoard(1 << sq.index())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0 == 0
|
self.0 == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_piece_at(&self, sq: &Square) -> bool {
|
pub fn has_piece_at(self, sq: Square) -> bool {
|
||||||
(self.0 & (1 << sq.index())) > 0
|
!(self & sq.into()).is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn place_piece_at(&mut self, sq: &Square) {
|
pub fn place_piece_at(&mut self, sq: Square) {
|
||||||
self.0 |= 1 << sq.index()
|
let sq_bb: BitBoard = sq.into();
|
||||||
|
*self |= sq_bb
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_piece_at(&mut self, sq: &Square) {
|
fn remove_piece_at(&mut self, sq: Square) {
|
||||||
self.0 &= !(1 << sq.index())
|
let sq_bb: BitBoard = sq.into();
|
||||||
|
*self &= !sq_bb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +66,13 @@ impl BitBoard {
|
||||||
BitScanner::new(self.0)
|
BitScanner::new(self.0)
|
||||||
.map(|x| u8::try_from(x))
|
.map(|x| u8::try_from(x))
|
||||||
.take_while(|x| x.is_ok())
|
.take_while(|x| x.is_ok())
|
||||||
.map(|x| Square::from_index_unsafe(x.unwrap()))
|
.map(|x| Square::from_index(x.unwrap() as usize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Square> for BitBoard {
|
||||||
|
fn from(value: Square) -> Self {
|
||||||
|
BitBoard(1 << value as u64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,41 +129,35 @@ impl fmt::Debug for BitBoard {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! infix_op {
|
macro_rules! infix_op {
|
||||||
($trait_type:ident, $func_name:ident, $left_type:ty, $right_type:ty, $op:tt) => {
|
($trait_type:ident, $func_name:ident, $left_type:ty, $right_type:ty) => {
|
||||||
impl $trait_type<$right_type> for $left_type {
|
impl $trait_type<$right_type> for $left_type {
|
||||||
type Output = BitBoard;
|
type Output = BitBoard;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn $func_name(self, rhs: $right_type) -> Self::Output {
|
fn $func_name(self, rhs: $right_type) -> Self::Output {
|
||||||
BitBoard(self.0 $op rhs.0)
|
BitBoard($trait_type::$func_name(self.0, rhs.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! assign_op {
|
macro_rules! assign_op {
|
||||||
($trait_type:ident, $func_name:ident, $left_type:ty, $op:tt) => {
|
($trait_type:ident, $func_name:ident, $left_type:ty) => {
|
||||||
impl $trait_type for $left_type {
|
impl $trait_type for $left_type {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn $func_name(&mut self, rhs: Self) {
|
fn $func_name(&mut self, rhs: Self) {
|
||||||
self.0 $op rhs.0
|
$trait_type::$func_name(&mut self.0, rhs.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
infix_op!(BitAnd, bitand, BitBoard, BitBoard, &);
|
infix_op!(BitAnd, bitand, BitBoard, BitBoard);
|
||||||
infix_op!(BitAnd, bitand, &BitBoard, BitBoard, &);
|
|
||||||
infix_op!(BitAnd, bitand, BitBoard, &BitBoard, &);
|
|
||||||
infix_op!(BitAnd, bitand, &BitBoard, &BitBoard, &);
|
|
||||||
|
|
||||||
assign_op!(BitAndAssign, bitand_assign, BitBoard, &=);
|
assign_op!(BitAndAssign, bitand_assign, BitBoard);
|
||||||
assign_op!(BitOrAssign, bitor_assign, BitBoard, |=);
|
assign_op!(BitOrAssign, bitor_assign, BitBoard);
|
||||||
|
|
||||||
infix_op!(BitOr, bitor, BitBoard, BitBoard, |);
|
infix_op!(BitOr, bitor, BitBoard, BitBoard);
|
||||||
infix_op!(BitOr, bitor, &BitBoard, BitBoard, |);
|
|
||||||
infix_op!(BitOr, bitor, BitBoard, &BitBoard, |);
|
|
||||||
infix_op!(BitOr, bitor, &BitBoard, &BitBoard, |);
|
|
||||||
|
|
||||||
impl Not for BitBoard {
|
impl Not for BitBoard {
|
||||||
type Output = BitBoard;
|
type Output = BitBoard;
|
||||||
|
@ -212,41 +210,44 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn has_piece_at() {
|
fn has_piece_at() {
|
||||||
let bb = BitBoard(0b1001100);
|
let bb = BitBoard(0b1001100);
|
||||||
|
assert!(bb.has_piece_at(Square::C1));
|
||||||
let c1 = Square::from_algebraic_str("c1").expect("Unable to get square for 'c1'");
|
assert!(!bb.has_piece_at(Square::B1));
|
||||||
assert!(bb.has_piece_at(&c1));
|
|
||||||
|
|
||||||
let b1 = Square::from_algebraic_str("b1").expect("Unable to get square for 'b1'");
|
|
||||||
assert!(!bb.has_piece_at(&b1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn place_piece_at() {
|
fn place_piece_at() {
|
||||||
let sq = Square::from_index(34).expect("Invalid square index");
|
let sq = Square::E4;
|
||||||
|
|
||||||
let mut bb = BitBoard(0b1001100);
|
let mut bb = BitBoard(0b1001100);
|
||||||
bb.place_piece_at(&sq);
|
bb.place_piece_at(sq);
|
||||||
assert!(bb.has_piece_at(&sq));
|
assert!(bb.has_piece_at(sq));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_piece_at() {
|
fn remove_piece_at() {
|
||||||
let sq = Square::from_index(2).expect("Invalid square index");
|
let sq = Square::A3;
|
||||||
|
|
||||||
let mut bb = BitBoard(0b1001100);
|
let mut bb = BitBoard(0b1001100);
|
||||||
bb.remove_piece_at(&sq);
|
bb.remove_piece_at(sq);
|
||||||
assert!(!bb.has_piece_at(&sq));
|
assert!(!bb.has_piece_at(sq));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pieces() {
|
fn single_rank_occupancy() {
|
||||||
let bb = BitBoard(0x1001_1010); // e4
|
let bb = BitBoard(0b01010100);
|
||||||
|
let expected_squares = [Square::G1, Square::E1, Square::C1];
|
||||||
|
for (a, b) in bb.occupied_squares().zip(expected_squares.iter().cloned()) {
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut occupied_squares = bb.occupied_squares();
|
#[test]
|
||||||
assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(28)));
|
fn occupancy_spot_check() {
|
||||||
assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(16)));
|
let bb =
|
||||||
assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(12)));
|
BitBoard(0b10000000_00000000_00100000_00000100_00000000_00000000_00010000_00001000);
|
||||||
assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(4)));
|
|
||||||
assert_eq!(occupied_squares.next(), None);
|
let expected_squares = [Square::H8, Square::F6, Square::C5, Square::E2, Square::D1];
|
||||||
|
|
||||||
|
for (a, b) in bb.occupied_squares().zip(expected_squares.iter().cloned()) {
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ impl DiagonalRays {
|
||||||
macro_rules! library_getter {
|
macro_rules! library_getter {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
pub(super) fn $name(&self, sq: Square) -> BitBoard {
|
pub(super) fn $name(&self, sq: Square) -> BitBoard {
|
||||||
self.$name[sq.index() as usize]
|
self.$name[sq as usize]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -91,8 +91,8 @@ macro_rules! library_getter {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct MoveLibrary {
|
pub(super) struct MoveLibrary {
|
||||||
// Rays
|
// Rays
|
||||||
diagonal_rays: [DiagonalRays; 64],
|
diagonal_rays: [DiagonalRays; Square::NUM as usize],
|
||||||
orthogonal_rays: [OrthogonalRays; 64],
|
orthogonal_rays: [OrthogonalRays; Square::NUM as usize],
|
||||||
|
|
||||||
// Piecewise move tables
|
// Piecewise move tables
|
||||||
knight_moves: [BitBoard; 64],
|
knight_moves: [BitBoard; 64],
|
||||||
|
@ -116,33 +116,31 @@ impl MoveLibrary {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) {
|
fn init(&mut self) {
|
||||||
for i in 0..64 {
|
for sq in Square::ALL {
|
||||||
self.init_orthogonal_rays(i);
|
self.init_orthogonal_rays(sq);
|
||||||
self.init_diagonal_rays(i);
|
self.init_diagonal_rays(sq);
|
||||||
self.init_knight_moves(i as usize);
|
self.init_knight_moves(sq as usize);
|
||||||
self.init_bishop_moves(i as usize);
|
self.init_bishop_moves(sq as usize);
|
||||||
self.init_rook_moves(i as usize);
|
self.init_rook_moves(sq as usize);
|
||||||
self.init_queen_moves(i as usize);
|
self.init_queen_moves(sq as usize);
|
||||||
self.init_king_moves(i as usize);
|
self.init_king_moves(sq as usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_orthogonal_rays(&mut self, idx: u8) {
|
fn init_orthogonal_rays(&mut self, sq: Square) {
|
||||||
let sq = Square::from_index_unsafe(idx);
|
let sq_bb: BitBoard = sq.into();
|
||||||
let sq_bb = BitBoard::from_square(sq);
|
|
||||||
|
|
||||||
let ortho_rays = &mut self.orthogonal_rays[idx as usize];
|
let ortho_rays = &mut self.orthogonal_rays[sq as usize];
|
||||||
ortho_rays.positive_file = Self::generate_ray(sq_bb, BitBoard::shift_north_one);
|
ortho_rays.positive_file = Self::generate_ray(sq_bb, BitBoard::shift_north_one);
|
||||||
ortho_rays.negative_file = Self::generate_ray(sq_bb, BitBoard::shift_south_one);
|
ortho_rays.negative_file = Self::generate_ray(sq_bb, BitBoard::shift_south_one);
|
||||||
ortho_rays.positive_rank = Self::generate_ray(sq_bb, BitBoard::shift_east_one);
|
ortho_rays.positive_rank = Self::generate_ray(sq_bb, BitBoard::shift_east_one);
|
||||||
ortho_rays.negative_rank = Self::generate_ray(sq_bb, BitBoard::shift_west_one);
|
ortho_rays.negative_rank = Self::generate_ray(sq_bb, BitBoard::shift_west_one);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_diagonal_rays(&mut self, idx: u8) {
|
fn init_diagonal_rays(&mut self, sq: Square) {
|
||||||
let sq = Square::from_index_unsafe(idx);
|
let sq_bb: BitBoard = sq.into();
|
||||||
let sq_bb = BitBoard::from_square(sq);
|
|
||||||
|
|
||||||
let diag_rays = &mut self.diagonal_rays[idx as usize];
|
let diag_rays = &mut self.diagonal_rays[sq as usize];
|
||||||
diag_rays.positive_diagonal = Self::generate_ray(sq_bb, BitBoard::shift_north_east_one);
|
diag_rays.positive_diagonal = Self::generate_ray(sq_bb, BitBoard::shift_north_east_one);
|
||||||
diag_rays.positive_antidiagonal = Self::generate_ray(sq_bb, BitBoard::shift_north_west_one);
|
diag_rays.positive_antidiagonal = Self::generate_ray(sq_bb, BitBoard::shift_north_west_one);
|
||||||
diag_rays.negative_diagonal = Self::generate_ray(sq_bb, BitBoard::shift_south_west_one);
|
diag_rays.negative_diagonal = Self::generate_ray(sq_bb, BitBoard::shift_south_west_one);
|
||||||
|
@ -204,13 +202,13 @@ impl MoveLibrary {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn generate_ray(sq: BitBoard, shift: fn(&BitBoard) -> BitBoard) -> BitBoard {
|
fn generate_ray(sq: BitBoard, shift: fn(BitBoard) -> BitBoard) -> BitBoard {
|
||||||
let mut ray = BitBoard::empty();
|
let mut ray = BitBoard::empty();
|
||||||
|
|
||||||
let mut iter = shift(&sq);
|
let mut iter = shift(sq);
|
||||||
while !iter.is_empty() {
|
while !iter.is_empty() {
|
||||||
ray |= iter;
|
ray |= iter;
|
||||||
iter = shift(&iter);
|
iter = shift(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
ray
|
ray
|
||||||
|
|
|
@ -7,58 +7,58 @@ impl BitBoard {
|
||||||
const NOT_H_FILE: u64 = 0x7f7f7f7f7f7f7f7f;
|
const NOT_H_FILE: u64 = 0x7f7f7f7f7f7f7f7f;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_north(&self, n: u8) -> BitBoard {
|
pub fn shift_north(self, n: u8) -> BitBoard {
|
||||||
BitBoard(self.0 << (8 * n))
|
BitBoard(self.0 << (8 * n))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_north_one(&self) -> BitBoard {
|
pub fn shift_north_one(self) -> BitBoard {
|
||||||
BitBoard(self.0 << 8)
|
BitBoard(self.0 << 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_north_east_one(&self) -> BitBoard {
|
pub fn shift_north_east_one(self) -> BitBoard {
|
||||||
BitBoard(self.0 << 9 & BitBoard::NOT_A_FILE)
|
BitBoard(self.0 << 9 & BitBoard::NOT_A_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_east(&self, n: u8) -> BitBoard {
|
pub fn shift_east(self, n: u8) -> BitBoard {
|
||||||
// TODO: Implement a bounds check here.
|
// TODO: Implement a bounds check here.
|
||||||
BitBoard(self.0 << n)
|
BitBoard(self.0 << n)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_east_one(&self) -> BitBoard {
|
pub fn shift_east_one(self) -> BitBoard {
|
||||||
BitBoard(self.0 << 1 & BitBoard::NOT_A_FILE)
|
BitBoard(self.0 << 1 & BitBoard::NOT_A_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_south_east_one(&self) -> BitBoard {
|
pub fn shift_south_east_one(self) -> BitBoard {
|
||||||
BitBoard(self.0 >> 7 & BitBoard::NOT_A_FILE)
|
BitBoard(self.0 >> 7 & BitBoard::NOT_A_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_south(&self, n: u8) -> BitBoard {
|
pub fn shift_south(self, n: u8) -> BitBoard {
|
||||||
BitBoard(self.0 >> (8 * n))
|
BitBoard(self.0 >> (8 * n))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_south_one(&self) -> BitBoard {
|
pub fn shift_south_one(self) -> BitBoard {
|
||||||
BitBoard(self.0 >> 8)
|
BitBoard(self.0 >> 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_south_west_one(&self) -> BitBoard {
|
pub fn shift_south_west_one(self) -> BitBoard {
|
||||||
BitBoard(self.0 >> 9 & BitBoard::NOT_H_FILE)
|
BitBoard(self.0 >> 9 & BitBoard::NOT_H_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_west_one(&self) -> BitBoard {
|
pub fn shift_west_one(self) -> BitBoard {
|
||||||
BitBoard(self.0 >> 1 & BitBoard::NOT_H_FILE)
|
BitBoard(self.0 >> 1 & BitBoard::NOT_H_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_north_west_one(&self) -> BitBoard {
|
pub fn shift_north_west_one(self) -> BitBoard {
|
||||||
BitBoard(self.0 << 7 & BitBoard::NOT_H_FILE)
|
BitBoard(self.0 << 7 & BitBoard::NOT_H_FILE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,6 @@ pub mod piece;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
mod square;
|
mod square;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
pub use moves::Move;
|
pub use moves::Move;
|
||||||
pub use position::Position;
|
pub use position::Position;
|
||||||
pub use square::Square;
|
pub use square::{File, Rank, Square};
|
||||||
|
|
|
@ -161,13 +161,13 @@ impl<'pos> Iterator for KingMoveGenerator<'pos> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Position, Square};
|
use crate::{piece::Piece, Position, Square};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_king() {
|
fn one_king() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::king(Color::White), &Square::e4())
|
pos.place_piece(Piece::king(Color::White), Square::E4)
|
||||||
.expect("Failed to place king on e4");
|
.expect("Failed to place king on e4");
|
||||||
|
|
||||||
let generator = KingMoveGenerator::new(&pos, Color::White);
|
let generator = KingMoveGenerator::new(&pos, Color::White);
|
||||||
|
@ -181,14 +181,14 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected_moves = [
|
let expected_moves = [
|
||||||
Move::new(Piece::king(Color::White), Square::e4(), Square::d5()),
|
Move::new(Piece::king(Color::White), Square::E4, Square::D5),
|
||||||
Move::new(Piece::king(Color::White), Square::e4(), Square::e5()),
|
Move::new(Piece::king(Color::White), Square::E4, Square::E5),
|
||||||
Move::new(Piece::king(Color::White), Square::e4(), Square::f5()),
|
Move::new(Piece::king(Color::White), Square::E4, Square::F5),
|
||||||
Move::new(Piece::king(Color::White), Square::e4(), Square::f4()),
|
Move::new(Piece::king(Color::White), Square::E4, Square::F4),
|
||||||
Move::new(Piece::king(Color::White), Square::e4(), Square::f3()),
|
Move::new(Piece::king(Color::White), Square::E4, Square::F3),
|
||||||
Move::new(Piece::king(Color::White), Square::e4(), Square::e3()),
|
Move::new(Piece::king(Color::White), Square::E4, Square::E3),
|
||||||
Move::new(Piece::king(Color::White), Square::e4(), Square::d3()),
|
Move::new(Piece::king(Color::White), Square::E4, Square::D3),
|
||||||
Move::new(Piece::king(Color::White), Square::e4(), Square::d4()),
|
Move::new(Piece::king(Color::White), Square::E4, Square::D4),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut generated_moves: HashSet<Move> = generator.iter().cloned().collect();
|
let mut generated_moves: HashSet<Move> = generator.iter().cloned().collect();
|
||||||
|
@ -211,7 +211,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn one_king_corner() {
|
fn one_king_corner() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::king(Color::White), &Square::a1())
|
pos.place_piece(Piece::king(Color::White), Square::A1)
|
||||||
.expect("Failed to place king on a1");
|
.expect("Failed to place king on a1");
|
||||||
|
|
||||||
let generator = KingMoveGenerator::new(&pos, Color::White);
|
let generator = KingMoveGenerator::new(&pos, Color::White);
|
||||||
|
@ -225,9 +225,9 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected_moves = [
|
let expected_moves = [
|
||||||
Move::new(Piece::king(Color::White), Square::a1(), Square::a2()),
|
Move::new(Piece::king(Color::White), Square::A1, Square::A2),
|
||||||
Move::new(Piece::king(Color::White), Square::a1(), Square::b1()),
|
Move::new(Piece::king(Color::White), Square::A1, Square::B1),
|
||||||
Move::new(Piece::king(Color::White), Square::a1(), Square::b2()),
|
Move::new(Piece::king(Color::White), Square::A1, Square::B2),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut generated_moves: HashSet<Move> = generator.iter().cloned().collect();
|
let mut generated_moves: HashSet<Move> = generator.iter().cloned().collect();
|
||||||
|
|
|
@ -97,7 +97,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn one_knight() {
|
fn one_knight() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::knight(Color::White), &Square::e4())
|
pos.place_piece(Piece::knight(Color::White), Square::E4)
|
||||||
.expect("Failed to place knight on e4");
|
.expect("Failed to place knight on e4");
|
||||||
|
|
||||||
let generator = KnightMoveGenerator::new(&pos, Color::White);
|
let generator = KnightMoveGenerator::new(&pos, Color::White);
|
||||||
|
@ -113,14 +113,14 @@ mod tests {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let expected_moves = [
|
let expected_moves = [
|
||||||
Move::new(Piece::knight(Color::White), Square::e4(), Square::c3()),
|
Move::new(Piece::knight(Color::White), Square::E4, Square::C3),
|
||||||
Move::new(Piece::knight(Color::White), Square::e4(), Square::d2()),
|
Move::new(Piece::knight(Color::White), Square::E4, Square::D2),
|
||||||
Move::new(Piece::knight(Color::White), Square::e4(), Square::f2()),
|
Move::new(Piece::knight(Color::White), Square::E4, Square::F2),
|
||||||
Move::new(Piece::knight(Color::White), Square::e4(), Square::g3()),
|
Move::new(Piece::knight(Color::White), Square::E4, Square::G3),
|
||||||
Move::new(Piece::knight(Color::White), Square::e4(), Square::c5()),
|
Move::new(Piece::knight(Color::White), Square::E4, Square::C5),
|
||||||
Move::new(Piece::knight(Color::White), Square::e4(), Square::d6()),
|
Move::new(Piece::knight(Color::White), Square::E4, Square::D6),
|
||||||
Move::new(Piece::knight(Color::White), Square::e4(), Square::g5()),
|
Move::new(Piece::knight(Color::White), Square::E4, Square::G5),
|
||||||
Move::new(Piece::knight(Color::White), Square::e4(), Square::f6()),
|
Move::new(Piece::knight(Color::White), Square::E4, Square::F6),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut generated_moves: HashSet<Move> = generator.iter().cloned().collect();
|
let mut generated_moves: HashSet<Move> = generator.iter().cloned().collect();
|
||||||
|
|
|
@ -18,9 +18,9 @@ struct MoveIterator(usize, usize);
|
||||||
struct MoveGenerationParameters {
|
struct MoveGenerationParameters {
|
||||||
starting_rank: BitBoard,
|
starting_rank: BitBoard,
|
||||||
promotion_rank: BitBoard,
|
promotion_rank: BitBoard,
|
||||||
push_shift: fn(&BitBoard) -> BitBoard,
|
push_shift: fn(BitBoard) -> BitBoard,
|
||||||
left_capture_shift: fn(&BitBoard) -> BitBoard,
|
left_capture_shift: fn(BitBoard) -> BitBoard,
|
||||||
right_capture_shift: fn(&BitBoard) -> BitBoard,
|
right_capture_shift: fn(BitBoard) -> BitBoard,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct PawnMoveGenerator<'pos> {
|
pub(super) struct PawnMoveGenerator<'pos> {
|
||||||
|
@ -106,7 +106,7 @@ impl<'pos> PawnMoveGenerator<'pos> {
|
||||||
|
|
||||||
let legal_1square_pushes = (parameters.push_shift)(bb) & empty_squares;
|
let legal_1square_pushes = (parameters.push_shift)(bb) & empty_squares;
|
||||||
let legal_2square_pushes =
|
let legal_2square_pushes =
|
||||||
(parameters.push_shift)(&(legal_1square_pushes & BitBoard::rank(2))) & empty_squares;
|
(parameters.push_shift)(legal_1square_pushes & BitBoard::rank(2)) & empty_squares;
|
||||||
|
|
||||||
self.pushes = legal_1square_pushes | legal_2square_pushes;
|
self.pushes = legal_1square_pushes | legal_2square_pushes;
|
||||||
}
|
}
|
||||||
|
@ -145,9 +145,9 @@ impl<'pos> PawnMoveGenerator<'pos> {
|
||||||
let black_pieces = self.position.bitboard_for_color(self.color.other());
|
let black_pieces = self.position.bitboard_for_color(self.color.other());
|
||||||
|
|
||||||
for from_sq in bb.occupied_squares() {
|
for from_sq in bb.occupied_squares() {
|
||||||
let pawn = BitBoard::from_square(from_sq);
|
let pawn: BitBoard = from_sq.into();
|
||||||
|
|
||||||
let push = (parameters.push_shift)(&pawn);
|
let push = (parameters.push_shift)(pawn);
|
||||||
if !(push & empty_squares).is_empty() {
|
if !(push & empty_squares).is_empty() {
|
||||||
let to_sq = push.occupied_squares().next().unwrap();
|
let to_sq = push.occupied_squares().next().unwrap();
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ impl<'pos> PawnMoveGenerator<'pos> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(pawn & parameters.starting_rank).is_empty() {
|
if !(pawn & parameters.starting_rank).is_empty() {
|
||||||
let push = (parameters.push_shift)(&push);
|
let push = (parameters.push_shift)(push);
|
||||||
if !(push & empty_squares).is_empty() {
|
if !(push & empty_squares).is_empty() {
|
||||||
let to_sq = push.occupied_squares().next().unwrap();
|
let to_sq = push.occupied_squares().next().unwrap();
|
||||||
self.quiet_move_list()
|
self.quiet_move_list()
|
||||||
|
@ -172,8 +172,8 @@ impl<'pos> PawnMoveGenerator<'pos> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for attack in [
|
for attack in [
|
||||||
(parameters.left_capture_shift)(&pawn),
|
(parameters.left_capture_shift)(pawn),
|
||||||
(parameters.right_capture_shift)(&pawn),
|
(parameters.right_capture_shift)(pawn),
|
||||||
] {
|
] {
|
||||||
if !(attack & black_pieces).is_empty() {
|
if !(attack & black_pieces).is_empty() {
|
||||||
let to_sq = attack.occupied_squares().next().unwrap();
|
let to_sq = attack.occupied_squares().next().unwrap();
|
||||||
|
@ -254,15 +254,16 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn one_2square_push() {
|
fn one_2square_push() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::pawn(Color::White), &Square::e2())
|
|
||||||
|
pos.place_piece(Piece::pawn(Color::White), Square::E2)
|
||||||
.expect("Failed to place pawn on e2");
|
.expect("Failed to place pawn on e2");
|
||||||
|
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White);
|
let generator = PawnMoveGenerator::new(&pos, Color::White);
|
||||||
|
|
||||||
let expected_moves = HashSet::from_iter(
|
let expected_moves = HashSet::from_iter(
|
||||||
[
|
[
|
||||||
Move::new(Piece::pawn(Color::White), Square::e2(), Square::e3()),
|
Move::new(Piece::pawn(Color::White), Square::E2, Square::E3),
|
||||||
Move::new(Piece::pawn(Color::White), Square::e2(), Square::e4()),
|
Move::new(Piece::pawn(Color::White), Square::E2, Square::E4),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -275,18 +276,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn one_1square_push() {
|
fn one_1square_push() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::pawn(Color::White), &Square::e3())
|
|
||||||
|
pos.place_piece(Piece::pawn(Color::White), Square::E3)
|
||||||
.expect("Failed to place pawn on e3");
|
.expect("Failed to place pawn on e3");
|
||||||
|
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White);
|
let generator = PawnMoveGenerator::new(&pos, Color::White);
|
||||||
|
|
||||||
let expected_moves = HashSet::from_iter(
|
let expected_moves = HashSet::from_iter(
|
||||||
[Move::new(
|
[Move::new(Piece::pawn(Color::White), Square::E3, Square::E4)].into_iter(),
|
||||||
Piece::pawn(Color::White),
|
|
||||||
Square::e3(),
|
|
||||||
Square::e4(),
|
|
||||||
)]
|
|
||||||
.into_iter(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = generator.collect();
|
let generated_moves: HashSet<Move> = generator.collect();
|
||||||
|
@ -297,9 +294,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn one_obstructed_2square_push() {
|
fn one_obstructed_2square_push() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::pawn(Color::White), &Square::e2())
|
|
||||||
|
pos.place_piece(Piece::pawn(Color::White), Square::E2)
|
||||||
.expect("Failed to place pawn on e2");
|
.expect("Failed to place pawn on e2");
|
||||||
pos.place_piece(&Piece::knight(Color::White), &Square::e4())
|
pos.place_piece(Piece::knight(Color::White), Square::E4)
|
||||||
.expect("Failed to place knight on e4");
|
.expect("Failed to place knight on e4");
|
||||||
|
|
||||||
println!("{}", DiagramFormatter::new(&pos));
|
println!("{}", DiagramFormatter::new(&pos));
|
||||||
|
@ -307,12 +305,7 @@ mod tests {
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White);
|
let generator = PawnMoveGenerator::new(&pos, Color::White);
|
||||||
|
|
||||||
let expected_moves = HashSet::from_iter(
|
let expected_moves = HashSet::from_iter(
|
||||||
[Move::new(
|
[Move::new(Piece::pawn(Color::White), Square::E2, Square::E3)].into_iter(),
|
||||||
Piece::pawn(Color::White),
|
|
||||||
Square::e2(),
|
|
||||||
Square::e3(),
|
|
||||||
)]
|
|
||||||
.into_iter(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = generator.collect();
|
let generated_moves: HashSet<Move> = generator.collect();
|
||||||
|
@ -323,9 +316,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn one_obstructed_1square_push() {
|
fn one_obstructed_1square_push() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::pawn(Color::White), &Square::e2())
|
|
||||||
|
pos.place_piece(Piece::pawn(Color::White), Square::E2)
|
||||||
.expect("Failed to place pawn on e2");
|
.expect("Failed to place pawn on e2");
|
||||||
pos.place_piece(&Piece::knight(Color::White), &Square::e3())
|
pos.place_piece(Piece::knight(Color::White), Square::E3)
|
||||||
.expect("Failed to place knight on e4");
|
.expect("Failed to place knight on e4");
|
||||||
|
|
||||||
println!("{}", DiagramFormatter::new(&pos));
|
println!("{}", DiagramFormatter::new(&pos));
|
||||||
|
@ -340,11 +334,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn one_attack() {
|
fn one_attack() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::pawn(Color::White), &Square::e4())
|
pos.place_piece(Piece::pawn(Color::White), Square::E4)
|
||||||
.expect("Failed to place pawn on e4");
|
.expect("Failed to place pawn on e4");
|
||||||
pos.place_piece(&Piece::bishop(Color::White), &Square::e5())
|
pos.place_piece(Piece::bishop(Color::White), Square::E5)
|
||||||
.expect("Failed to place pawn on e4");
|
.expect("Failed to place pawn on e4");
|
||||||
pos.place_piece(&Piece::knight(Color::Black), &Square::d5())
|
pos.place_piece(Piece::knight(Color::Black), Square::D5)
|
||||||
.expect("Failed to place knight on d5");
|
.expect("Failed to place knight on d5");
|
||||||
|
|
||||||
println!("{}", DiagramFormatter::new(&pos));
|
println!("{}", DiagramFormatter::new(&pos));
|
||||||
|
@ -352,10 +346,8 @@ mod tests {
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White);
|
let generator = PawnMoveGenerator::new(&pos, Color::White);
|
||||||
|
|
||||||
let expected_moves = HashSet::from_iter(
|
let expected_moves = HashSet::from_iter(
|
||||||
[
|
[Move::new(Piece::pawn(Color::White), Square::E4, Square::D5)
|
||||||
Move::new(Piece::pawn(Color::White), Square::e4(), Square::d5())
|
.capturing(PlacedPiece::new(Piece::knight(Color::Black), Square::D5))]
|
||||||
.capturing(PlacedPiece::new(Piece::knight(Color::Black), Square::d5())),
|
|
||||||
]
|
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -367,13 +359,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn one_double_attack() {
|
fn one_double_attack() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::pawn(Color::White), &Square::e4())
|
pos.place_piece(Piece::pawn(Color::White), Square::E4)
|
||||||
.expect("Failed to place pawn on e4");
|
.expect("Failed to place pawn on e4");
|
||||||
pos.place_piece(&Piece::bishop(Color::White), &Square::e5())
|
pos.place_piece(Piece::bishop(Color::White), Square::E5)
|
||||||
.expect("Failed to place pawn on e4");
|
.expect("Failed to place pawn on e4");
|
||||||
pos.place_piece(&Piece::knight(Color::Black), &Square::d5())
|
pos.place_piece(Piece::knight(Color::Black), Square::D5)
|
||||||
.expect("Failed to place knight on d5");
|
.expect("Failed to place knight on d5");
|
||||||
pos.place_piece(&Piece::queen(Color::Black), &Square::f5())
|
pos.place_piece(Piece::queen(Color::Black), Square::F5)
|
||||||
.expect("Failed to place knight on f5");
|
.expect("Failed to place knight on f5");
|
||||||
|
|
||||||
println!("{}", DiagramFormatter::new(&pos));
|
println!("{}", DiagramFormatter::new(&pos));
|
||||||
|
@ -382,10 +374,10 @@ mod tests {
|
||||||
|
|
||||||
let expected_moves = HashSet::from_iter(
|
let expected_moves = HashSet::from_iter(
|
||||||
[
|
[
|
||||||
Move::new(Piece::pawn(Color::White), Square::e4(), Square::d5())
|
Move::new(Piece::pawn(Color::White), Square::E4, Square::D5)
|
||||||
.capturing(PlacedPiece::new(Piece::knight(Color::Black), Square::d5())),
|
.capturing(PlacedPiece::new(Piece::knight(Color::Black), Square::D5)),
|
||||||
Move::new(Piece::pawn(Color::White), Square::e4(), Square::f5())
|
Move::new(Piece::pawn(Color::White), Square::E4, Square::F5)
|
||||||
.capturing(PlacedPiece::new(Piece::queen(Color::Black), Square::f5())),
|
.capturing(PlacedPiece::new(Piece::queen(Color::Black), Square::F5)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use crate::{Position, Square};
|
use crate::{File, Position, Rank, Square};
|
||||||
use std::fmt;
|
use std::{fmt, fmt::Write};
|
||||||
use std::fmt::Write;
|
|
||||||
|
|
||||||
pub struct DiagramFormatter<'a>(&'a Position);
|
pub struct DiagramFormatter<'a>(&'a Position);
|
||||||
|
|
||||||
|
@ -18,11 +17,11 @@ impl<'a> fmt::Display for DiagramFormatter<'a> {
|
||||||
|
|
||||||
output.push_str(" +-----------------+\n");
|
output.push_str(" +-----------------+\n");
|
||||||
|
|
||||||
for rank in (0..8).rev() {
|
for rank in Rank::ALL.iter().rev() {
|
||||||
write!(output, "{} | ", rank + 1)?;
|
write!(output, "{} | ", rank)?;
|
||||||
|
|
||||||
for file in 0..8 {
|
for file in File::ALL.iter() {
|
||||||
let square = Square::from_rank_file(rank, file).unwrap();
|
let square = Square::from_file_rank(*file, *rank);
|
||||||
match self.0.piece_on_square(square) {
|
match self.0.piece_on_square(square) {
|
||||||
Some(placed_piece) => write!(output, "{} ", placed_piece.piece())?,
|
Some(placed_piece) => write!(output, "{} ", placed_piece.piece())?,
|
||||||
None => output.push_str(". "),
|
None => output.push_str(". "),
|
||||||
|
@ -56,8 +55,8 @@ mod tests {
|
||||||
fn one_king() {
|
fn one_king() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(
|
pos.place_piece(
|
||||||
&Piece::king(Color::Black),
|
Piece::king(Color::Black),
|
||||||
&Square::from_algebraic_str("h3").expect("h3"),
|
Square::from_algebraic_str("h3").expect("h3"),
|
||||||
)
|
)
|
||||||
.expect("Unable to place piece");
|
.expect("Unable to place piece");
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl<'a> Iterator for Pieces<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut current_shape: Option<Shape> = None;
|
let mut current_shape: Option<Shape> = None;
|
||||||
let mut next_nonempty_bitboard: Option<&BitBoard> = None;
|
let mut next_nonempty_bitboard: Option<BitBoard> = None;
|
||||||
|
|
||||||
while let Some(shape) = self.shape_iterator.next() {
|
while let Some(shape) = self.shape_iterator.next() {
|
||||||
let piece = Piece::new(self.color, *shape);
|
let piece = Piece::new(self.color, *shape);
|
||||||
|
@ -80,12 +80,8 @@ mod tests {
|
||||||
use crate::Square;
|
use crate::Square;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
fn square_at(sq: &str) -> Square {
|
fn place_piece_in_position(pos: &mut Position, piece: Piece, sq: Square) {
|
||||||
Square::from_algebraic_str(sq).expect(sq)
|
pos.place_piece(piece, sq)
|
||||||
}
|
|
||||||
|
|
||||||
fn place_piece_in_position(pos: &mut Position, sq: &str, piece: Piece) {
|
|
||||||
pos.place_piece(&piece, &square_at(sq))
|
|
||||||
.expect("Unable to place {piece:?} queen on {sq}");
|
.expect("Unable to place {piece:?} queen on {sq}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,11 +94,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one() {
|
fn one() {
|
||||||
let sq = Square::from_algebraic_str("e4").expect("e4");
|
let sq = Square::E4;
|
||||||
|
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
pos.place_piece(&Piece::new(Color::White, Shape::Queen), &sq)
|
place_piece_in_position(&mut pos, Piece::new(Color::White, Shape::Queen), Square::E4);
|
||||||
.expect("Unable to place white queen on e4");
|
|
||||||
println!("{:?}", &pos);
|
println!("{:?}", &pos);
|
||||||
|
|
||||||
let mut pieces = pos.pieces(Color::White);
|
let mut pieces = pos.pieces(Color::White);
|
||||||
|
@ -117,18 +112,18 @@ mod tests {
|
||||||
fn multiple_pieces() {
|
fn multiple_pieces() {
|
||||||
let mut pos = Position::empty();
|
let mut pos = Position::empty();
|
||||||
|
|
||||||
place_piece_in_position(&mut pos, "e4", Piece::new(Color::White, Shape::Queen));
|
place_piece_in_position(&mut pos, Piece::new(Color::White, Shape::Queen), Square::E4);
|
||||||
place_piece_in_position(&mut pos, "e1", Piece::new(Color::White, Shape::King));
|
place_piece_in_position(&mut pos, Piece::new(Color::White, Shape::King), Square::E1);
|
||||||
place_piece_in_position(&mut pos, "b2", Piece::new(Color::White, Shape::Pawn));
|
place_piece_in_position(&mut pos, Piece::new(Color::White, Shape::Pawn), Square::B2);
|
||||||
place_piece_in_position(&mut pos, "c2", Piece::new(Color::White, Shape::Pawn));
|
place_piece_in_position(&mut pos, Piece::new(Color::White, Shape::Pawn), Square::C2);
|
||||||
|
|
||||||
println!("{:?}", &pos);
|
println!("{:?}", &pos);
|
||||||
|
|
||||||
let expected_placed_pieces = HashSet::from([
|
let expected_placed_pieces = HashSet::from([
|
||||||
PlacedPiece::new(Piece::new(Color::White, Shape::Queen), square_at("e4")),
|
PlacedPiece::new(Piece::new(Color::White, Shape::Queen), Square::E4),
|
||||||
PlacedPiece::new(Piece::new(Color::White, Shape::King), square_at("e1")),
|
PlacedPiece::new(Piece::new(Color::White, Shape::King), Square::E1),
|
||||||
PlacedPiece::new(Piece::new(Color::White, Shape::Pawn), square_at("b2")),
|
PlacedPiece::new(Piece::new(Color::White, Shape::Pawn), Square::B2),
|
||||||
PlacedPiece::new(Piece::new(Color::White, Shape::Pawn), square_at("c2")),
|
PlacedPiece::new(Piece::new(Color::White, Shape::Pawn), Square::C2),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let placed_pieces = HashSet::from_iter(pos.pieces(Color::White));
|
let placed_pieces = HashSet::from_iter(pos.pieces(Color::White));
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use super::Pieces;
|
use super::Pieces;
|
||||||
use crate::bitboard::BitBoard;
|
use crate::{
|
||||||
use crate::moves::Moves;
|
bitboard::BitBoard,
|
||||||
use crate::piece::{Color, Piece, PiecePlacementError, PlacedPiece, Shape};
|
moves::Moves,
|
||||||
use crate::Square;
|
piece::{Color, Piece, PiecePlacementError, PlacedPiece, Shape},
|
||||||
|
Square,
|
||||||
|
};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
@ -68,28 +70,24 @@ impl Position {
|
||||||
Position {
|
Position {
|
||||||
color_to_move: Color::White,
|
color_to_move: Color::White,
|
||||||
pieces_per_color: [
|
pieces_per_color: [
|
||||||
white_pieces.iter().fold(BitBoard::empty(), |a, b| a | b),
|
white_pieces.iter().fold(BitBoard::empty(), |a, b| a | *b),
|
||||||
black_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],
|
pieces_per_type: [white_pieces, black_pieces],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn place_piece(
|
pub fn place_piece(&mut self, piece: Piece, square: Square) -> Result<(), PiecePlacementError> {
|
||||||
&mut self,
|
|
||||||
piece: &Piece,
|
|
||||||
square: &Square,
|
|
||||||
) -> Result<(), PiecePlacementError> {
|
|
||||||
let type_bb = self.bitboard_for_piece_mut(piece);
|
let type_bb = self.bitboard_for_piece_mut(piece);
|
||||||
|
|
||||||
if type_bb.has_piece_at(&square) {
|
if type_bb.has_piece_at(square) {
|
||||||
return Err(PiecePlacementError::ExistsOnSquare);
|
return Err(PiecePlacementError::ExistsOnSquare);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_bb.place_piece_at(&square);
|
type_bb.place_piece_at(square);
|
||||||
|
|
||||||
let color_bb = &mut self.bitboard_for_color_mut(piece.color());
|
let color_bb = &mut self.bitboard_for_color_mut(piece.color());
|
||||||
color_bb.place_piece_at(&square);
|
color_bb.place_piece_at(square);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -110,16 +108,16 @@ impl Position {
|
||||||
| self.pieces_per_color[Color::Black as usize])
|
| self.pieces_per_color[Color::Black as usize])
|
||||||
}
|
}
|
||||||
|
|
||||||
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]
|
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]
|
&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: Color) -> BitBoard {
|
||||||
&self.pieces_per_color[color as usize]
|
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: Color) -> &mut BitBoard {
|
||||||
|
@ -131,7 +129,7 @@ impl Position {
|
||||||
for shape in Shape::iter() {
|
for shape in Shape::iter() {
|
||||||
let piece = Piece::new(color, *shape);
|
let piece = Piece::new(color, *shape);
|
||||||
let bb = self.bitboard_for_piece(piece);
|
let bb = self.bitboard_for_piece(piece);
|
||||||
if bb.has_piece_at(&sq) {
|
if bb.has_piece_at(sq) {
|
||||||
return Some(PlacedPiece::new(piece, sq));
|
return Some(PlacedPiece::new(piece, sq));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,10 +180,10 @@ mod tests {
|
||||||
let mut position = Position::empty();
|
let mut position = Position::empty();
|
||||||
|
|
||||||
let piece = Piece::new(Color::White, Shape::Queen);
|
let piece = Piece::new(Color::White, Shape::Queen);
|
||||||
let square = Square::e4();
|
let square = Square::E4;
|
||||||
|
|
||||||
position
|
position
|
||||||
.place_piece(&piece, &square)
|
.place_piece(piece, square)
|
||||||
.expect("Unable to place white queen on e4");
|
.expect("Unable to place white queen on e4");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -198,7 +196,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
position
|
position
|
||||||
.place_piece(&piece, &square)
|
.place_piece(piece, square)
|
||||||
.expect_err("Placed white queen on e4 a second time?!");
|
.expect_err("Placed white queen on e4 a second time?!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,110 +14,198 @@ pub enum Direction {
|
||||||
NorthEast,
|
NorthEast,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ParseFileError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ParseSquareError;
|
pub struct ParseSquareError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SquareOutOfBoundsError;
|
pub struct SquareOutOfBoundsError;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
macro_rules! coordinate_enum {
|
||||||
pub struct Square {
|
($name: ident, $($variant:ident),*) => {
|
||||||
rank: u8,
|
#[repr(u8)]
|
||||||
file: u8,
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
index: u8,
|
pub enum $name {
|
||||||
|
$($variant), *
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
pub const NUM: usize = [$(Self::$variant), *].len();
|
||||||
|
pub const ALL: [Self; Self::NUM] = [$(Self::$variant), *];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn from_index(index: usize) -> Self {
|
||||||
|
assert!(
|
||||||
|
index < Self::NUM,
|
||||||
|
"Index {} out of bounds for {}.",
|
||||||
|
index,
|
||||||
|
stringify!($name)
|
||||||
|
);
|
||||||
|
Self::try_index(index).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_index(index: usize) -> Option<Self> {
|
||||||
|
$(
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
const $variant: usize = $name::$variant as usize;
|
||||||
|
)*
|
||||||
|
|
||||||
|
match index {
|
||||||
|
$($variant => Some($name::$variant),)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
coordinate_enum!(Rank,
|
||||||
|
One, Two, Three, Four, Five, Six, Seven, Eight
|
||||||
|
);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
coordinate_enum!(File,
|
||||||
|
A, B, C, D, E, F, G, H
|
||||||
|
);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
coordinate_enum!(Square,
|
||||||
|
A1, B1, C1, D1, E1, F1, G1, H1,
|
||||||
|
A2, B2, C2, D2, E2, F2, G2, H2,
|
||||||
|
A3, B3, C3, D3, E3, F3, G3, H3,
|
||||||
|
A4, B4, C4, D4, E4, F4, G4, H4,
|
||||||
|
A5, B5, C5, D5, E5, F5, G5, H5,
|
||||||
|
A6, B6, C6, D6, E6, F6, G6, H6,
|
||||||
|
A7, B7, C7, D7, E7, F7, G7, H7,
|
||||||
|
A8, B8, C8, D8, E8, F8, G8, H8
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Into<char> for File {
|
||||||
|
fn into(self) -> char {
|
||||||
|
('a' as u8 + self as u8) as char
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<char> for File {
|
||||||
|
type Error = ParseFileError;
|
||||||
|
|
||||||
|
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||||
|
let lowercase_value = value.to_ascii_lowercase();
|
||||||
|
for file in File::ALL.iter() {
|
||||||
|
if lowercase_value == (*file).into() {
|
||||||
|
return Ok(*file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ParseFileError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for File {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", Into::<char>::into(*self).to_uppercase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<char> for Rank {
|
||||||
|
fn into(self) -> char {
|
||||||
|
('1' as u8 + self as u8) as char
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<char> for Rank {
|
||||||
|
type Error = ParseFileError;
|
||||||
|
|
||||||
|
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||||
|
let lowercase_value = value.to_ascii_lowercase();
|
||||||
|
for rank in Self::ALL.iter().cloned() {
|
||||||
|
if lowercase_value == rank.into() {
|
||||||
|
return Ok(rank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ParseFileError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Rank {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", Into::<char>::into(*self))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Square {
|
impl Square {
|
||||||
pub fn from_rank_file(rank: u8, file: u8) -> Result<Square, SquareOutOfBoundsError> {
|
#[inline]
|
||||||
if rank >= 8 || file >= 8 {
|
pub fn from_file_rank(file: File, rank: Rank) -> Square {
|
||||||
return Err(SquareOutOfBoundsError);
|
Self::from_index((rank as usize) << 3 | file as usize)
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Square {
|
|
||||||
rank,
|
|
||||||
file,
|
|
||||||
index: rank * 8 + file,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn file(self) -> File {
|
||||||
|
File::from_index(self as usize & 0b000111)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn rank(self) -> Rank {
|
||||||
|
Rank::from_index(self as usize >> 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Square {
|
||||||
pub fn from_algebraic_str(s: &str) -> Result<Square, ParseSquareError> {
|
pub fn from_algebraic_str(s: &str) -> Result<Square, ParseSquareError> {
|
||||||
s.parse()
|
s.parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rank_file(&self) -> (u8, u8) {
|
pub fn neighbor(self, direction: Direction) -> Option<Square> {
|
||||||
(self.rank, self.file)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn neighbor(&self, direction: Direction) -> Option<Square> {
|
|
||||||
match direction {
|
match direction {
|
||||||
Direction::North => Square::from_index(self.index + 8),
|
Direction::North => Square::try_index(self as usize + 8),
|
||||||
Direction::NorthWest => {
|
Direction::NorthWest => {
|
||||||
if self.rank < 7 {
|
if self.rank() != Rank::Eight {
|
||||||
Square::from_index(self.index + 7)
|
Square::try_index(self as usize + 7)
|
||||||
} else {
|
} else {
|
||||||
Err(SquareOutOfBoundsError)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::West => {
|
Direction::West => {
|
||||||
if self.file > 0 {
|
if self.file() != File::A {
|
||||||
Square::from_index(self.index - 1)
|
Square::try_index(self as usize - 1)
|
||||||
} else {
|
} else {
|
||||||
Err(SquareOutOfBoundsError)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::SouthWest => {
|
Direction::SouthWest => {
|
||||||
if self.rank > 0 {
|
if self.rank() != Rank::One {
|
||||||
Square::from_index(self.index - 9)
|
Square::try_index(self as usize - 9)
|
||||||
} else {
|
} else {
|
||||||
Err(SquareOutOfBoundsError)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::South => {
|
Direction::South => {
|
||||||
if self.rank > 0 {
|
if self.rank() != Rank::One {
|
||||||
Square::from_index(self.index - 8)
|
Square::try_index(self as usize - 8)
|
||||||
} else {
|
} else {
|
||||||
Err(SquareOutOfBoundsError)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::SouthEast => {
|
Direction::SouthEast => {
|
||||||
if self.rank > 0 {
|
if self.rank() != Rank::One {
|
||||||
Square::from_index(self.index - 7)
|
Square::try_index(self as usize - 7)
|
||||||
} else {
|
} else {
|
||||||
Err(SquareOutOfBoundsError)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::East => {
|
Direction::East => {
|
||||||
if self.file < 7 {
|
if self.file() != File::H {
|
||||||
Square::from_index(self.index + 1)
|
Square::try_index(self as usize + 1)
|
||||||
} else {
|
} else {
|
||||||
Err(SquareOutOfBoundsError)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::NorthEast => Square::from_index(self.index + 9),
|
Direction::NorthEast => Square::try_index(self as usize + 9),
|
||||||
}
|
}
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Square {
|
|
||||||
pub(crate) fn from_index(index: u8) -> Result<Square, SquareOutOfBoundsError> {
|
|
||||||
if index >= 64 {
|
|
||||||
return Err(SquareOutOfBoundsError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Square::from_index_unsafe(index))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_index_unsafe(index: u8) -> Square {
|
|
||||||
Square {
|
|
||||||
rank: index / 8,
|
|
||||||
file: index % 8,
|
|
||||||
index: index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn index(&self) -> u8 {
|
|
||||||
self.index
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,47 +213,34 @@ impl FromStr for Square {
|
||||||
type Err = ParseSquareError;
|
type Err = ParseSquareError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
if !s.is_ascii() || s.len() != 2 {
|
|
||||||
return Err(ParseSquareError);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut chars = s.chars();
|
let mut chars = s.chars();
|
||||||
|
|
||||||
let file_char = chars.next().unwrap().to_ascii_lowercase();
|
let file: File = chars
|
||||||
if !file_char.is_ascii_lowercase() {
|
|
||||||
return Err(ParseSquareError);
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = (file_char as u8) - ('a' as u8);
|
|
||||||
if file >= 8 {
|
|
||||||
return Err(ParseSquareError);
|
|
||||||
}
|
|
||||||
|
|
||||||
let converted_rank_digit = chars
|
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.and_then(|c| c.try_into().ok())
|
||||||
.to_digit(10)
|
|
||||||
.and_then(|x| if x >= 1 && x <= 8 { Some(x) } else { None })
|
|
||||||
.ok_or(ParseSquareError)?;
|
.ok_or(ParseSquareError)?;
|
||||||
let rank = u8::try_from(converted_rank_digit).map_err(|_| ParseSquareError)? - 1;
|
|
||||||
|
|
||||||
Ok(Square {
|
let rank: Rank = chars
|
||||||
rank,
|
.next()
|
||||||
file,
|
.and_then(|c| c.try_into().ok())
|
||||||
index: rank * 8 + file,
|
.ok_or(ParseSquareError)?;
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<BitBoard> for Square {
|
if !chars.next().is_none() {
|
||||||
fn into(self) -> BitBoard {
|
return Err(ParseSquareError);
|
||||||
BitBoard::new(1 << self.index)
|
}
|
||||||
|
|
||||||
|
Ok(Square::from_file_rank(file, rank))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Square {
|
impl fmt::Display for Square {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}{}", ('a' as u8 + self.file) as char, self.rank + 1)
|
write!(
|
||||||
|
f,
|
||||||
|
"{}{}",
|
||||||
|
('a' as u8 + self.file() as u8) as char,
|
||||||
|
self.rank() as usize + 1
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,17 +250,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn good_algebraic_input() {
|
fn good_algebraic_input() {
|
||||||
let sq1 = Square::from_algebraic_str("a4").expect("Failed to parse 'a4' square");
|
let sq = Square::from_algebraic_str("a4").expect("Failed to parse 'a4' square");
|
||||||
assert_eq!(sq1.file, 0);
|
assert_eq!(sq.file(), File::A);
|
||||||
assert_eq!(sq1.rank, 3);
|
assert_eq!(sq.rank(), Rank::Four);
|
||||||
|
|
||||||
let sq2 = Square::from_algebraic_str("B8").expect("Failed to parse 'B8' square");
|
let sq = Square::from_algebraic_str("B8").expect("Failed to parse 'B8' square");
|
||||||
assert_eq!(sq2.file, 1);
|
assert_eq!(sq.file(), File::B);
|
||||||
assert_eq!(sq2.rank, 7);
|
assert_eq!(sq.rank(), Rank::Eight);
|
||||||
|
|
||||||
let sq3 = Square::from_algebraic_str("e4").expect("Failed to parse 'B8' square");
|
let sq = Square::from_algebraic_str("e4").expect("Failed to parse 'B8' square");
|
||||||
assert_eq!(sq3.rank, 3, "Expected rank of e4 to be 3");
|
assert_eq!(sq.file(), File::E);
|
||||||
assert_eq!(sq3.file, 4, "Expected file of e4 to be 4");
|
assert_eq!(sq.rank(), Rank::Four);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -200,87 +275,56 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_index() {
|
fn from_index() {
|
||||||
let sq1 = Square::from_index(4).expect("Unable to get Square from index");
|
let sq = Square::try_index(4).expect("Unable to get Square from index");
|
||||||
assert_eq!(sq1.rank, 0);
|
assert_eq!(sq.file(), File::E);
|
||||||
assert_eq!(sq1.file, 4);
|
assert_eq!(sq.rank(), Rank::One);
|
||||||
|
|
||||||
let sq1 = Square::from_index(28).expect("Unable to get Square from index");
|
let sq = Square::try_index(28).expect("Unable to get Square from index");
|
||||||
assert_eq!(sq1.rank, 3);
|
assert_eq!(sq.file(), File::E);
|
||||||
assert_eq!(sq1.file, 4);
|
assert_eq!(sq.rank(), Rank::Four);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn valid_neighbors() {
|
fn valid_neighbors() {
|
||||||
let sq = Square::from_index_unsafe(28);
|
let sq = Square::E4;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(sq.neighbor(Direction::North), Some(Square::E5));
|
||||||
sq.neighbor(Direction::North),
|
assert_eq!(sq.neighbor(Direction::NorthEast), Some(Square::F5));
|
||||||
Some(Square::from_index_unsafe(36))
|
assert_eq!(sq.neighbor(Direction::East), Some(Square::F4));
|
||||||
);
|
assert_eq!(sq.neighbor(Direction::SouthEast), Some(Square::F3));
|
||||||
|
assert_eq!(sq.neighbor(Direction::South), Some(Square::E3));
|
||||||
assert_eq!(
|
assert_eq!(sq.neighbor(Direction::SouthWest), Some(Square::D3));
|
||||||
sq.neighbor(Direction::NorthEast),
|
assert_eq!(sq.neighbor(Direction::West), Some(Square::D4));
|
||||||
Some(Square::from_index_unsafe(37))
|
assert_eq!(sq.neighbor(Direction::NorthWest), Some(Square::D5));
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
sq.neighbor(Direction::East),
|
|
||||||
Some(Square::from_index_unsafe(29))
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
sq.neighbor(Direction::SouthEast),
|
|
||||||
Some(Square::from_index_unsafe(21))
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
sq.neighbor(Direction::South),
|
|
||||||
Some(Square::from_index_unsafe(20))
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
sq.neighbor(Direction::SouthWest),
|
|
||||||
Some(Square::from_index_unsafe(19))
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
sq.neighbor(Direction::West),
|
|
||||||
Some(Square::from_index_unsafe(27))
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
sq.neighbor(Direction::NorthWest),
|
|
||||||
Some(Square::from_index_unsafe(35))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_neighbors() {
|
fn invalid_neighbors() {
|
||||||
let square0 = Square::from_index_unsafe(0);
|
let sq = Square::A1;
|
||||||
assert!(square0.neighbor(Direction::West).is_none());
|
assert!(sq.neighbor(Direction::West).is_none());
|
||||||
assert!(square0.neighbor(Direction::SouthWest).is_none());
|
assert!(sq.neighbor(Direction::SouthWest).is_none());
|
||||||
assert!(square0.neighbor(Direction::South).is_none());
|
assert!(sq.neighbor(Direction::South).is_none());
|
||||||
|
|
||||||
let square7 = Square::from_index_unsafe(7);
|
let sq = Square::H1;
|
||||||
assert!(square7.neighbor(Direction::East).is_none());
|
assert!(sq.neighbor(Direction::East).is_none());
|
||||||
assert!(square7.neighbor(Direction::SouthEast).is_none());
|
assert!(sq.neighbor(Direction::SouthEast).is_none());
|
||||||
assert!(square7.neighbor(Direction::South).is_none());
|
assert!(sq.neighbor(Direction::South).is_none());
|
||||||
|
|
||||||
let square56 = Square::from_index_unsafe(56);
|
let sq = Square::A8;
|
||||||
assert!(square56.neighbor(Direction::North).is_none());
|
assert!(sq.neighbor(Direction::North).is_none());
|
||||||
assert!(square56.neighbor(Direction::NorthWest).is_none());
|
assert!(sq.neighbor(Direction::NorthWest).is_none());
|
||||||
assert!(square56.neighbor(Direction::West).is_none());
|
assert!(sq.neighbor(Direction::West).is_none());
|
||||||
|
|
||||||
let square63 = Square::from_index_unsafe(63);
|
let sq = Square::H8;
|
||||||
assert!(square63.neighbor(Direction::North).is_none());
|
assert!(sq.neighbor(Direction::North).is_none());
|
||||||
assert!(square63.neighbor(Direction::NorthEast).is_none());
|
assert!(sq.neighbor(Direction::NorthEast).is_none());
|
||||||
assert!(square63.neighbor(Direction::East).is_none());
|
assert!(sq.neighbor(Direction::East).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn display() {
|
fn display() {
|
||||||
assert_eq!(format!("{}", Square::c5()), "c5");
|
assert_eq!(format!("{}", Square::C5), "c5");
|
||||||
assert_eq!(format!("{}", Square::a1()), "a1");
|
assert_eq!(format!("{}", Square::A1), "a1");
|
||||||
assert_eq!(format!("{}", Square::h8()), "h8");
|
assert_eq!(format!("{}", Square::H8), "h8");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
|
||||||
|
|
||||||
/// Test helper utilities.
|
|
||||||
use crate::Square;
|
|
||||||
|
|
||||||
/// A constructor function that returns a Square representing the square on the
|
|
||||||
/// chessboard indicated by the algebraic notation.
|
|
||||||
macro_rules! sq_constructor {
|
|
||||||
($func_name:ident) => {
|
|
||||||
pub(crate) fn $func_name() -> Square {
|
|
||||||
Square::from_algebraic_str(stringify!($func_name)).expect(stringify!($func_name))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Square {
|
|
||||||
sq_constructor!(a1);
|
|
||||||
sq_constructor!(a2);
|
|
||||||
sq_constructor!(b1);
|
|
||||||
sq_constructor!(b2);
|
|
||||||
sq_constructor!(c3);
|
|
||||||
sq_constructor!(c5);
|
|
||||||
sq_constructor!(d2);
|
|
||||||
sq_constructor!(d3);
|
|
||||||
sq_constructor!(d4);
|
|
||||||
sq_constructor!(d5);
|
|
||||||
sq_constructor!(d6);
|
|
||||||
sq_constructor!(e2);
|
|
||||||
sq_constructor!(e3);
|
|
||||||
sq_constructor!(e4);
|
|
||||||
sq_constructor!(e5);
|
|
||||||
sq_constructor!(f2);
|
|
||||||
sq_constructor!(f3);
|
|
||||||
sq_constructor!(f4);
|
|
||||||
sq_constructor!(f5);
|
|
||||||
sq_constructor!(f6);
|
|
||||||
sq_constructor!(g3);
|
|
||||||
sq_constructor!(g5);
|
|
||||||
sq_constructor!(h8);
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue