118 lines
2.6 KiB
Rust
118 lines
2.6 KiB
Rust
use anyhow::Result;
|
|
use rand::{seq::SliceRandom, thread_rng};
|
|
|
|
use super::{Agent, Chase, MaximizerAgent, MinimizerAgent, Random};
|
|
use crate::{
|
|
board::{Board, PlayerAction},
|
|
util::{Player, Symb},
|
|
};
|
|
|
|
/// A simple "operator diffusion" MINIMIZER agent.
|
|
///
|
|
/// Tries to keep operators as far apart as possible, denying large numbers.
|
|
/// Places numbers using the same algorithm as chase.
|
|
pub struct Diffuse {
|
|
player: Player,
|
|
}
|
|
|
|
impl Diffuse {
|
|
pub fn new(player: Player) -> Self {
|
|
Self { player }
|
|
}
|
|
|
|
/// Place a symbol on the board.
|
|
/// Assumes `symb` is not already on the board
|
|
fn step_symb(&self, board: &Board, symb: Symb) -> PlayerAction {
|
|
if board.contains(symb) {
|
|
panic!("Called `step_symb` with a symbol that's already on the board!")
|
|
}
|
|
|
|
// Fill distance array with largest possible value
|
|
let mut dist = [board.size() + 1; 11];
|
|
|
|
// Set up initial distances
|
|
dist[0] = 1;
|
|
*dist.last_mut().unwrap() = 1;
|
|
for (i, o) in board.get_board().iter().enumerate() {
|
|
if let Some(s) = o {
|
|
if s.is_op() {
|
|
dist[i] = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut did_something = true;
|
|
while did_something {
|
|
did_something = false;
|
|
for i in 1..(dist.len() - 1) {
|
|
let l = dist[i - 1];
|
|
let r = dist[i + 1];
|
|
|
|
let new = (l + 1).min(r + 1);
|
|
if new < dist[i] {
|
|
did_something = true;
|
|
dist[i] = new;
|
|
}
|
|
}
|
|
}
|
|
let mut max_dist = *dist.iter().max().unwrap();
|
|
|
|
loop {
|
|
for (pos, d) in dist.iter().enumerate() {
|
|
if *d >= max_dist {
|
|
let action = PlayerAction { symb, pos };
|
|
if board.can_play(&action) {
|
|
return action;
|
|
};
|
|
}
|
|
}
|
|
|
|
if max_dist == 0 {
|
|
return Random::new(self.player).step_max(board).unwrap();
|
|
}
|
|
|
|
max_dist -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Agent for Diffuse {
|
|
fn name(&self) -> &'static str {
|
|
"Diffuse"
|
|
}
|
|
|
|
fn player(&self) -> Player {
|
|
self.player
|
|
}
|
|
}
|
|
|
|
impl MinimizerAgent for Diffuse {
|
|
fn step_min(&mut self, board: &Board) -> Result<PlayerAction> {
|
|
let mut x = [Symb::Minus, Symb::Times, Symb::Plus, Symb::Div];
|
|
x.shuffle(&mut thread_rng());
|
|
let symb = x.iter().find(|x| !board.contains(**x));
|
|
|
|
if let Some(symb) = symb {
|
|
Ok(self.step_symb(board, *symb))
|
|
} else {
|
|
// No symbols available, play a random number
|
|
Chase::new(self.player).step_min(board)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MaximizerAgent for Diffuse {
|
|
fn step_max(&mut self, board: &Board) -> Result<PlayerAction> {
|
|
let mut x = [Symb::Minus, Symb::Times, Symb::Plus, Symb::Div];
|
|
x.shuffle(&mut thread_rng());
|
|
let symb = x.iter().find(|x| !board.contains(**x));
|
|
|
|
if let Some(symb) = symb {
|
|
Ok(self.step_symb(board, *symb))
|
|
} else {
|
|
// No symbols available, play a random number
|
|
Chase::new(self.player).step_max(board)
|
|
}
|
|
}
|
|
}
|