add back functionality
This commit is contained in:
parent
80db0a872d
commit
fc8c2b8178
33
src/agent.rs
33
src/agent.rs
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user