#[cfg(feature = "analysis")]
use crate::analysis::{evaluate_board, find_best_actions};
use crate::error;
use crate::prelude::{Action, ActionContainer, ActionsFwd, Board, Color, SurroundedStatus};
#[derive(Debug, Clone, Copy)]
pub struct GameRule {
is_remove_accepted: bool,
first_player: Color,
suicide_atk_judge: Judge,
initial_board: Board,
}
impl GameRule {
pub fn new(is_remove_accepted: bool) -> Self {
let first_player = Color::Red;
let suicide_atk_judge = Judge::NextWins;
let initial_board = Board::new();
Self {
is_remove_accepted,
first_player,
suicide_atk_judge,
initial_board,
}
}
pub fn is_remove_accepted(&self) -> &bool {
&self.is_remove_accepted
}
pub fn first_player(&self) -> &Color {
&self.first_player
}
pub fn suicide_atk_judge(&self) -> &Judge {
&self.suicide_atk_judge
}
pub fn initial_board(&self) -> &Board {
&self.initial_board
}
pub fn with_is_remove_accepted(self, is_remove_accepted: bool) -> Self {
Self {
is_remove_accepted,
..self
}
}
pub fn with_first_player(self, first_player: Color) -> Self {
Self {
first_player,
..self
}
}
pub fn with_suicide_atk_judge(self, judge: Judge) -> Self {
Self {
suicide_atk_judge: judge,
..self
}
}
pub fn with_initial_board(self, initial_board: Board) -> Result<Self, error::Error> {
if !matches!(initial_board.surrounded_status(), SurroundedStatus::None) {
return Err(error::GameRuleCreateErrorKind::InitialBoardError.into());
}
let rule = Self {
initial_board,
..self
};
Ok(rule)
}
}
impl Default for GameRule {
fn default() -> Self {
Self::new(true)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Judge {
LastWins,
NextWins,
Draw,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GameStatus {
Ongoing,
Win(Color),
Draw,
}
#[derive(Debug, Clone, Copy)]
pub struct Game {
board: Board,
player: Color,
status: GameStatus,
rule: GameRule,
}
impl Game {
pub fn new(is_remove_accepted: bool) -> Game {
let rule = GameRule {
is_remove_accepted,
..Default::default()
};
Self::new_with_rule(rule)
}
pub fn new_with_rule(rule: GameRule) -> Game {
let board = rule.initial_board;
let player = rule.first_player;
let status = GameStatus::Ongoing;
Game {
board,
player,
status,
rule,
}
}
pub fn reset(&mut self) {
*self = Self::new_with_rule(self.rule)
}
pub fn rule(&self) -> &GameRule {
&self.rule
}
pub fn board(&self) -> &Board {
&self.board
}
pub fn next_player(&self) -> &Color {
&self.player
}
pub fn status(&self) -> &GameStatus {
&self.status
}
pub fn is_ongoing(&self) -> bool {
matches!(self.status, GameStatus::Ongoing)
}
pub fn winner(&self) -> Option<Color> {
use GameStatus::*;
match self.status {
Ongoing | Draw => None,
Win(player) => Some(player),
}
}
pub fn check_action(&self, action: Action) -> Result<(), error::Error> {
use error::PlayingErrorKind::*;
if self.player != *action.player() {
return Err(PlayerMismatch.into());
}
if !self.rule.is_remove_accepted && matches!(action, Action::Remove(_, _)) {
return Err(ProhibitedRemove(action).into());
}
self.board().check_action(action)?;
Ok(())
}
pub fn legal_actions(&self) -> ActionsFwd {
self.board
.legal_actions(self.player, true, true, self.rule.is_remove_accepted)
}
pub fn perform(&mut self, action: Action) -> Result<(), error::Error> {
if !self.is_ongoing() {
return Err(error::PlayingErrorKind::GameFinished(self.status).into());
}
self.check_action(action)?;
self.board.perform_unchecked(action);
use GameStatus::*;
use SurroundedStatus::*;
match self.board.surrounded_status() {
Both => match self.rule.suicide_atk_judge {
Judge::LastWins => self.status = Win(self.player),
Judge::NextWins => self.status = Win(!self.player),
Judge::Draw => self.status = Draw,
},
OneSide(player) => self.status = Win(!player),
None => self.player = !self.player,
}
Ok(())
}
pub fn display(&self) -> GameDisplay {
GameDisplay::new(self)
}
}
impl std::fmt::Display for Game {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display())
}
}
#[derive(Debug, Clone)]
pub enum GameDisplayFormat {
Standard,
}
impl Default for GameDisplayFormat {
fn default() -> Self {
Self::Standard
}
}
impl GameDisplayFormat {
fn typeset(&self, game: &Game) -> String {
use GameDisplayFormat::*;
match self {
Standard => Self::typeset_standard(game),
}
}
fn typeset_standard(game: &Game) -> String {
let next_player_string = if game.is_ongoing() {
format!("{:?}", game.next_player())
} else {
String::from("None")
};
format!("{}\nNext Player: {next_player_string}", game.board())
}
}
#[derive(Debug, Clone)]
pub struct GameDisplay<'a> {
game: &'a Game,
format: GameDisplayFormat,
}
impl<'a> GameDisplay<'a> {
fn new(game: &'a Game) -> Self {
Self {
game,
format: Default::default(),
}
}
pub fn with_format(self, format: GameDisplayFormat) -> Self {
Self { format, ..self }
}
}
impl<'a> std::fmt::Display for GameDisplay<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.format.typeset(self.game))
}
}
pub trait Agent {
fn play(&mut self, game: &mut Game);
}
#[derive(Default)]
pub struct RandomAgent {
n: usize,
}
impl std::fmt::Debug for RandomAgent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RandomAgent")
}
}
impl std::fmt::Display for RandomAgent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<Self as std::fmt::Debug>::fmt(self, f)
}
}
impl RandomAgent {
pub fn new() -> Self {
Self::default()
}
fn update_parameter(&mut self) {
self.n = (33 * self.n + 31) % 65536
}
}
impl Agent for RandomAgent {
fn play(&mut self, game: &mut Game) {
self.update_parameter();
let actions = game.legal_actions();
let action = actions[self.n % actions.len()];
game.perform(action).expect("illegal situation");
}
}
#[cfg(feature = "analysis")]
pub struct AnalystAgent {
depth: usize,
n: usize,
declare_about_to_end: bool,
}
#[cfg(feature = "analysis")]
impl std::fmt::Debug for AnalystAgent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AnalystAgent")
.field("depth", &self.depth)
.field("declare_about_to_end", &self.declare_about_to_end)
.finish()
}
}
#[cfg(feature = "analysis")]
impl std::fmt::Display for AnalystAgent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AnalystAgent")
}
}
#[cfg(feature = "analysis")]
impl AnalystAgent {
pub fn new(depth: usize, declare_about_to_end: bool) -> Self {
Self {
depth,
n: 0,
declare_about_to_end,
}
}
fn update_parameter(&mut self) {
self.n = (33 * self.n + 31) % 65536
}
}
#[cfg(feature = "analysis")]
impl Agent for AnalystAgent {
fn play(&mut self, game: &mut Game) {
self.update_parameter();
let board = *game.board();
let player = *game.next_player();
let rule = *game.rule();
let candidates = find_best_actions(board, player, self.depth, rule).unwrap();
let action = candidates[self.n % candidates.len()];
if self.declare_about_to_end {
if let Some(val) = evaluate_board(board, player, self.depth, rule)
.unwrap()
.single()
{
println!("!!! This game is about to end: value={val}");
}
}
game.perform(action).unwrap();
}
}
#[derive(Default)]
pub struct ConsoleAgent;
impl ConsoleAgent {
pub fn new() -> Self {
Self {}
}
}
impl std::fmt::Debug for ConsoleAgent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ConsoleAgent")
}
}
impl std::fmt::Display for ConsoleAgent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<Self as std::fmt::Debug>::fmt(self, f)
}
}
impl Agent for ConsoleAgent {
fn play(&mut self, game: &mut Game) {
let legal_actions = game.legal_actions();
let mut buffer = String::new();
let action: Action;
loop {
buffer.clear();
println!("Input an action in SSN:");
std::io::stdin()
.read_line(&mut buffer)
.expect("read line error");
let ssn = buffer.trim();
let Ok(action_tmp) = Action::try_from_ssn(ssn, game.board()) else {
println!("Invalid Input. Try Again.");
continue;
};
println!("---> Infered Action: {action_tmp:?}");
if !legal_actions.contains(action_tmp) {
println!("Illegal Action. Try Again.");
continue;
}
action = action_tmp;
break;
} game.perform(action).expect("failed to perform");
}
}
#[derive(Debug, Clone)]
pub struct Arena<AR, AG>
where
AR: Agent,
AG: Agent,
{
agent_red: AR,
agent_green: AG,
game: Game,
}
impl<AR, AG> std::fmt::Display for Arena<AR, AG>
where
AR: Agent + std::fmt::Display,
AG: Agent + std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = format!(
"Red Agent : {}\nGreen Agent: {}\n{}",
self.agent_red, self.agent_green, self.game
);
write!(f, "{s}")
}
}
impl<AR, AG> Arena<AR, AG>
where
AR: Agent,
AG: Agent,
{
pub fn new(agent_red: AR, agent_green: AG, game: Game) -> Self {
Self {
agent_red,
agent_green,
game,
}
}
pub fn auto_play(&mut self, verbose: bool) {
let mut num_turns = 0_u128;
loop {
num_turns += 1;
if verbose {
println!("{}", self.game);
}
match self.game.next_player() {
Color::Red => self.agent_red.play(&mut self.game),
Color::Green => self.agent_green.play(&mut self.game),
}
if !self.game.is_ongoing() {
break;
}
}
println!("~~~~~~ Game Finished! ~~~~~~");
println!("Total {num_turns} turns");
println!("{}", self.game);
match self.game.winner() {
Some(player) => println!("---> {player} win!"),
None => println!("---> Draw!"),
}
}
}