use std::fmt::Display;
use crate::{
bitboard::Bitboard,
board::Board,
color::Color,
outcome::{DrawReason, Outcome},
ply::Ply,
position::Position,
uci::Uci,
};
#[derive(Debug, Default, Clone)]
pub struct Game {
position: Position,
turn: Ply,
outcome: Option<Outcome>,
}
impl Game {
pub fn new() -> Self {
Self {
position: Position::new(),
turn: Ply::new(),
outcome: None,
}
}
pub fn mve(self, uci: &Uci) -> Option<Self> {
self.position
.mve(uci.orig, uci.dest, uci.promotion)
.map(|position| {
let outcome = eval(&position);
Self {
position,
turn: self.turn.incr(),
outcome,
}
})
}
pub fn is_white_turn(&self) -> bool {
self.position.color() == Color::White
}
pub fn outcome(&self) -> Option<Outcome> {
self.outcome
}
pub fn position(&self) -> &Position {
&self.position
}
pub fn ply(&self) -> Ply {
self.turn
}
}
fn eval(position: &Position) -> Option<Outcome> {
let has_moves = position.has_moves();
if is_insufficient_material(position.board()) {
Some(Outcome::Draw(DrawReason::InsufficientMaterial))
} else if position.history().is_threefold_repetition() {
Some(Outcome::Draw(DrawReason::ThreeFoldRepetition))
} else if position.history().half_moves() >= 50 {
Some(Outcome::Draw(DrawReason::FiftyMoveRule))
} else if !has_moves && position.is_check() {
let winner = position.color().opponent();
Some(Outcome::Win(winner))
} else if !has_moves {
Some(Outcome::Draw(DrawReason::Stalemate))
} else {
None
}
}
fn is_insufficient_material(board: &Board) -> bool {
if board.pawns().is_non_empty() || board.rooks().is_non_empty() || board.queens().is_non_empty()
{
return false;
}
let minors = (board.knights() | board.bishops()).count();
if minors <= 1 {
return true;
}
if board.bishops().is_empty() && board.knights().count() == 2 {
return true;
}
if board.knights().is_empty() {
let bishops = board.bishops();
return (bishops & Bitboard::LIGHT).is_empty() || (bishops & Bitboard::DARK).is_empty();
}
false
}
impl Display for Game {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.position.board())?;
match self.outcome {
Some(outcome) => write!(f, "{}", outcome),
None => write!(
f,
"{:?} to move (move {})",
self.turn.turn(),
self.turn.full_move_number()
),
}
}
}