use thiserror::Error;
use crate::{
CastlingRights, Columns::COLUMN_AMOUNT, DEFAULT_BOARD_SETUP, Fen, Field, GameResult, Lan,
Piece, PlayerColor, Rows::ROW_AMOUNT, Ruleset, Turn,
};
pub type BoardSetup = [[Option<Piece>; COLUMN_AMOUNT]; ROW_AMOUNT];
#[derive(Error, Debug)]
pub enum PositionError {
#[error("The given turn is not legal in the current position: {0}")]
IllegalTurnError(String),
}
#[derive(Clone, PartialEq, Debug)]
pub struct Position {
board_position: BoardSetup,
active_color: PlayerColor,
castling_white: CastlingRights,
castling_black: CastlingRights,
en_passant: Option<Field>,
halfmove_clock: u16,
fullmove_counter: u16,
}
impl Position {
#[must_use]
pub fn new(
board_position: BoardSetup,
active_color: PlayerColor,
castling_white: CastlingRights,
castling_black: CastlingRights,
en_passant: Option<Field>,
halfmove_clock: u16,
fullmove_counter: u16,
) -> Position {
Position {
board_position,
active_color,
castling_white,
castling_black,
en_passant,
halfmove_clock,
fullmove_counter,
}
}
#[must_use]
pub fn get_board_position(&self) -> &BoardSetup {
&self.board_position
}
pub fn set_board_position(&mut self, board_position: BoardSetup) {
self.board_position = board_position;
}
#[must_use]
pub fn get_field_occupation(&self, field: &Field) -> Option<Piece> {
self.board_position[field.get_row() as usize][field.get_column() as usize]
}
pub fn set_field_occupation(&mut self, field: &Field, piece: Option<Piece>) {
self.board_position[field.get_row() as usize][field.get_column() as usize] = piece;
}
#[must_use]
pub fn get_active_color(&self) -> PlayerColor {
self.active_color
}
pub fn set_active_color(&mut self, active_color: PlayerColor) {
self.active_color = active_color;
}
#[must_use]
pub fn get_castling_rights(&self, color: PlayerColor) -> CastlingRights {
match color {
PlayerColor::Black => self.castling_black,
PlayerColor::White => self.castling_white,
}
}
pub fn set_castling_rights(&mut self, color: PlayerColor, castling_rights: CastlingRights) {
match color {
PlayerColor::Black => self.castling_black = castling_rights,
PlayerColor::White => self.castling_white = castling_rights,
}
}
#[must_use]
pub fn get_en_passant(&self) -> Option<Field> {
self.en_passant
}
pub fn set_en_passant(&mut self, en_passant: Option<Field>) {
self.en_passant = en_passant;
}
#[must_use]
pub fn get_halfmove_clock(&self) -> u16 {
self.halfmove_clock
}
pub fn set_halfmove_clock(&mut self, halfmove_clock: u16) {
self.halfmove_clock = halfmove_clock;
}
#[must_use]
pub fn get_fullmove_counter(&self) -> u16 {
self.fullmove_counter
}
pub fn set_fullmove_counter(&mut self, fullmove_counter: u16) {
self.fullmove_counter = fullmove_counter;
}
pub fn get_possible_turns(&self, ruleset: &Ruleset) -> Vec<Turn> {
ruleset.get_possible_turns(self)
}
pub fn turn(&self, ruleset: &Ruleset, turn: &Turn) -> Result<Position, PositionError> {
let possible_moves = self.get_possible_turns(ruleset);
if !possible_moves.contains(turn) {
return Err(PositionError::IllegalTurnError(Lan::export(turn)));
}
Ok(ruleset.execute_turn(self, turn))
}
#[must_use]
pub fn game_over_check(&self, ruleset: &Ruleset) -> Option<GameResult> {
ruleset.game_over_check(self)
}
}
impl Default for Position {
fn default() -> Self {
Fen::import(DEFAULT_BOARD_SETUP).unwrap()
}
}