make clippy happy
This commit is contained in:
parent
0fc6af54b9
commit
bb2b2b02f8
|
@ -13,12 +13,16 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Provides structs and functions for managing representations of a chess board.
|
||||||
|
|
||||||
use crate::boardfield::{Boardfield, BoardfieldOps};
|
use crate::boardfield::{Boardfield, BoardfieldOps};
|
||||||
use crate::error::FENParseError;
|
use crate::error::FENParseError;
|
||||||
use crate::piece::{PieceColor, PieceOnBoard, PieceType};
|
use crate::piece::{PieceColor, PieceOnBoard, PieceType};
|
||||||
use crate::utils::{algebraic_to_boardloc};
|
use crate::utils::{algebraic_to_boardloc};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
|
/// A struct to represent a chessboard and the pieces and state associated with it.
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
bitfield: Boardfield,
|
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.
|
// While this is a less efficient memory layout, having an array of where all the pieces are will make move generation significantly faster.
|
||||||
|
@ -38,6 +42,7 @@ pub struct Board {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Board {
|
impl Board {
|
||||||
|
/// Convert the current state of the board into a 41-byte packed board ID. This packed representation contains all piece data and flags associated with the chessboard and is sufficient for unique identification and compact storage of chessboards.
|
||||||
pub fn to_board_id(&self) -> [u8; 41] {
|
pub fn to_board_id(&self) -> [u8; 41] {
|
||||||
let mut res = [0u8; 41];
|
let mut res = [0u8; 41];
|
||||||
|
|
||||||
|
@ -76,22 +81,23 @@ impl Board {
|
||||||
}
|
}
|
||||||
res[32] = flags_byte;
|
res[32] = flags_byte;
|
||||||
|
|
||||||
let en_passant_target: [u8; 4] = match self.en_passant_target {
|
#[allow(clippy::cast_possible_truncation)] {
|
||||||
Some(target) => {
|
let en_passant_target: [u8; 4] = self.en_passant_target.map_or_else(|| u32::MAX.to_le_bytes(), |target| (target as i32).to_le_bytes());
|
||||||
(target as u32).to_le_bytes()
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
u32::MAX.to_le_bytes()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
res[33..37].copy_from_slice(&en_passant_target);
|
res[33..37].copy_from_slice(&en_passant_target);
|
||||||
|
|
||||||
res[37..39].copy_from_slice(&(self.halfmove_counter as u16).to_le_bytes());
|
res[37..39].copy_from_slice(&(self.halfmove_counter as u16).to_le_bytes());
|
||||||
res[39..41].copy_from_slice(&(self.move_counter as u16).to_le_bytes());
|
res[39..41].copy_from_slice(&(self.move_counter as u16).to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `Board` from a FEN string.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if an invalid FEN string is passed.
|
||||||
|
/// # Panics
|
||||||
|
/// This function, while containing code that can panic, will never panic as the string is bounds checked.
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
#[allow(clippy::match_on_vec_items)]
|
||||||
pub fn from_fen(fen: &str) -> Result<Self, FENParseError> {
|
pub fn from_fen(fen: &str) -> Result<Self, FENParseError> {
|
||||||
let components = fen.split(' ').collect::<Vec<&str>>();
|
let components = fen.split(' ').collect::<Vec<&str>>();
|
||||||
if components.len() != 6 {
|
if components.len() != 6 {
|
||||||
|
@ -146,7 +152,11 @@ impl Board {
|
||||||
file += 1;
|
file += 1;
|
||||||
}
|
}
|
||||||
_ if char.is_numeric() => {
|
_ if char.is_numeric() => {
|
||||||
let num = char.to_digit(10).unwrap() as isize;
|
let num;
|
||||||
|
// see above (.is_numeric())
|
||||||
|
#[allow(clippy::unwrap_used)] {
|
||||||
|
num = char.to_digit(10).unwrap() as isize;
|
||||||
|
}
|
||||||
|
|
||||||
if !(1..=8).contains(&num) {
|
if !(1..=8).contains(&num) {
|
||||||
return Err(FENParseError::CannotSkipToOutsideOfBoard { got: num, which_is: num });
|
return Err(FENParseError::CannotSkipToOutsideOfBoard { got: num, which_is: num });
|
||||||
|
@ -185,7 +195,7 @@ impl Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_fen_piece(c: char, rank: isize, file: isize) -> Result<PieceOnBoard, FENParseError> {
|
const fn create_fen_piece(c: char, rank: isize, file: isize) -> Result<PieceOnBoard, FENParseError> {
|
||||||
Ok(PieceOnBoard {
|
Ok(PieceOnBoard {
|
||||||
loc: (rank + file * 8) - 1,
|
loc: (rank + file * 8) - 1,
|
||||||
value: match c {
|
value: match c {
|
||||||
|
@ -193,20 +203,20 @@ fn create_fen_piece(c: char, rank: isize, file: isize) -> Result<PieceOnBoard, F
|
||||||
'n' => PieceColor::Black as u8 | PieceType::Knight as u8,
|
'n' => PieceColor::Black as u8 | PieceType::Knight as u8,
|
||||||
'b' => PieceColor::Black as u8 | PieceType::Bishop as u8,
|
'b' => PieceColor::Black as u8 | PieceType::Bishop as u8,
|
||||||
'q' => PieceColor::Black as u8 | PieceType::Queen as u8,
|
'q' => PieceColor::Black as u8 | PieceType::Queen as u8,
|
||||||
'k' => PieceColor::Black as u8 | PieceType::Knight as u8,
|
'k' => PieceColor::Black as u8 | PieceType::King as u8,
|
||||||
'p' => PieceColor::Black as u8 | PieceType::Pawn as u8,
|
'p' => PieceColor::Black as u8 | PieceType::Pawn as u8,
|
||||||
'R' => PieceColor::White as u8 | PieceType::Rook as u8,
|
'R' => PieceColor::White as u8 | PieceType::Rook as u8,
|
||||||
'N' => PieceColor::White as u8 | PieceType::Knight as u8,
|
'N' => PieceColor::White as u8 | PieceType::Knight as u8,
|
||||||
'B' => PieceColor::White as u8 | PieceType::Bishop as u8,
|
'B' => PieceColor::White as u8 | PieceType::Bishop as u8,
|
||||||
'Q' => PieceColor::White as u8 | PieceType::Queen as u8,
|
'Q' => PieceColor::White as u8 | PieceType::Queen as u8,
|
||||||
'K' => PieceColor::White as u8 | PieceType::Knight as u8,
|
'K' => PieceColor::White as u8 | PieceType::King as u8,
|
||||||
'P' => PieceColor::White as u8 | PieceType::Pawn as u8,
|
'P' => PieceColor::White as u8 | PieceType::Pawn as u8,
|
||||||
_ => return Err(FENParseError::InvalidPieceCharacter { got: c })
|
_ => return Err(FENParseError::InvalidPieceCharacter { got: c })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_bitfield_piece(c: char) -> Result<u8, FENParseError> {
|
const fn create_bitfield_piece(c: char) -> Result<u8, FENParseError> {
|
||||||
match c {
|
match c {
|
||||||
'r' => Ok(PieceColor::Black as u8 | PieceType::Rook as u8),
|
'r' => Ok(PieceColor::Black as u8 | PieceType::Rook as u8),
|
||||||
'n' => Ok(PieceColor::Black as u8 | PieceType::Knight as u8),
|
'n' => Ok(PieceColor::Black as u8 | PieceType::Knight as u8),
|
||||||
|
@ -227,6 +237,8 @@ fn create_bitfield_piece(c: char) -> Result<u8, FENParseError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::board::{Board, create_bitfield_piece, create_fen_piece};
|
use crate::board::{Board, create_bitfield_piece, create_fen_piece};
|
||||||
use crate::boardfield::BoardfieldOps;
|
use crate::boardfield::BoardfieldOps;
|
||||||
|
@ -276,6 +288,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
pub fn fen_parse_testing() {
|
pub fn fen_parse_testing() {
|
||||||
let board = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
|
let board = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
|
||||||
|
|
||||||
|
|
|
@ -13,15 +13,25 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Operations for working with a packed square-wise representation of a chessboard.
|
||||||
|
|
||||||
use crate::boardloc;
|
use crate::boardloc;
|
||||||
use crate::piece::{PieceColor, PieceType};
|
use crate::piece::{PieceColor, PieceType};
|
||||||
|
|
||||||
|
/// A Boardfield is a packed, square-wise representation of a chessboard. It **does not** include any extra metadata such as castling rights.
|
||||||
pub type Boardfield = [u8; 32];
|
pub type Boardfield = [u8; 32];
|
||||||
|
|
||||||
|
/// A trait to contain the operations available on a `Boardfield`
|
||||||
pub trait BoardfieldOps {
|
pub trait BoardfieldOps {
|
||||||
|
/// Create a new, entirely empty `Boardfield`.
|
||||||
fn new() -> Self where Self: Sized;
|
fn new() -> Self where Self: Sized;
|
||||||
|
/// Create a new `Boardfield` from the standard chess starting position.
|
||||||
fn startpos() -> Self where Self: Sized;
|
fn startpos() -> Self where Self: Sized;
|
||||||
|
/// Get the piece value stored at the provided boardloc.
|
||||||
fn get_pos(&self, boardloc: isize) -> u8;
|
fn get_pos(&self, boardloc: isize) -> u8;
|
||||||
|
/// Set the piece stored at the provided boardloc to the given piece value.
|
||||||
fn set_pos(&mut self, boardloc: isize, piece: u8);
|
fn set_pos(&mut self, boardloc: isize, piece: u8);
|
||||||
|
/// Get the entire boardfield as a 32-length bytearray.
|
||||||
fn get_field(&self) -> [u8; 32];
|
fn get_field(&self) -> [u8; 32];
|
||||||
}
|
}
|
||||||
impl BoardfieldOps for Boardfield {
|
impl BoardfieldOps for Boardfield {
|
||||||
|
@ -76,13 +86,13 @@ impl BoardfieldOps for Boardfield {
|
||||||
field
|
field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
fn get_pos(&self, boardloc: isize) -> u8 {
|
fn get_pos(&self, boardloc: isize) -> 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
|
// 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
|
// 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 {
|
assert!(boardloc <= 63, "boardloc out of range");
|
||||||
panic!("boardloc out of range");
|
assert!(boardloc > 0, "boardloc out of range");
|
||||||
}
|
|
||||||
|
|
||||||
let field = self[(boardloc / 2) as usize];
|
let field = self[(boardloc / 2) as usize];
|
||||||
let shift = 4 * (boardloc % 2);
|
let shift = 4 * (boardloc % 2);
|
||||||
|
@ -90,10 +100,10 @@ impl BoardfieldOps for Boardfield {
|
||||||
(field & (0b1111 << shift)) >> shift
|
(field & (0b1111 << shift)) >> shift
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
fn set_pos(&mut self, boardloc: isize, piece: u8) {
|
fn set_pos(&mut self, boardloc: isize, piece: u8) {
|
||||||
if boardloc > 63 {
|
assert!(boardloc <= 63, "boardloc out of range {boardloc}");
|
||||||
panic!("boardloc out of range {}", boardloc);
|
assert!(boardloc > 0, "boardloc out of range {boardloc}");
|
||||||
}
|
|
||||||
|
|
||||||
let field = self[(boardloc / 2) as usize];
|
let field = self[(boardloc / 2) as usize];
|
||||||
let shift = 4 * (boardloc % 2);
|
let shift = 4 * (boardloc % 2);
|
||||||
|
@ -136,6 +146,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn bitfield_board() {
|
fn bitfield_board() {
|
||||||
let field = Boardfield::startpos();
|
let field = Boardfield::startpos();
|
||||||
|
|
||||||
|
|
|
@ -13,18 +13,52 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Module to contain the various types of errors used by Bamboo.
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
/// An error type to represent the possible errors while parsing a FEN string
|
||||||
pub enum FENParseError {
|
pub enum FENParseError {
|
||||||
InvalidNumberOfComponents { got: isize },
|
/// A FEN string is expected to have six space-delimited components, but there were less or more than 6 components provided.
|
||||||
InvalidPlayerToMove { got: String },
|
InvalidNumberOfComponents {
|
||||||
EnPassantTargetParseError { e: AlgebraicNotationError },
|
/// How many components were actually found in the string
|
||||||
InvalidMoveCounter { e: ParseIntError },
|
got: isize
|
||||||
InvalidPieceCharacter { got: char },
|
},
|
||||||
CannotSkipToOutsideOfBoard { got: isize, which_is: isize },
|
|
||||||
|
/// The Player To Move field should either be a `w`, indicating white to move, or `b`, indicating black to move, but neither were found.
|
||||||
|
InvalidPlayerToMove {
|
||||||
|
/// The actual Player To Move field found in the FEN string.
|
||||||
|
got: String
|
||||||
|
},
|
||||||
|
|
||||||
|
/// An error occured parsing the en passant target square.
|
||||||
|
EnPassantTargetParseError {
|
||||||
|
/// The actual error that occured
|
||||||
|
e: AlgebraicNotationError
|
||||||
|
},
|
||||||
|
|
||||||
|
/// An error occured parsing the fullmove or halfmove color.
|
||||||
|
InvalidMoveCounter {
|
||||||
|
/// The parsing error that occured
|
||||||
|
e: ParseIntError
|
||||||
|
},
|
||||||
|
|
||||||
|
/// While parsing the piece data, an unexpected character was found
|
||||||
|
InvalidPieceCharacter {
|
||||||
|
/// The unexpected character that was found
|
||||||
|
got: char
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Chess boards are 8x8 but this FEN string attempted to place a piece outside of the board.
|
||||||
|
CannotSkipToOutsideOfBoard {
|
||||||
|
/// The skip value that was found
|
||||||
|
got: isize,
|
||||||
|
/// The location that skip value would place a piece at
|
||||||
|
which_is: isize
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for FENParseError {}
|
impl Error for FENParseError {}
|
||||||
|
@ -32,29 +66,42 @@ impl Error for FENParseError {}
|
||||||
impl Display for FENParseError {
|
impl Display for FENParseError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::InvalidNumberOfComponents { got } => write!(f, "Invalid number of components: got {}, expected 6", got),
|
Self::InvalidNumberOfComponents { got } => write!(f, "Invalid number of components: got {got}, expected 6"),
|
||||||
Self::InvalidPlayerToMove { got } => write!(f, "Invalid player to move: expected one of `wb`, got `{}`", 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::EnPassantTargetParseError { e } => write!(f, "Error parsing en passant target: {e}"),
|
||||||
Self::InvalidMoveCounter { e } => write!(f, "Invalid move counter: {}", 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::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)
|
Self::CannotSkipToOutsideOfBoard { got, which_is } => write!(f, "Cannot skip files to outside the board (tried to skip {got} positions, which would be at position {which_is})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
/// Represents an error that occured parsing an algebraic notation coordinate.
|
||||||
pub enum AlgebraicNotationError {
|
pub enum AlgebraicNotationError {
|
||||||
InvalidRank { got: String },
|
/// An invalid rank (1-8) was encountered
|
||||||
InvalidFile { got: String },
|
InvalidRank {
|
||||||
InvalidLength { got: isize }
|
/// The rank that was provided
|
||||||
|
got: String
|
||||||
|
},
|
||||||
|
/// An invalid file (a-h) was encountered
|
||||||
|
InvalidFile {
|
||||||
|
/// The file that was provided
|
||||||
|
got: String
|
||||||
|
},
|
||||||
|
/// Algebraic notation is two characters long, but this string was not two characters long
|
||||||
|
InvalidLength {
|
||||||
|
/// The length of the provided string
|
||||||
|
got: isize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Error for AlgebraicNotationError {}
|
impl Error for AlgebraicNotationError {}
|
||||||
impl Display for AlgebraicNotationError {
|
impl Display for AlgebraicNotationError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
AlgebraicNotationError::InvalidRank { got } => write!(f, "Invalid rank: expected one of `012345678`, got `{}`", got),
|
Self::InvalidRank { got } => write!(f, "Invalid rank: expected one of `12345678`, got `{got}`"),
|
||||||
AlgebraicNotationError::InvalidFile { got } => write!(f, "Invalid file: expected one of `abcdefgh`, got `{}`", got),
|
Self::InvalidFile { got } => write!(f, "Invalid file: expected one of `abcdefgh`, got `{got}`"),
|
||||||
AlgebraicNotationError::InvalidLength { got } => write!(f, "Invalid length, expected 2, got `{}`", got)
|
Self::InvalidLength { got } => write!(f, "Invalid length, expected 2, got `{got}`")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,6 +13,21 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! # Bamboo
|
||||||
|
//! Bamboo is a pure-rust chess engine that uses a machine learning model to provide NNUE-like board evaluation.
|
||||||
|
|
||||||
|
#![warn(clippy::pedantic)]
|
||||||
|
#![warn(clippy::nursery)]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![deny(clippy::unwrap_used)]
|
||||||
|
#![deny(clippy::expect_used)]
|
||||||
|
#![deny(clippy::missing_errors_doc)]
|
||||||
|
#![deny(clippy::missing_panics_doc)]
|
||||||
|
#![deny(clippy::missing_safety_doc)]
|
||||||
|
#![allow(clippy::must_use_candidate)]
|
||||||
|
#![allow(clippy::too_many_lines)]
|
||||||
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
pub mod boardfield;
|
pub mod boardfield;
|
||||||
pub mod piece;
|
pub mod piece;
|
||||||
pub mod board;
|
pub mod board;
|
||||||
|
|
|
@ -13,7 +13,10 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Functions, structs, traits and macros for working with `BambooNN`.
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
/// A feed-forward machine learning model used to evaluate board positions
|
||||||
pub struct BNNModel {
|
pub struct BNNModel {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,23 +13,28 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Structs and methods for working with chess pieces.
|
||||||
|
|
||||||
use crate::piece::PieceType::{Bishop, King, Knight, Pawn, Queen, Rook};
|
use crate::piece::PieceType::{Bishop, King, Knight, Pawn, Queen, Rook};
|
||||||
|
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
|
||||||
|
/// Represents a piece on the chessboard. Used for the piece-based evaluation in `Board`.
|
||||||
pub struct PieceOnBoard {
|
pub struct PieceOnBoard {
|
||||||
|
/// The boardloc this piece is at
|
||||||
pub loc: isize,
|
pub loc: isize,
|
||||||
|
/// The value of the piece
|
||||||
pub value: Piece
|
pub value: Piece
|
||||||
}
|
}
|
||||||
impl PieceOnBoard {
|
impl PieceOnBoard {
|
||||||
fn empty(loc: isize) -> Self {
|
const fn empty(loc: isize) -> Self {
|
||||||
PieceOnBoard {
|
Self {
|
||||||
loc,
|
loc,
|
||||||
value: 0u8
|
value: 0u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(loc: isize, value: Piece) -> Self {
|
const fn new(loc: isize, value: Piece) -> Self {
|
||||||
PieceOnBoard {
|
Self {
|
||||||
loc,
|
loc,
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
@ -38,37 +43,56 @@ impl PieceOnBoard {
|
||||||
|
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
/// The type of a chess piece, used for `u8`/four-bit piece representations
|
||||||
pub enum PieceType {
|
pub enum PieceType {
|
||||||
|
/// Represents a pawn
|
||||||
Pawn = 1,
|
Pawn = 1,
|
||||||
|
/// Represents a knight
|
||||||
Knight = 2,
|
Knight = 2,
|
||||||
|
/// Represents a bishop
|
||||||
Bishop = 3,
|
Bishop = 3,
|
||||||
|
/// Represents a rook
|
||||||
Rook = 4,
|
Rook = 4,
|
||||||
|
/// Represents a queen
|
||||||
Queen = 5,
|
Queen = 5,
|
||||||
|
/// Represents a king
|
||||||
King = 6
|
King = 6
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
/// Represents the color of a piece, used for `u8`/four-bit piece representations
|
||||||
pub enum PieceColor {
|
pub enum PieceColor {
|
||||||
|
/// Represents a white piece
|
||||||
White = 8,
|
White = 8,
|
||||||
|
/// Represents a black piece
|
||||||
Black = 0
|
Black = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PieceColor {
|
impl PieceColor {
|
||||||
pub fn invert(&self) -> Self {
|
/// Get the opposite of this color, e.g. for white return black and vice versa.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn invert(&self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::White => PieceColor::Black,
|
Self::White => Self::Black,
|
||||||
Self::Black => PieceColor::White
|
Self::Black => Self::White
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a four-bit packed chess piece.
|
||||||
pub type Piece = u8;
|
pub type Piece = u8;
|
||||||
|
|
||||||
|
/// A trait for the operations that can be done on a `Piece`
|
||||||
pub trait PieceOps {
|
pub trait PieceOps {
|
||||||
|
/// Determines if this piece is white. Inverse of `is_black`
|
||||||
fn is_white(value: u8) -> bool;
|
fn is_white(value: u8) -> bool;
|
||||||
|
/// Determines if this piece is black. Inverse of `is_black`
|
||||||
fn is_black(value: u8) -> bool;
|
fn is_black(value: u8) -> bool;
|
||||||
|
|
||||||
|
/// Get the `PieceType` of this piece. This returns an option, as a Piece can be any `u8`, but not all of them are actual piece types.
|
||||||
fn get_type(value: u8) -> Option<PieceType>;
|
fn get_type(value: u8) -> Option<PieceType>;
|
||||||
|
/// Gets the color of this piece. This effectively just checks if the fourth bit is set, and as such does not return an option.
|
||||||
fn get_color(value: u8) -> PieceColor;
|
fn get_color(value: u8) -> PieceColor;
|
||||||
}
|
}
|
||||||
impl PieceOps for Piece {
|
impl PieceOps for Piece {
|
||||||
|
@ -76,7 +100,7 @@ impl PieceOps for Piece {
|
||||||
if value < 8 {
|
if value < 8 {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
value & PieceColor::White as u8 != 0
|
value & PieceColor::White as Self != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +130,7 @@ impl PieceOps for Piece {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
mod piece_tests {
|
mod piece_tests {
|
||||||
use crate::piece::{Piece, PieceColor, PieceOnBoard, PieceOps, PieceType};
|
use crate::piece::{Piece, PieceColor, PieceOnBoard, PieceOps, PieceType};
|
||||||
|
|
||||||
|
@ -159,6 +184,7 @@ mod piece_tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn piece_color_checks() {
|
fn piece_color_checks() {
|
||||||
assert!(!Piece::is_white(PieceType::Pawn as u8 | PieceColor::Black as u8));
|
assert!(!Piece::is_white(PieceType::Pawn as u8 | PieceColor::Black as u8));
|
||||||
assert!(Piece::is_white(PieceType::Pawn as u8 | PieceColor::White as u8));
|
assert!(Piece::is_white(PieceType::Pawn as u8 | PieceColor::White as u8));
|
||||||
|
|
|
@ -13,8 +13,15 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Provides various utility functions and macros
|
||||||
|
|
||||||
use crate::error::AlgebraicNotationError;
|
use crate::error::AlgebraicNotationError;
|
||||||
|
|
||||||
|
/// Convert an algebraic notation, such as `a1`, to the corresponding boardloc.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the algebraic string is invalid.
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
pub fn algebraic_to_boardloc(algebraic: &str) -> Result<isize, AlgebraicNotationError> {
|
pub fn algebraic_to_boardloc(algebraic: &str) -> Result<isize, AlgebraicNotationError> {
|
||||||
if algebraic.chars().count() != 2 {
|
if algebraic.chars().count() != 2 {
|
||||||
return Err(AlgebraicNotationError::InvalidLength { got: algebraic.chars().count() as isize })
|
return Err(AlgebraicNotationError::InvalidLength { got: algebraic.chars().count() as isize })
|
||||||
|
@ -38,12 +45,19 @@ pub fn algebraic_to_boardloc(algebraic: &str) -> Result<isize, AlgebraicNotation
|
||||||
Ok(rank + file * 8)
|
Ok(rank + file * 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An array for converting boardloc to algebraic notation. Do not use this directly, use `boardloc_to_algebraic`.
|
||||||
pub const BOARDLOC_TO_ALG: [&str; 64] = ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "g1", "g2", "g3", "g4", "g5", "g6", "g7", "g8", "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8"];
|
pub const BOARDLOC_TO_ALG: [&str; 64] = ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "g1", "g2", "g3", "g4", "g5", "g6", "g7", "g8", "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8"];
|
||||||
|
|
||||||
pub fn boardloc_to_algebraic<'a>(boardloc: isize) -> &'a str {
|
/// Convert a 0-63 boardloc into an algebraic notation string.
|
||||||
|
/// # Panics
|
||||||
|
/// This function will **panic** if you provide an invalid boardloc. Please bounds check it first.
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
pub const fn boardloc_to_algebraic<'a>(boardloc: isize) -> &'a str {
|
||||||
BOARDLOC_TO_ALG[boardloc as usize]
|
BOARDLOC_TO_ALG[boardloc as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A macro to shorten `algebraic_to_boardloc`. This will **panic** if you provide an invalid algebraic notation.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! boardloc {
|
macro_rules! boardloc {
|
||||||
($alg:expr) => {
|
($alg:expr) => {
|
||||||
|
@ -51,6 +65,7 @@ macro_rules! boardloc {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A macro to shorten `boardloc_to_algebraic`. This will **panic** if you provide an invalid (out of boudns) boardloc.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! algebraic {
|
macro_rules! algebraic {
|
||||||
($boardloc:expr) => {
|
($boardloc:expr) => {
|
||||||
|
@ -59,6 +74,7 @@ macro_rules! algebraic {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::utils::{algebraic_to_boardloc, boardloc_to_algebraic};
|
use crate::utils::{algebraic_to_boardloc, boardloc_to_algebraic};
|
||||||
|
|
||||||
|
@ -73,7 +89,7 @@ mod tests {
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn boardloc_invalid_file() {
|
fn boardloc_invalid_file() {
|
||||||
if let Err(e) = algebraic_to_boardloc("j8") {
|
if let Err(e) = algebraic_to_boardloc("j8") {
|
||||||
println!("{}", e);
|
println!("{e}");
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +98,7 @@ mod tests {
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn boardloc_invalid_length() {
|
fn boardloc_invalid_length() {
|
||||||
if let Err(e) = algebraic_to_boardloc("jsdf8") {
|
if let Err(e) = algebraic_to_boardloc("jsdf8") {
|
||||||
println!("{}", e);
|
println!("{e}");
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +107,7 @@ mod tests {
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn boardloc_invalid_rank() {
|
fn boardloc_invalid_rank() {
|
||||||
if let Err(e) = algebraic_to_boardloc("a9") {
|
if let Err(e) = algebraic_to_boardloc("a9") {
|
||||||
println!("{}", e);
|
println!("{e}");
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue