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,9 +70,10 @@ 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 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(_) => {
@ -89,7 +90,12 @@ impl GameInner {
);
}
}
} else {
}
AgentMove::Back => {
self.back();
}
AgentMove::Skip => {
self.move_log.push((None, player_color));
if self.do_print {
println!("Player {} did not make a move!", player_i);
}
@ -108,6 +114,7 @@ impl GameInner {
}
}
}
}
// TODO! make this loop good
pub fn game_loop(&mut self) {
let mut current_player: usize = 0;
@ -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);
}
}