diff --git a/src/agent.rs b/src/agent.rs index 003648c..7ecfbf6 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -3,11 +3,18 @@ use rand::prelude::*; use std::io; use std::io::prelude::*; +#[derive(Debug, Clone, Copy)] +pub enum AgentMove { + CoordPair(CoordPair), + Back, + Skip, +} + #[allow(dead_code)] // implements `Send` so we can use it in a multithreaded `EloArena` pub trait Agent: Send { /// Returns the move of an [`Agent`] - fn next_move(&mut self, board: &Board) -> Option; + fn next_move(&mut self, board: &Board) -> AgentMove; /// Returns the name of the [`Agent`] fn name(&self) -> &'static str; /// Returns the color the [`Agent`] is playing @@ -28,16 +35,23 @@ impl ManualAgent { #[allow(dead_code)] impl Agent for ManualAgent { - fn next_move(&mut self, board: &Board) -> Option { + fn next_move(&mut self, board: &Board) -> AgentMove { let stdin = io::stdin(); let mut input = String::new(); - println!("Your turn! ('Skip' to skip)"); + println!("Your turn! ('Skip' to skip 'back' to go back a move)"); loop { input.clear(); - stdin.lock().read_line(&mut input).ok()?; + if stdin.lock().read_line(&mut input).is_err() { + return AgentMove::Skip; + }; + if input.to_lowercase().trim() == "skip" { // skips move - return None; + return AgentMove::Skip; + } + if input.to_lowercase().trim() == "back" { + // skips move + return AgentMove::Back; } let got = input @@ -53,7 +67,7 @@ impl Agent for ManualAgent { println!("Invalid move! Try again."); continue; } - return Some(got.into()); + return AgentMove::CoordPair(got.into()); } } } @@ -74,8 +88,11 @@ pub struct RandomAgent { #[allow(dead_code)] impl Agent for RandomAgent { - fn next_move(&mut self, board: &Board) -> Option { - board.possible_moves(self.color).choose(&mut rand::rng()) + fn next_move(&mut self, board: &Board) -> AgentMove { + match board.possible_moves(self.color).choose(&mut rand::rng()) { + Some(x) => AgentMove::CoordPair(x), + None => AgentMove::Skip, + } } fn name(&self) -> &'static str { diff --git a/src/complexagent.rs b/src/complexagent.rs index 0826cf4..0ce89af 100644 --- a/src/complexagent.rs +++ b/src/complexagent.rs @@ -1,7 +1,7 @@ use crate::{ - agent::Agent, + agent::{Agent, AgentMove}, logic::{FutureMoveConfig, FutureMoves}, - repr::{Board, CoordPair, Piece}, + repr::{Board, Piece}, }; pub struct ComplexAgent { @@ -20,13 +20,18 @@ impl ComplexAgent { } impl Agent for ComplexAgent { - fn next_move(&mut self, board: &Board) -> Option { + fn next_move(&mut self, board: &Board) -> AgentMove { self.future_moves.update_from_board(board); self.future_moves.generate(); - self.future_moves + match self + .future_moves .best_move() .expect("FutureMoves has no move?") + { + Some(x) => AgentMove::CoordPair(x), + None => AgentMove::Skip, + } } fn name(&self) -> &'static str { diff --git a/src/game_inner.rs b/src/game_inner.rs index bbf6671..ac688bb 100644 --- a/src/game_inner.rs +++ b/src/game_inner.rs @@ -1,5 +1,5 @@ use crate::{ - agent::Agent, + agent::{Agent, AgentMove}, repr::{Board, CoordPair, Piece, Winner}, }; @@ -70,41 +70,48 @@ impl GameInner { // when it comes to human input loop { let player_move = self.players[player_i].next_move(&self.board); - self.move_log.push((player_move, player_color)); - if let Some(coord) = player_move { - match self.board.place(coord, player_color) { - // Check if its a valid move - Ok(_) => { - if self.do_print { - println!("Player {} placed at {}", player_i, coord); + match player_move { + AgentMove::CoordPair(coord) => { + self.move_log.push((Some(coord), player_color)); + match self.board.place(coord, player_color) { + // Check if its a valid move + Ok(_) => { + if self.do_print { + println!("Player {} placed at {}", player_i, coord); + } + break; } - break; - } - Err(err) => { - panic!( - "Invalid move by Player {}: {}. Move: {} State: {:?}", - player_i, err, coord, self - ); + Err(err) => { + panic!( + "Invalid move by Player {}: {}. Move: {} State: {:?}", + player_i, err, coord, self + ); + } } } - } else { - if self.do_print { - println!("Player {} did not make a move!", player_i); + AgentMove::Back => { + self.back(); } - - // players are able to skip a move if they have no valid moves to make - if self.board.possible_moves(player_color).next().is_some() { + AgentMove::Skip => { + self.move_log.push((None, player_color)); if self.do_print { - println!( - "Player {} has possible moves, but skipped (invalid)", - player_i - ); + println!("Player {} did not make a move!", player_i); } - continue; + + // players are able to skip a move if they have no valid moves to make + if self.board.possible_moves(player_color).next().is_some() { + if self.do_print { + println!( + "Player {} has possible moves, but skipped (invalid)", + player_i + ); + } + continue; + } + return; // No valid move available } - return; // No valid move available } } } @@ -146,4 +153,19 @@ impl GameInner { self.game_loop(); self.board.game_winner() } + + pub fn back(&mut self) { + if self.move_log.is_empty() { + println!("This is the initial board state!"); + return; + } + self.move_log.pop(); + self.board = self.first_board; + for &(l, p) in &self.move_log { + if let Some(loc) = l { + self.board.place(loc, p).expect("unable to go backwards"); + } + } + println!("{}", self); + } }