pub mod logic;
pub mod state;
use crate::error::{BoardError, InvalidPlay, ParseError};
use crate::pieces::{PlacedPiece, Side};
use crate::play::{Play, PlayRecord, PlayIterator};
use crate::rules::Ruleset;
use crate::tiles::Tile;
use std::cmp::PartialEq;
use std::collections::HashSet;
use crate::board::state::{BoardState, HugeBasicBoardState, LargeBasicBoardState, MediumBasicBoardState, SmallBasicBoardState};
use crate::game::logic::GameLogic;
use crate::game::state::GameState;
#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash)]
pub enum WinReason {
KingEscaped,
ExitFort,
KingCaptured,
AllCaptured,
Enclosed,
NoPlays,
Repetition
}
#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash)]
pub enum DrawReason {
Repetition,
NoPlays
}
#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash)]
pub enum GameOutcome {
Win(WinReason, Side),
Draw(DrawReason)
}
#[derive(Eq, PartialEq, Debug, Default, Clone)]
pub struct PlayEffects {
pub captures: HashSet<PlacedPiece>,
pub game_outcome: Option<GameOutcome>
}
#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash)]
pub enum GameStatus {
Ongoing,
Over(GameOutcome)
}
#[derive(Eq, PartialEq, Debug)]
pub enum PlayValidity {
Valid,
Invalid(InvalidPlay)
}
#[derive(Clone)]
pub struct Game<T: BoardState> {
pub logic: GameLogic,
pub state: GameState<T>,
pub play_history: Vec<PlayRecord>,
pub state_history: Vec<GameState<T>>
}
impl<T: BoardState> Game<T> {
pub fn new(rules: Ruleset, starting_board: &str) -> Result<Self, ParseError> {
let state: GameState<T> = GameState::new(starting_board, rules.starting_side)?;
let logic = GameLogic::new(rules, state.board.side_len());
Ok(Self { state, logic, play_history: vec![], state_history: vec![state] })
}
pub fn do_play(&mut self, play: Play) -> Result<GameStatus, InvalidPlay> {
let (state, play_record) = self.logic.do_play(play, self.state)?.into();
self.state_history.push(self.state);
self.state = state;
self.play_history.push(play_record);
Ok(self.state.status)
}
pub fn undo_last_play(&mut self) {
if let Some(state) = self.state_history.pop() {
self.state = state;
self.play_history.pop();
}
}
pub fn iter_plays(&self, tile: Tile) -> Result<PlayIterator<T>, BoardError> {
PlayIterator::new(&self.logic, &self.state, tile)
}
}
pub type SmallBasicGame = Game<SmallBasicBoardState>;
pub type MediumBasicGame = Game<MediumBasicBoardState>;
pub type LargeBasicGame = Game<LargeBasicBoardState>;
pub type HugeBasicGame = Game<HugeBasicBoardState>;
#[cfg(test)]
mod tests {
use crate::game::Game;
use crate::play::Play;
use crate::preset::{boards, rules};
use crate::tiles::Tile;
use std::collections::HashSet;
use crate::board::state::SmallBasicBoardState;
#[test]
fn test_iter_plays() {
let game: Game<SmallBasicBoardState> = Game::new(rules::BRANDUBH, boards::BRANDUBH).unwrap();
assert!(game.iter_plays(Tile::new(0, 0)).is_err());
assert!(game.iter_plays(Tile::new(1, 0)).is_err());
let outer_att_tile = Tile::new(0, 3);
let outer_att_iter = game.iter_plays(outer_att_tile);
assert!(outer_att_iter.is_ok());
assert_eq!(
outer_att_iter.unwrap().collect::<HashSet<Play>>(),
hashset!(
Play::from_tiles(outer_att_tile, Tile::new(0, 1)).unwrap(),
Play::from_tiles(outer_att_tile, Tile::new(0, 2)).unwrap(),
Play::from_tiles(outer_att_tile, Tile::new(0, 4)).unwrap(),
Play::from_tiles(outer_att_tile, Tile::new(0, 5)).unwrap()
)
);
let inner_att_tile = Tile::new(1, 3);
let inner_att_iter = game.iter_plays(inner_att_tile);
assert!(inner_att_iter.is_ok());
assert_eq!(
inner_att_iter.unwrap().collect::<HashSet<Play>>(),
hashset!(
Play::from_tiles(inner_att_tile, Tile::new(1, 0)).unwrap(),
Play::from_tiles(inner_att_tile, Tile::new(1, 1)).unwrap(),
Play::from_tiles(inner_att_tile, Tile::new(1, 2)).unwrap(),
Play::from_tiles(inner_att_tile, Tile::new(1, 4)).unwrap(),
Play::from_tiles(inner_att_tile, Tile::new(1, 5)).unwrap(),
Play::from_tiles(inner_att_tile, Tile::new(1, 6)).unwrap()
)
);
let outer_def_tile = Tile::new(2, 3);
let outer_def_iter = game.iter_plays(outer_def_tile);
assert!(outer_def_iter.is_ok());
assert_eq!(
outer_def_iter.unwrap().collect::<HashSet<Play>>(),
hashset!(
Play::from_tiles(outer_def_tile, Tile::new(2, 0)).unwrap(),
Play::from_tiles(outer_def_tile, Tile::new(2, 1)).unwrap(),
Play::from_tiles(outer_def_tile, Tile::new(2, 2)).unwrap(),
Play::from_tiles(outer_def_tile, Tile::new(2, 4)).unwrap(),
Play::from_tiles(outer_def_tile, Tile::new(2, 5)).unwrap(),
Play::from_tiles(outer_def_tile, Tile::new(2, 6)).unwrap()
)
);
let king_tile = Tile::new(3, 3);
let king_iter = game.iter_plays(king_tile);
assert!(king_iter.is_ok());
assert_eq!(king_iter.unwrap().collect::<HashSet<Play>>(), HashSet::new());
let game: Game<SmallBasicBoardState> = Game::new(
rules::BRANDUBH,
"1T5/7/7/1t3K1/7/7/7"
).unwrap();
let test_tile = Tile::new(3, 1);
let iter = game.iter_plays(test_tile);
assert!(iter.is_ok());
assert_eq!(
iter.unwrap().collect::<HashSet<Play>>(),
hashset!(
Play::from_tiles(test_tile, Tile::new(1, 1)).unwrap(),
Play::from_tiles(test_tile, Tile::new(2, 1)).unwrap(),
Play::from_tiles(test_tile, Tile::new(4, 1)).unwrap(),
Play::from_tiles(test_tile, Tile::new(5, 1)).unwrap(),
Play::from_tiles(test_tile, Tile::new(6, 1)).unwrap(),
Play::from_tiles(test_tile, Tile::new(3, 0)).unwrap(),
Play::from_tiles(test_tile, Tile::new(3, 2)).unwrap(),
Play::from_tiles(test_tile, Tile::new(3, 4)).unwrap()
)
)
}
#[test]
fn test_undo() {
let mut g: Game<SmallBasicBoardState> = Game::new(rules::BRANDUBH, boards::BRANDUBH).unwrap();
let state_0 = g.state.clone();
g.do_play(Play::from_tiles(Tile::new(0, 3), Tile::new(0, 2)).unwrap()).unwrap();
let state_1 = g.state.clone();
assert_ne!(state_0, state_1);
g.do_play(Play::from_tiles(Tile::new(2, 3), Tile::new(2, 1)).unwrap()).unwrap();
let state_2 = g.state.clone();
assert_ne!(state_0, state_2);
g.do_play(Play::from_tiles(Tile::new(1, 3), Tile::new(1, 1)).unwrap()).unwrap();
let state_3 = g.state.clone();
assert_ne!(state_0, state_3);
g.undo_last_play();
assert_eq!(g.state, state_2);
g.undo_last_play();
assert_eq!(g.state, state_1);
g.undo_last_play();
assert_eq!(g.state, state_0);
g.undo_last_play();
assert_eq!(g.state, state_0);
}
}