113 lines
2.8 KiB
Rust
113 lines
2.8 KiB
Rust
use crate::repr::{Board, CoordAxis, CoordPair, Piece};
|
|
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) -> AgentMove;
|
|
/// Returns the name of the [`Agent`]
|
|
fn name(&self) -> &'static str;
|
|
/// Returns the color the [`Agent`] is playing
|
|
fn color(&self) -> Piece;
|
|
}
|
|
|
|
/// An [`Agent`] which a user controls
|
|
pub struct ManualAgent {
|
|
color: Piece,
|
|
}
|
|
|
|
impl ManualAgent {
|
|
#[allow(dead_code)]
|
|
pub const fn new(color: Piece) -> Self {
|
|
Self { color }
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl Agent for ManualAgent {
|
|
fn next_move(&mut self, board: &Board) -> AgentMove {
|
|
let stdin = io::stdin();
|
|
let mut input = String::new();
|
|
println!("Your turn! ('Skip' to skip 'back' to go back a move)");
|
|
loop {
|
|
input.clear();
|
|
if stdin.lock().read_line(&mut input).is_err() {
|
|
return AgentMove::Skip;
|
|
};
|
|
|
|
if input.to_lowercase().trim() == "skip" {
|
|
// skips move
|
|
return AgentMove::Skip;
|
|
}
|
|
if input.to_lowercase().trim() == "back" {
|
|
// skips move
|
|
return AgentMove::Back;
|
|
}
|
|
|
|
let got = input
|
|
.split_whitespace()
|
|
.map(str::parse)
|
|
.map(Result::ok)
|
|
.collect::<Option<Vec<_>>>()
|
|
.and_then(|x| -> Option<[CoordAxis; 2]> { x.try_into().ok() })
|
|
.map(|x| (x[0], x[1]));
|
|
|
|
if let Some(got) = got {
|
|
if board.possible_moves(self.color).all(|x| x != got.into()) {
|
|
println!("Invalid move! Try again.");
|
|
continue;
|
|
}
|
|
return AgentMove::CoordPair(got.into());
|
|
}
|
|
}
|
|
}
|
|
|
|
fn name(&self) -> &'static str {
|
|
"Manual Agent"
|
|
}
|
|
|
|
fn color(&self) -> Piece {
|
|
self.color
|
|
}
|
|
}
|
|
|
|
/// An [`Agent`] that just makes a random move that is legal
|
|
pub struct RandomAgent {
|
|
color: Piece,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl Agent for RandomAgent {
|
|
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 {
|
|
"Random Agent"
|
|
}
|
|
|
|
fn color(&self) -> Piece {
|
|
self.color
|
|
}
|
|
}
|
|
|
|
impl RandomAgent {
|
|
#[allow(dead_code)]
|
|
pub const fn new(color: Piece) -> Self {
|
|
Self { color }
|
|
}
|
|
}
|