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;
use std::io::prelude::*; use std::io::prelude::*;
#[derive(Debug, Clone, Copy)]
pub enum AgentMove {
CoordPair(CoordPair),
Back,
Skip,
}
#[allow(dead_code)] #[allow(dead_code)]
// implements `Send` so we can use it in a multithreaded `EloArena` // implements `Send` so we can use it in a multithreaded `EloArena`
pub trait Agent: Send { pub trait Agent: Send {
/// Returns the move of an [`Agent`] /// 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`] /// Returns the name of the [`Agent`]
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
/// Returns the color the [`Agent`] is playing /// Returns the color the [`Agent`] is playing
@ -28,16 +35,23 @@ impl ManualAgent {
#[allow(dead_code)] #[allow(dead_code)]
impl Agent for ManualAgent { 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 stdin = io::stdin();
let mut input = String::new(); let mut input = String::new();
println!("Your turn! ('Skip' to skip)"); println!("Your turn! ('Skip' to skip 'back' to go back a move)");
loop { loop {
input.clear(); 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" { if input.to_lowercase().trim() == "skip" {
// skips move // skips move
return None; return AgentMove::Skip;
}
if input.to_lowercase().trim() == "back" {
// skips move
return AgentMove::Back;
} }
let got = input let got = input
@ -53,7 +67,7 @@ impl Agent for ManualAgent {
println!("Invalid move! Try again."); println!("Invalid move! Try again.");
continue; continue;
} }
return Some(got.into()); return AgentMove::CoordPair(got.into());
} }
} }
} }
@ -74,8 +88,11 @@ pub struct RandomAgent {
#[allow(dead_code)] #[allow(dead_code)]
impl Agent for RandomAgent { impl Agent for RandomAgent {
fn next_move(&mut self, board: &Board) -> Option<CoordPair> { fn next_move(&mut self, board: &Board) -> AgentMove {
board.possible_moves(self.color).choose(&mut rand::rng()) match board.possible_moves(self.color).choose(&mut rand::rng()) {
Some(x) => AgentMove::CoordPair(x),
None => AgentMove::Skip,
}
} }
fn name(&self) -> &'static str { fn name(&self) -> &'static str {

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
agent::Agent, agent::{Agent, AgentMove},
logic::{FutureMoveConfig, FutureMoves}, logic::{FutureMoveConfig, FutureMoves},
repr::{Board, CoordPair, Piece}, repr::{Board, Piece},
}; };
pub struct ComplexAgent { pub struct ComplexAgent {
@ -20,13 +20,18 @@ impl ComplexAgent {
} }
impl Agent for 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.update_from_board(board);
self.future_moves.generate(); self.future_moves.generate();
self.future_moves match self
.future_moves
.best_move() .best_move()
.expect("FutureMoves has no move?") .expect("FutureMoves has no move?")
{
Some(x) => AgentMove::CoordPair(x),
None => AgentMove::Skip,
}
} }
fn name(&self) -> &'static str { fn name(&self) -> &'static str {

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
agent::Agent, agent::{Agent, AgentMove},
repr::{Board, CoordPair, Piece, Winner}, repr::{Board, CoordPair, Piece, Winner},
}; };
@ -70,9 +70,10 @@ impl GameInner {
// when it comes to human input // when it comes to human input
loop { loop {
let player_move = self.players[player_i].next_move(&self.board); 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 player_move {
AgentMove::CoordPair(coord) => {
self.move_log.push((Some(coord), player_color));
match self.board.place(coord, player_color) { match self.board.place(coord, player_color) {
// Check if its a valid move // Check if its a valid move
Ok(_) => { Ok(_) => {
@ -89,7 +90,12 @@ impl GameInner {
); );
} }
} }
} else { }
AgentMove::Back => {
self.back();
}
AgentMove::Skip => {
self.move_log.push((None, player_color));
if self.do_print { if self.do_print {
println!("Player {} did not make a move!", player_i); println!("Player {} did not make a move!", player_i);
} }
@ -108,6 +114,7 @@ impl GameInner {
} }
} }
} }
}
// TODO! make this loop good // TODO! make this loop good
pub fn game_loop(&mut self) { pub fn game_loop(&mut self) {
let mut current_player: usize = 0; let mut current_player: usize = 0;
@ -146,4 +153,19 @@ impl GameInner {
self.game_loop(); self.game_loop();
self.board.game_winner() 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);
}
} }