use std::fmt::{Display, Write};
use crate::game_outcome::GameOutcome;
#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub enum BoardSpace {
#[default]
Empty,
X,
O
}
impl BoardSpace {
pub fn get_char(&self) -> char
{
match self {
Self::Empty => ' ',
Self::X => 'X',
Self::O => 'O'
}
}
}
impl Display for BoardSpace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char(self.get_char())?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BoardSpaceLocation {
TopLeft,
TopMiddle,
TopRight,
MiddleLeft,
MiddleMiddle,
MiddleRight,
BottomLeft,
BottomMiddle,
BottomRight
}
impl BoardSpaceLocation {
pub const fn as_coordinates(&self) -> (u8, u8)
{
match self {
Self::TopLeft => (0,0),
Self::TopMiddle => (1,0),
Self::TopRight => (2,0),
Self::MiddleLeft => (0,1),
Self::MiddleMiddle => (1,1),
Self::MiddleRight => (2,1),
Self::BottomLeft => (0,2),
Self::BottomMiddle => (1,2),
Self::BottomRight => (2,2)
}
}
pub fn from_coordinates((x, y): (u8, u8)) -> Self
{
for board_space_location in Self::all() {
if board_space_location.as_coordinates() == (x,y) {
return board_space_location;
}
}
panic!("Coordinates ({},{}) don't correspond to any BoardSpaceLocation", x, y);
}
pub fn all() -> impl Iterator<Item = Self>
{
const VARIANTS: [BoardSpaceLocation; 9] = [
BoardSpaceLocation::TopLeft,
BoardSpaceLocation::TopMiddle,
BoardSpaceLocation::TopRight,
BoardSpaceLocation::MiddleLeft,
BoardSpaceLocation::MiddleMiddle,
BoardSpaceLocation::MiddleRight,
BoardSpaceLocation::BottomLeft,
BoardSpaceLocation::BottomMiddle,
BoardSpaceLocation::BottomRight
];
VARIANTS.into_iter()
}
}
#[derive(Default, Clone)]
pub struct GameBoard {
board_state: [[BoardSpace; 3]; 3]
}
impl GameBoard {
pub fn new() -> Self
{
GameBoard::default()
}
pub fn space(&self, space_location: BoardSpaceLocation) -> &BoardSpace
{
self.space_by_coordinates(space_location.as_coordinates())
}
pub fn space_mut(&mut self, space_location: BoardSpaceLocation) -> &mut BoardSpace
{
self.space_by_coordinates_mut(space_location.as_coordinates())
}
pub fn all_spaces(&self) -> impl Iterator<Item = (BoardSpaceLocation, &BoardSpace)>
{
BoardSpaceLocation::all().map(|space_location|{
(space_location, self.space(space_location))
})
}
pub fn space_by_coordinates(&self, (x,y): (u8,u8)) -> &BoardSpace
{
let board_state_column =
match self.board_state.get(x as usize) {
Some(col) => col,
None => {
panic!("Invalid coordinates ({},{}); maximum is (2,2)", x, y);
}
};
match board_state_column.get(y as usize) {
Some(space) => space,
None => {
panic!("Invalid coordinates ({},{}); maximum is (2,2)", x, y);
}
}
}
pub fn space_by_coordinates_mut(&mut self, (x,y): (u8,u8)) -> &mut BoardSpace
{
let board_state_column =
match self.board_state.get_mut(x as usize) {
Some(col) => col,
None => {
panic!("Invalid coordinates ({},{}); maximum is (2,2)", x, y);
}
};
match board_state_column.get_mut(y as usize) {
Some(space) => space,
None => {
panic!("Invalid coordinates ({},{}); maximum is (2,2)", x, y);
}
}
}
pub fn as_string(&self) -> String
{
format!("{}", self)
}
pub fn game_outcome(&self) -> GameOutcome
{
GameOutcome::analyze_game(self)
}
}
impl Display for GameBoard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
const HORIZ_LINE: &str = "-----------\n";
f.write_fmt(format_args!("\n {} | {} | {}\n",
self.space(BoardSpaceLocation::TopLeft),
self.space(BoardSpaceLocation::TopMiddle),
self.space(BoardSpaceLocation::TopRight)
))?;
f.write_str(HORIZ_LINE)?;
f.write_fmt(format_args!(" {} | {} | {}\n",
self.space(BoardSpaceLocation::MiddleLeft),
self.space(BoardSpaceLocation::MiddleMiddle),
self.space(BoardSpaceLocation::MiddleRight)
))?;
f.write_str(HORIZ_LINE)?;
f.write_fmt(format_args!(" {} | {} | {}",
self.space(BoardSpaceLocation::BottomLeft),
self.space(BoardSpaceLocation::BottomMiddle),
self.space(BoardSpaceLocation::BottomRight)
))?;
Ok(())
}
}