use crate::othello::{
Bitboard, Board, Position, Stone,
constants::{FILES, RANKS},
};
use std::fmt;
#[derive(Clone)]
pub struct BoardDisplay<'a> {
board: &'a Board,
display: Format,
stone: Option<Stone>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Format {
Compact,
Standard,
}
impl<'a> BoardDisplay<'a> {
pub(crate) fn new(board: &'a Board) -> Self {
Self {
board,
display: Format::Standard,
stone: None,
}
}
#[must_use]
pub fn with_stone(&self, stone: Stone) -> Self {
Self {
board: self.board,
display: self.display,
stone: Some(stone),
}
}
#[must_use]
pub fn with_format(&self, display: Format) -> Self {
Self {
board: self.board,
display,
stone: self.stone,
}
}
}
impl fmt::Display for BoardDisplay<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
display(f, self.board, self.stone, self.display)
}
}
fn display(
f: &mut fmt::Formatter,
board: &Board,
stone: Option<Stone>,
display: Format,
) -> fmt::Result {
let legal_moves = stone.map_or(Bitboard::EMPTY, |stone| board.moves_for(stone));
let char_at = |rank: usize, file: usize| {
let pos = RANKS[rank] & FILES[file];
let pos = Position::new_unchecked(pos);
board
.stone_at(pos)
.map(|stone| match stone {
Stone::White => "W",
Stone::Black => "B",
})
.or({
if legal_moves & pos > 0 {
Some("*")
} else {
None
}
})
.unwrap_or(match display {
Format::Compact => ".",
Format::Standard => " ",
})
};
match display {
Format::Compact => {
writeln!(f, " ABCDEFGH")?;
writeln!(f, " +--------+")?;
for rank in 0..8 {
write!(f, "{} |", rank + 1)?;
for file in 0..8 {
write!(f, "{}", char_at(rank, file))?;
}
writeln!(f, "|")?;
}
writeln!(f, " +--------+")
}
Format::Standard => {
let top_row = " A B C D E F G H";
let horizontal = format!(" +{}", "---+".repeat(8));
writeln!(f, "{top_row}")?;
for rank in 0..8 {
writeln!(f, "{horizontal}")?;
write!(f, "{} |", rank + 1)?;
for file in 0..8 {
write!(f, " {} |", char_at(rank, file))?;
}
writeln!(f)?;
}
writeln!(f, "{horizontal}")
}
}
}