fail miserably at parsing fen (i have failed in the same way like 6 times now)
This commit is contained in:
parent
e64128fbf1
commit
530d316dc1
251
src/board.rs
251
src/board.rs
|
@ -1,144 +1,161 @@
|
|||
// Bitpacked board:
|
||||
// One piece can be represented with 4 bits. Therefore, we can fit two pieces in one byte:
|
||||
// 0b0110_1110 -
|
||||
// | \
|
||||
// Black king \- White king
|
||||
// As there are 64 spaces on the board, we can pack the board into a 256-bit integer, and we use that as the index for transposition tables and such.
|
||||
// Boards are stored as two 128-bit bitfields, to make accessing them as efficient as possible.
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::num::ParseIntError;
|
||||
use crate::boardfield::{Boardfield, BoardfieldOps};
|
||||
use crate::piece::{PieceColor, PieceOnBoard, PieceType};
|
||||
use crate::utils::{algebraic_to_boardloc, AlgebraicNotationError, boardloc_to_algebraic};
|
||||
|
||||
/*
|
||||
#[derive(Debug)]
|
||||
pub struct Board {
|
||||
bitfield: Boardfield,
|
||||
// While this is a less efficient memory layout, having an array of where all the pieces are will make move generation significantly faster.
|
||||
piecelist: Vec<PieceOnBoard>,
|
||||
|
||||
a b c d e f g h
|
||||
turn: PieceColor,
|
||||
|
||||
8 56 57 58 59 60 61 62 63 8 -+
|
||||
7 48 49 50 51 52 53 54 55 7 |- bf2
|
||||
6 40 41 42 43 44 45 46 47 6 |
|
||||
5 32 33 34 35 36 37 38 39 5 -+
|
||||
4 24 25 26 27 28 29 30 31 4 -+
|
||||
3 16 17 18 19 20 21 22 23 3 |
|
||||
2 8 9 10 11 12 13 14 15 2 |- bf1
|
||||
1 0 1 2 3 4 5 6 7 1 -+
|
||||
castle_white_kingside: bool,
|
||||
castle_white_queenside: bool,
|
||||
castle_black_kingside: bool,
|
||||
castle_black_queenside: bool,
|
||||
|
||||
a b c d e f g h
|
||||
*/
|
||||
en_passant_target: Option<usize>,
|
||||
|
||||
use crate::piece::{PieceColor, PieceType};
|
||||
|
||||
type Boardfield = [u8; 32];
|
||||
pub trait BoardfieldOps {
|
||||
fn new() -> Self where Self: Sized;
|
||||
fn startpos() -> Self where Self: Sized;
|
||||
fn get_pos(&self, boardloc: usize) -> u8;
|
||||
fn set_pos(&mut self, boardloc: usize, piece: u8);
|
||||
}
|
||||
impl BoardfieldOps for Boardfield {
|
||||
fn new() -> Self where Self: Sized {
|
||||
[0u8; 32]
|
||||
move_counter: usize,
|
||||
halfmove_counter: usize
|
||||
}
|
||||
|
||||
fn startpos() -> Self where Self: Sized {
|
||||
let mut field = Self::new();
|
||||
|
||||
// a1-h1 (RNBQKBNR)
|
||||
field.set_pos(0, PieceColor::White as u8 | PieceType::Rook as u8);
|
||||
field.set_pos(1, PieceColor::White as u8 | PieceType::Knight as u8);
|
||||
field.set_pos(2, PieceColor::White as u8 | PieceType::Bishop as u8);
|
||||
field.set_pos(3, PieceColor::White as u8 | PieceType::Queen as u8);
|
||||
field.set_pos(4, PieceColor::White as u8 | PieceType::King as u8);
|
||||
field.set_pos(5, PieceColor::White as u8 | PieceType::Bishop as u8);
|
||||
field.set_pos(6, PieceColor::White as u8 | PieceType::Knight as u8);
|
||||
field.set_pos(7, PieceColor::White as u8 | PieceType::Rook as u8);
|
||||
|
||||
// a2-h2 (PPPPPPPP)
|
||||
field.set_pos(8, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(9, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(10, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(11, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(12, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(13, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(14, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(15, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
|
||||
// a7-h7 (pppppppp)
|
||||
field.set_pos(48, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(49, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(50, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(51, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(52, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(53, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(54, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(55, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
|
||||
// a8-h8 (RNBQKBNR)
|
||||
field.set_pos(56, PieceColor::Black as u8 | PieceType::Rook as u8);
|
||||
field.set_pos(57, PieceColor::Black as u8 | PieceType::Knight as u8);
|
||||
field.set_pos(58, PieceColor::Black as u8 | PieceType::Bishop as u8);
|
||||
field.set_pos(59, PieceColor::Black as u8 | PieceType::Queen as u8);
|
||||
field.set_pos(60, PieceColor::Black as u8 | PieceType::King as u8);
|
||||
field.set_pos(61, PieceColor::Black as u8 | PieceType::Bishop as u8);
|
||||
field.set_pos(62, PieceColor::Black as u8 | PieceType::Knight as u8);
|
||||
field.set_pos(63, PieceColor::Black as u8 | PieceType::Rook as u8);
|
||||
|
||||
|
||||
field
|
||||
#[derive(Debug)]
|
||||
pub enum FENParseError {
|
||||
InvalidNumberOfComponents { got: usize },
|
||||
InvalidPlayerToMove { got: String },
|
||||
EnPassantTargetParseError { e: AlgebraicNotationError },
|
||||
InvalidMoveCounter { e: ParseIntError },
|
||||
InvalidPieceCharacter { got: char },
|
||||
CannotSkipToOutsideOfBoard { got: usize, which_is: usize },
|
||||
}
|
||||
impl Error for FENParseError {}
|
||||
impl Display for FENParseError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::InvalidNumberOfComponents { got } => write!(f, "Invalid number of components: got {}, expected 6", got),
|
||||
Self::InvalidPlayerToMove { got } => write!(f, "Invalid player to move: expected one of `wb`, got `{}`", got),
|
||||
Self::EnPassantTargetParseError { e } => write!(f, "Error parsing en passant target: {}", e),
|
||||
Self::InvalidMoveCounter { e } => write!(f, "Invalid move counter: {}", e),
|
||||
Self::InvalidPieceCharacter { got } => write!(f, "Invalid piece character: expected one of `rnbqkpRNBQKP`, got `{}`", got),
|
||||
Self::CannotSkipToOutsideOfBoard { got, which_is } => write!(f, "Cannot skip files to outside the board (tried to skip {} positions, which would be at position {})", got, which_is)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pos(&self, boardloc: usize) -> u8 {
|
||||
// bf1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
||||
// bf2: 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
||||
|
||||
if boardloc > 63 {
|
||||
panic!("boardloc out of range");
|
||||
impl Board {
|
||||
pub fn from_fen(fen: &str) -> Result<Self, FENParseError> {
|
||||
let components = fen.split(' ').collect::<Vec<&str>>();
|
||||
if components.len() != 6 {
|
||||
return Err(FENParseError::InvalidNumberOfComponents { got: components.len() })
|
||||
}
|
||||
|
||||
let field = self[boardloc / 2];
|
||||
let shift = 4 * (boardloc % 2);
|
||||
// turn to move
|
||||
let turn = match components[1] {
|
||||
"w" => PieceColor::White,
|
||||
"b" => PieceColor::Black,
|
||||
_ => return Err(FENParseError::InvalidPlayerToMove { got: components[0].to_string() })
|
||||
};
|
||||
|
||||
(field & (0b1111 << shift)) >> shift
|
||||
// castling rights
|
||||
let castle_white_kingside = components[2].contains("K");
|
||||
let castle_white_queenside = components[2].contains("Q");
|
||||
let castle_black_kingside = components[2].contains("k");
|
||||
let castle_black_queenside = components[2].contains("q");
|
||||
|
||||
let en_passant_target = match components[3] {
|
||||
"-" => None,
|
||||
_ => match algebraic_to_boardloc(components[3]) {
|
||||
Ok(t) => Some(t),
|
||||
Err(e) => return Err(FENParseError::EnPassantTargetParseError { e })
|
||||
}
|
||||
};
|
||||
|
||||
let halfmove_counter = match components[4].parse() {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Err(FENParseError::InvalidMoveCounter { e })
|
||||
};
|
||||
|
||||
let move_counter = match components[5].parse() {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Err(FENParseError::InvalidMoveCounter { e })
|
||||
};
|
||||
|
||||
// parse the actual piece string
|
||||
|
||||
let mut piecelist = Vec::new();
|
||||
let mut bitfield = Boardfield::new();
|
||||
|
||||
|
||||
|
||||
Ok(Self {
|
||||
bitfield,
|
||||
piecelist,
|
||||
turn,
|
||||
castle_white_kingside,
|
||||
castle_white_queenside,
|
||||
castle_black_kingside,
|
||||
castle_black_queenside,
|
||||
en_passant_target,
|
||||
move_counter,
|
||||
halfmove_counter,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn set_pos(&mut self, boardloc: usize, piece: u8) {
|
||||
if boardloc > 63 {
|
||||
panic!("boardloc out of range");
|
||||
fn create_fen_piece(c: char, rank: usize, file: usize) -> Result<PieceOnBoard, FENParseError> {
|
||||
Ok(PieceOnBoard {
|
||||
loc: rank + file * 8,
|
||||
value: match c {
|
||||
'r' => PieceColor::Black as u8 | PieceType::Rook as u8,
|
||||
'n' => PieceColor::Black as u8 | PieceType::Knight as u8,
|
||||
'b' => PieceColor::Black as u8 | PieceType::Bishop as u8,
|
||||
'q' => PieceColor::Black as u8 | PieceType::Queen as u8,
|
||||
'k' => PieceColor::Black as u8 | PieceType::Knight as u8,
|
||||
'p' => PieceColor::Black as u8 | PieceType::Pawn as u8,
|
||||
'R' => PieceColor::White as u8 | PieceType::Rook as u8,
|
||||
'N' => PieceColor::White as u8 | PieceType::Knight as u8,
|
||||
'B' => PieceColor::White as u8 | PieceType::Bishop as u8,
|
||||
'Q' => PieceColor::White as u8 | PieceType::Queen as u8,
|
||||
'K' => PieceColor::White as u8 | PieceType::Knight as u8,
|
||||
'P' => PieceColor::White as u8 | PieceType::Pawn as u8,
|
||||
_ => return Err(FENParseError::InvalidPieceCharacter { got: c })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
let field = self[boardloc / 2];
|
||||
let shift = 4 * (boardloc % 2);
|
||||
|
||||
// clear out the field
|
||||
let bf_cleared = field & !(0b1111 << shift);
|
||||
// then shift over the actual piece data
|
||||
let piece_shifted = piece << shift;
|
||||
// then OR them together
|
||||
let bf_new = bf_cleared | piece_shifted;
|
||||
|
||||
self[boardloc / 2] = bf_new;
|
||||
fn create_bitfield_piece(c: char) -> Result<u8, FENParseError> {
|
||||
match c {
|
||||
'r' => Ok(PieceColor::Black as u8 | PieceType::Rook as u8),
|
||||
'n' => Ok(PieceColor::Black as u8 | PieceType::Knight as u8),
|
||||
'b' => Ok(PieceColor::Black as u8 | PieceType::Bishop as u8),
|
||||
'q' => Ok(PieceColor::Black as u8 | PieceType::Queen as u8),
|
||||
'k' => Ok(PieceColor::Black as u8 | PieceType::Knight as u8),
|
||||
'p' => Ok(PieceColor::Black as u8 | PieceType::Pawn as u8),
|
||||
'R' => Ok(PieceColor::White as u8 | PieceType::Rook as u8),
|
||||
'N' => Ok(PieceColor::White as u8 | PieceType::Knight as u8),
|
||||
'B' => Ok(PieceColor::White as u8 | PieceType::Bishop as u8),
|
||||
'Q' => Ok(PieceColor::White as u8 | PieceType::Queen as u8),
|
||||
'K' => Ok(PieceColor::White as u8 | PieceType::Knight as u8),
|
||||
'P' => Ok(PieceColor::White as u8 | PieceType::Pawn as u8),
|
||||
_ => Err(FENParseError::InvalidPieceCharacter { got: c })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::board::{Boardfield, BoardfieldOps};
|
||||
use crate::board::Board;
|
||||
use crate::boardfield::BoardfieldOps;
|
||||
use crate::piece::{PieceColor, PieceType};
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn get_out_of_bounds() {
|
||||
let field = Boardfield::startpos();
|
||||
field.get_pos(64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn set_out_of_bounds() {
|
||||
let mut field = Boardfield::startpos();
|
||||
field.set_pos(64, 0u8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitfield_board() {
|
||||
let field = Boardfield::startpos();
|
||||
pub fn fen_parse_testing() {
|
||||
let board = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
|
||||
|
||||
let field = board.bitfield;
|
||||
assert_eq!(field.get_pos(0), PieceType::Rook as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(1), PieceType::Knight as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(2), PieceType::Bishop as u8 | PieceColor::White as u8);
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
// Bitpacked board:
|
||||
// One piece can be represented with 4 bits. Therefore, we can fit two pieces in one byte:
|
||||
// 0b0110_1110 -
|
||||
// | \
|
||||
// Black king \- White king
|
||||
// As there are 64 spaces on the board, we can pack the board into a 256-bit integer, and we use that as the index for transposition tables and such.
|
||||
// Boards are stored as a 32-byte bitfield, to make accessing them as efficient as possible.
|
||||
// We could theoretically pack it into two u128s, but some basic testing seems like LLVM can optimize array indexing faster than the bucketload of shift ops needed to pack it into u128s.
|
||||
|
||||
|
||||
/*
|
||||
|
||||
a b c d e f g h
|
||||
|
||||
8 56 57 58 59 60 61 62 63 8 -+
|
||||
7 48 49 50 51 52 53 54 55 7 |- bf2
|
||||
6 40 41 42 43 44 45 46 47 6 |
|
||||
5 32 33 34 35 36 37 38 39 5 -+
|
||||
4 24 25 26 27 28 29 30 31 4 -+
|
||||
3 16 17 18 19 20 21 22 23 3 |
|
||||
2 8 9 10 11 12 13 14 15 2 |- bf1
|
||||
1 0 1 2 3 4 5 6 7 1 -+
|
||||
|
||||
a b c d e f g h
|
||||
*/
|
||||
|
||||
use crate::piece::{PieceColor, PieceType};
|
||||
|
||||
pub type Boardfield = [u8; 32];
|
||||
pub trait BoardfieldOps {
|
||||
fn new() -> Self where Self: Sized;
|
||||
fn startpos() -> Self where Self: Sized;
|
||||
fn get_pos(&self, boardloc: usize) -> u8;
|
||||
fn set_pos(&mut self, boardloc: usize, piece: u8);
|
||||
}
|
||||
impl BoardfieldOps for Boardfield {
|
||||
fn new() -> Self where Self: Sized {
|
||||
[0u8; 32]
|
||||
}
|
||||
|
||||
fn startpos() -> Self where Self: Sized {
|
||||
let mut field = Self::new();
|
||||
|
||||
// a1-h1 (RNBQKBNR)
|
||||
field.set_pos(0, PieceColor::White as u8 | PieceType::Rook as u8);
|
||||
field.set_pos(1, PieceColor::White as u8 | PieceType::Knight as u8);
|
||||
field.set_pos(2, PieceColor::White as u8 | PieceType::Bishop as u8);
|
||||
field.set_pos(3, PieceColor::White as u8 | PieceType::Queen as u8);
|
||||
field.set_pos(4, PieceColor::White as u8 | PieceType::King as u8);
|
||||
field.set_pos(5, PieceColor::White as u8 | PieceType::Bishop as u8);
|
||||
field.set_pos(6, PieceColor::White as u8 | PieceType::Knight as u8);
|
||||
field.set_pos(7, PieceColor::White as u8 | PieceType::Rook as u8);
|
||||
|
||||
// a2-h2 (PPPPPPPP)
|
||||
field.set_pos(8, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(9, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(10, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(11, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(12, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(13, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(14, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(15, PieceColor::White as u8 | PieceType::Pawn as u8);
|
||||
|
||||
// a7-h7 (pppppppp)
|
||||
field.set_pos(48, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(49, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(50, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(51, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(52, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(53, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(54, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
field.set_pos(55, PieceColor::Black as u8 | PieceType::Pawn as u8);
|
||||
|
||||
// a8-h8 (RNBQKBNR)
|
||||
field.set_pos(56, PieceColor::Black as u8 | PieceType::Rook as u8);
|
||||
field.set_pos(57, PieceColor::Black as u8 | PieceType::Knight as u8);
|
||||
field.set_pos(58, PieceColor::Black as u8 | PieceType::Bishop as u8);
|
||||
field.set_pos(59, PieceColor::Black as u8 | PieceType::Queen as u8);
|
||||
field.set_pos(60, PieceColor::Black as u8 | PieceType::King as u8);
|
||||
field.set_pos(61, PieceColor::Black as u8 | PieceType::Bishop as u8);
|
||||
field.set_pos(62, PieceColor::Black as u8 | PieceType::Knight as u8);
|
||||
field.set_pos(63, PieceColor::Black as u8 | PieceType::Rook as u8);
|
||||
|
||||
|
||||
field
|
||||
}
|
||||
|
||||
fn get_pos(&self, boardloc: usize) -> u8 {
|
||||
// bf1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
||||
// bf2: 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
||||
|
||||
if boardloc > 63 {
|
||||
panic!("boardloc out of range");
|
||||
}
|
||||
|
||||
let field = self[boardloc / 2];
|
||||
let shift = 4 * (boardloc % 2);
|
||||
|
||||
(field & (0b1111 << shift)) >> shift
|
||||
}
|
||||
|
||||
fn set_pos(&mut self, boardloc: usize, piece: u8) {
|
||||
if boardloc > 63 {
|
||||
panic!("boardloc out of range");
|
||||
}
|
||||
|
||||
let field = self[boardloc / 2];
|
||||
let shift = 4 * (boardloc % 2);
|
||||
|
||||
// clear out the field
|
||||
let bf_cleared = field & !(0b1111 << shift);
|
||||
// then shift over the actual piece data
|
||||
let piece_shifted = piece << shift;
|
||||
// then OR them together
|
||||
let bf_new = bf_cleared | piece_shifted;
|
||||
|
||||
self[boardloc / 2] = bf_new;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::boardfield::{Boardfield, BoardfieldOps};
|
||||
use crate::piece::{PieceColor, PieceType};
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn get_out_of_bounds() {
|
||||
let field = Boardfield::startpos();
|
||||
field.get_pos(64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn set_out_of_bounds() {
|
||||
let mut field = Boardfield::startpos();
|
||||
field.set_pos(64, 0u8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitfield_board() {
|
||||
let field = Boardfield::startpos();
|
||||
|
||||
assert_eq!(field.get_pos(0), PieceType::Rook as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(1), PieceType::Knight as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(2), PieceType::Bishop as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(3), PieceType::Queen as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(4), PieceType::King as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(5), PieceType::Bishop as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(6), PieceType::Knight as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(7), PieceType::Rook as u8 | PieceColor::White as u8);
|
||||
|
||||
assert_eq!(field.get_pos(8), PieceType::Pawn as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(9), PieceType::Pawn as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(10), PieceType::Pawn as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(11), PieceType::Pawn as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(12), PieceType::Pawn as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(13), PieceType::Pawn as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(14), PieceType::Pawn as u8 | PieceColor::White as u8);
|
||||
assert_eq!(field.get_pos(15), PieceType::Pawn as u8 | PieceColor::White as u8);
|
||||
|
||||
assert_eq!(field.get_pos(48), PieceType::Pawn as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(49), PieceType::Pawn as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(50), PieceType::Pawn as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(51), PieceType::Pawn as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(52), PieceType::Pawn as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(53), PieceType::Pawn as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(54), PieceType::Pawn as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(55), PieceType::Pawn as u8 | PieceColor::Black as u8);
|
||||
|
||||
assert_eq!(field.get_pos(56), PieceType::Rook as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(57), PieceType::Knight as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(58), PieceType::Bishop as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(59), PieceType::Queen as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(60), PieceType::King as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(61), PieceType::Bishop as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(62), PieceType::Knight as u8 | PieceColor::Black as u8);
|
||||
assert_eq!(field.get_pos(63), PieceType::Rook as u8 | PieceColor::Black as u8);
|
||||
}
|
||||
}
|
|
@ -7,8 +7,10 @@ use spin::Mutex;
|
|||
#[cfg(not(tarpaulin_include))] // UCI parse engine is not tested with tarpaulin, outer test harness is planned
|
||||
pub mod uci;
|
||||
|
||||
pub mod board;
|
||||
pub mod boardfield;
|
||||
pub mod piece;
|
||||
pub mod board;
|
||||
pub mod utils;
|
||||
|
||||
pub const ENGINE_NAME: &str = "Bamboo";
|
||||
pub const ENGINE_VERSION: &str = "1.0";
|
||||
|
|
16
src/piece.rs
16
src/piece.rs
|
@ -1,3 +1,18 @@
|
|||
#[derive(Debug)]
|
||||
pub struct PieceOnBoard {
|
||||
pub loc: usize,
|
||||
pub value: Piece
|
||||
}
|
||||
impl PieceOnBoard {
|
||||
fn new(loc: usize, value: Piece) -> Self {
|
||||
Self {
|
||||
loc,
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum PieceType {
|
||||
Pawn = 1,
|
||||
|
@ -8,6 +23,7 @@ pub enum PieceType {
|
|||
King = 6
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum PieceColor {
|
||||
White = 8,
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
|
||||
a b c d e f g h
|
||||
|
||||
8 56 57 58 59 60 61 62 63 8 -+
|
||||
7 48 49 50 51 52 53 54 55 7 |- bf2
|
||||
6 40 41 42 43 44 45 46 47 6 |
|
||||
5 32 33 34 35 36 37 38 39 5 -+
|
||||
4 24 25 26 27 28 29 30 31 4 -+
|
||||
3 16 17 18 19 20 21 22 23 3 |
|
||||
2 8 9 10 11 12 13 14 15 2 |- bf1
|
||||
1 0 1 2 3 4 5 6 7 1 -+
|
||||
|
||||
a b c d e f g h
|
||||
*/
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AlgebraicNotationError {
|
||||
InvalidRank { got: String },
|
||||
InvalidFile { got: String },
|
||||
InvalidLength { got: usize }
|
||||
}
|
||||
impl Error for AlgebraicNotationError {}
|
||||
impl Display for AlgebraicNotationError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AlgebraicNotationError::InvalidRank { got } => write!(f, "Invalid rank: expected one of `012345678`, got `{}`", got),
|
||||
AlgebraicNotationError::InvalidFile { got } => write!(f, "Invalid file: expected one of `abcdefgh`, got `{}`", got),
|
||||
AlgebraicNotationError::InvalidLength { got } => write!(f, "Invalid length, expected 2, got `{}`", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn algebraic_to_boardloc(algebraic: &str) -> Result<usize, AlgebraicNotationError> {
|
||||
if algebraic.chars().count() != 2 {
|
||||
return Err(AlgebraicNotationError::InvalidLength { got: algebraic.chars().count() })
|
||||
}
|
||||
|
||||
let chars: Vec<char> = algebraic.chars().collect();
|
||||
|
||||
let file_char = chars[0];
|
||||
let rank_char = chars[1];
|
||||
|
||||
if (file_char as u8) < b'a' || (file_char as u8) > b'h' {
|
||||
return Err(AlgebraicNotationError::InvalidFile { got: file_char.to_string() })
|
||||
}
|
||||
if (rank_char as u8) < b'0' || (rank_char as u8) > b'8' {
|
||||
return Err(AlgebraicNotationError::InvalidRank { got: rank_char.to_string() })
|
||||
}
|
||||
|
||||
let file = (file_char as u8 - 97) as usize;
|
||||
let rank = (rank_char as u8 - 48) as usize;
|
||||
|
||||
Ok(rank + file * 8)
|
||||
}
|
||||
|
||||
const BOARDLOC_TO_ALG: [&str; 64] = ["a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", "h0", "h1", "h2", "h3", "h4", "h5", "h6", "h7"];
|
||||
|
||||
pub fn boardloc_to_algebraic<'a>(boardloc: usize) -> &'a str {
|
||||
BOARDLOC_TO_ALG[boardloc]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::utils::{algebraic_to_boardloc, boardloc_to_algebraic};
|
||||
|
||||
#[test]
|
||||
fn boardloc_testing() {
|
||||
for i in 0..64 {
|
||||
assert_eq!(algebraic_to_boardloc(boardloc_to_algebraic(i)).unwrap(), i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn boardloc_invalid_file() {
|
||||
if let Err(e) = algebraic_to_boardloc("j8") {
|
||||
println!("{}", e);
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn boardloc_invalid_length() {
|
||||
if let Err(e) = algebraic_to_boardloc("jsdf8") {
|
||||
println!("{}", e);
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn boardloc_invalid_rank() {
|
||||
if let Err(e) = algebraic_to_boardloc("a9") {
|
||||
println!("{}", e);
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue