add back functionality

This commit is contained in:
Simon Gardling 2025-04-10 14:18:50 -04:00
parent 80db0a872d
commit fc8c2b8178
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
3 changed files with 83 additions and 39 deletions

View File

@ -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<CoordPair>;
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<CoordPair> {
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<CoordPair> {
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 {

View File

@ -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<CoordPair> {
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 {

View File

@ -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);
}
}