use crate::othello::{Bitboard, Board, BoardDisplay, BoardError, Position, Stone};
use std::{cmp::Ordering, error, fmt};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum GameError {
IllegalMove,
InvalidBoard(BoardError),
}
impl From<BoardError> for GameError {
fn from(e: BoardError) -> Self {
GameError::InvalidBoard(e)
}
}
impl fmt::Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IllegalMove => write!(f, "illegal move"),
Self::InvalidBoard(_) => write!(f, "invalid board"),
}
}
}
impl error::Error for GameError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::IllegalMove => None,
Self::InvalidBoard(e) => Some(e),
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Game {
board: Board,
next_player: Stone,
passed_last_turn: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum Status {
Win(Stone),
Draw,
Progressing,
}
impl Game {
#[must_use]
pub fn new() -> Self {
Game::from_state(Board::standard(), Stone::Black, false).unwrap()
}
pub fn from_state(
board: Board,
next_player: Stone,
passed_last_turn: bool,
) -> Result<Self, GameError> {
if board.is_valid() {
Ok(Self {
board,
next_player,
passed_last_turn,
})
} else {
Err(BoardError::OverlappingPieces.into())
}
}
#[must_use]
pub fn current_turn(&self) -> Stone {
self.next_player
}
pub fn pass_turn(&mut self) {
self.next_player = self.next_player.flip();
self.passed_last_turn = true;
}
#[must_use]
pub fn status(&self) -> Status {
let finished = self.passed_last_turn && self.board.moves_for(self.next_player).is_empty();
if finished {
let black_stones = self.board.bits_for(Stone::Black).count_set();
let white_stones = self.board.bits_for(Stone::White).count_set();
match black_stones.cmp(&white_stones) {
Ordering::Greater => Status::Win(Stone::Black),
Ordering::Less => Status::Win(Stone::White),
Ordering::Equal => Status::Draw,
}
} else {
Status::Progressing
}
}
pub fn play(&mut self, pos: Position) -> Result<(), GameError> {
if self.is_legal_move(pos) {
self.board.play(self.next_player, pos);
self.next_player = self.next_player.flip();
self.passed_last_turn = false;
Ok(())
} else {
Err(GameError::IllegalMove)
}
}
#[must_use]
pub fn board(&self) -> Board {
self.board.clone()
}
#[must_use]
pub fn passed_last_turn(&self) -> bool {
self.passed_last_turn
}
#[must_use]
pub fn is_legal_move(&self, pos: Position) -> bool {
self.board.is_legal_move(self.next_player, pos)
}
#[must_use]
pub fn moves(&self) -> Bitboard {
self.board.moves_for(self.next_player)
}
#[must_use]
pub fn bits_for(&self, stone: Stone) -> Bitboard {
self.board.bits_for(stone)
}
#[must_use]
pub fn empty_squares(&self) -> Bitboard {
self.board.empty_squares()
}
#[must_use]
pub fn stone_at(&self, pos: Position) -> Option<Stone> {
self.board.stone_at(pos)
}
#[must_use]
pub fn display(&'_ self) -> BoardDisplay<'_> {
self.board.display()
}
}
impl Default for Game {
fn default() -> Self {
Self::new()
}
}