[board] Fix some bugs in the starting position

Turns out I was doing the starting position really wrong. In an upcoming commit,
I will implement FEN output for Positions. While doing that work, I found several
issues that were causing the output of the FEN formatter to return garbage.

Implement a handful of unit tests to track down the errors.

Rename Shape::_ascii_representation() → Shape::to_ascii.
Implement to_ascii() on Piece.
This commit is contained in:
Eryn Wells 2024-01-21 14:56:03 -08:00
parent 84c9c43a7d
commit 829d9af52c
5 changed files with 87 additions and 42 deletions

View file

@ -53,7 +53,8 @@ impl BitBoard {
}
pub fn is_set(self, sq: Square) -> bool {
!(self & &sq.into()).is_empty()
let square_bitboard: BitBoard = sq.into();
!(self & square_bitboard).is_empty()
}
pub fn set_square(&mut self, sq: Square) {
@ -341,4 +342,10 @@ mod tests {
assert_eq!(a, bitboard![B5, C5, G7, H3]);
}
#[test]
fn from_square() {
assert_eq!(BitBoard::from(Square::A1), BitBoard(0b1));
assert_eq!(BitBoard::from(Square::H8), BitBoard(1 << 63));
}
}

View file

@ -63,7 +63,7 @@ impl Shape {
PROMOTABLE_SHAPES.iter()
}
fn _ascii_representation(&self) -> char {
fn to_ascii(&self) -> char {
match self {
Shape::Pawn => 'P',
Shape::Knight => 'N',
@ -83,12 +83,12 @@ impl TryFrom<char> for Shape {
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),
'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(TryFromError),
}
}
@ -105,13 +105,13 @@ impl TryFrom<&str> for Shape {
impl Into<char> for &Shape {
fn into(self) -> char {
self._ascii_representation()
self.to_ascii()
}
}
impl Into<char> for Shape {
fn into(self) -> char {
self._ascii_representation()
self.to_ascii()
}
}
@ -173,6 +173,10 @@ impl Piece {
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 {
@ -193,18 +197,18 @@ impl UnicodeDisplay for Piece {
f,
"{}",
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) => '♔',
(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) => '♔',
}
)
}
@ -212,7 +216,7 @@ impl UnicodeDisplay for Piece {
impl FENDisplay for Piece {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ascii = self.shape()._ascii_representation();
let ascii = self.shape().to_ascii();
write!(
f,
"{}",

View file

@ -36,12 +36,15 @@ struct ByColorAndShape([[BitBoard; 6]; 2]);
impl PieceBitBoards {
pub(super) fn new(pieces: [[BitBoard; 6]; 2]) -> Self {
use std::ops::BitOr;
let white_pieces = pieces[Color::White as usize]
.iter()
.fold(BitBoard::empty(), std::ops::BitOr::bitor);
let black_pieces = pieces[Color::White as usize]
.fold(BitBoard::empty(), BitOr::bitor);
let black_pieces = pieces[Color::Black as usize]
.iter()
.fold(BitBoard::empty(), std::ops::BitOr::bitor);
.fold(BitBoard::empty(), BitOr::bitor);
let all_pieces = white_pieces | black_pieces;
Self {

View file

@ -33,22 +33,22 @@ impl Position {
/// Return a starting position.
pub fn starting() -> Self {
let white_pieces = [
BitBoard::new(0x00FF000000000000),
BitBoard::new(0x4200000000000000),
BitBoard::new(0x2400000000000000),
BitBoard::new(0x8100000000000000),
BitBoard::new(0x1000000000000000),
BitBoard::new(0x0800000000000000),
let black_pieces = [
BitBoard::new(0b0000000011111111 << 48),
BitBoard::new(0b0100001000000000 << 48),
BitBoard::new(0b0010010000000000 << 48),
BitBoard::new(0b1000000100000000 << 48),
BitBoard::new(0b0000100000000000 << 48),
BitBoard::new(0b0001000000000000 << 48),
];
let black_pieces = [
BitBoard::new(0xFF00),
BitBoard::new(0x0042),
BitBoard::new(0x0024),
BitBoard::new(0x0081),
BitBoard::new(0x0010),
BitBoard::new(0x0008),
let white_pieces = [
BitBoard::new(0b1111111100000000),
BitBoard::new(0b0000000001000010),
BitBoard::new(0b0000000000100100),
BitBoard::new(0b0000000010000001),
BitBoard::new(0b0000000000001000),
BitBoard::new(0b0000000000010000),
];
Self {
@ -242,7 +242,32 @@ impl fmt::Display for Position {
#[cfg(test)]
mod tests {
use crate::{position, Castle, Color};
use crate::{position, Castle, Color, Position, Square};
#[test]
fn piece_on_square() {
let pos = test_position![
Black Bishop on F7,
];
let piece = pos.piece_on_square(Square::F7);
assert_eq!(piece, Some(piece!(Black Bishop on F7)));
}
#[test]
fn piece_in_starting_position() {
let pos = Position::starting();
println!("{pos}");
assert_eq!(
pos.piece_on_square(Square::H1),
Some(piece!(White Rook on H1))
);
assert_eq!(
pos.piece_on_square(Square::A8),
Some(piece!(Black Rook on A8))
);
}
#[test]
fn king_is_in_check() {

View file

@ -1,6 +1,6 @@
// Eryn Wells <eryn@erynwells.me>
use crate::{r#move::Castle, Color};
use crate::Color;
use std::{fmt, str::FromStr};
pub enum Direction {
@ -308,6 +308,12 @@ mod tests {
assert_eq!(sq.rank(), Rank::Four);
}
#[test]
fn to_index() {
assert_eq!(Square::A1 as usize, 0);
assert_eq!(Square::H8 as usize, 63);
}
#[test]
fn valid_neighbors() {
let sq = Square::E4;