Added diffuse agent

master
Mark 2024-03-03 21:48:33 -08:00
parent 2d7432c228
commit a5e3998727
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
5 changed files with 130 additions and 20 deletions

105
src/agents/diffuse.rs Normal file
View File

@ -0,0 +1,105 @@
use super::PlayerAgent;
use crate::util::Symb;
use rand::Rng;
use std::num::NonZeroU8;
/// A simple "operator diffusion" MINIMIZER agent.
///
/// Tries to keep operators as far apart as possible, denying large numbers.
pub struct DiffuseAgent {}
impl DiffuseAgent {
/// Place a symbol on the board.
/// Assumes `symb` is not already on the board
fn step_symb(&self, board: &mut crate::board::Board, symb: Symb) {
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.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();
println!("{:?}", dist);
'outer: loop {
for i in 0..11 {
if dist[i] >= max_dist {
if board.play(i, symb) {
break 'outer;
};
}
}
max_dist -= 1;
}
}
/// Called when we're out of symbols
fn step_number(&self, board: &mut crate::board::Board) {
let mut rng = rand::thread_rng();
let n = board.size();
let mut c = rng.gen_range(0..n);
let mut s = {
let n = rng.gen_range(0..=9);
if n == 0 {
Symb::Zero
} else {
Symb::Number(NonZeroU8::new(n).unwrap())
}
};
while !board.play(c, s) {
c = rng.gen_range(0..n);
s = {
let n = rng.gen_range(0..=9);
if n == 0 {
Symb::Zero
} else {
Symb::Number(NonZeroU8::new(n).unwrap())
}
};
}
}
}
impl PlayerAgent for DiffuseAgent {
fn step(&mut self, board: &mut crate::board::Board) {
let symb = [Symb::Minus, Symb::Times, Symb::Plus, Symb::Div]
.iter()
.filter(|x| !board.contains(**x))
.next();
// No symbols available, play a random number
if symb.is_none() {
self.step_number(board)
} else {
self.step_symb(board, *symb.unwrap())
}
}
}

11
src/agents/mod.rs Normal file
View File

@ -0,0 +1,11 @@
mod diffuse;
mod random;
pub use diffuse::DiffuseAgent;
pub use random::RandomAgent;
use crate::board::Board;
pub trait PlayerAgent {
fn step(&mut self, board: &mut Board);
}

View File

@ -1,8 +1,7 @@
use std::num::NonZeroU8;
use super::PlayerAgent;
use crate::util::Symb;
use rand::Rng;
use crate::{util::Symb, PlayerAgent};
use std::num::NonZeroU8;
pub struct RandomAgent {}

View File

@ -11,22 +11,16 @@ use termion::{
raw::IntoRawMode,
};
mod agents;
mod board;
mod random;
mod util;
use board::Board;
use util::{Player, Symb};
use crate::random::RandomAgent;
pub trait PlayerAgent {
fn step(&mut self, board: &mut Board);
}
fn play(
stdout: &mut StdoutLock,
player_max: bool,
computer: &mut dyn PlayerAgent,
computer: &mut dyn agents::PlayerAgent,
) -> Result<Board> {
let mut cursor = 0usize;
let cursor_offset = 10usize - 1;
@ -88,6 +82,7 @@ fn play(
break;
}
// Player turn
let stdin = stdin();
for c in stdin.keys() {
print_board = match c.unwrap() {
@ -112,10 +107,10 @@ fn play(
Key::Char('8') => board.play(cursor, Symb::Number(NonZeroU8::new(8).unwrap())),
Key::Char('9') => board.play(cursor, Symb::Number(NonZeroU8::new(9).unwrap())),
Key::Char('0') => board.play(cursor, Symb::Zero),
Key::Char('a') => board.play(cursor, Symb::Plus),
Key::Char('s') => board.play(cursor, Symb::Minus),
Key::Char('m') => board.play(cursor, Symb::Times),
Key::Char('d') => board.play(cursor, Symb::Div),
Key::Char('+') => board.play(cursor, Symb::Plus),
Key::Char('-') => board.play(cursor, Symb::Minus),
Key::Char('*') => board.play(cursor, Symb::Times),
Key::Char('/') => board.play(cursor, Symb::Div),
_ => false,
};
continue 'outer;
@ -129,7 +124,7 @@ fn main() -> Result<()> {
let stdout = HideCursor::from(stdout().into_raw_mode().unwrap());
let mut stdout = stdout.lock();
let mut agent = RandomAgent {};
let mut agent = agents::DiffuseAgent {};
let a = play(&mut stdout, true, &mut agent)?;
if a.is_done() {
println!(
@ -147,7 +142,7 @@ fn main() -> Result<()> {
return Ok(());
}
let mut agent = RandomAgent {};
let mut agent = agents::RandomAgent {};
let b = play(&mut stdout, false, &mut agent)?;
if b.is_done() {
println!(

View File

@ -59,9 +59,9 @@ impl Display for Symb {
impl Symb {
/// Is this symbol a plain binary operator?
pub fn is_binop(&self) -> bool {
pub fn is_op(&self) -> bool {
match self {
Symb::Div | Symb::Plus | Symb::Times => true,
Symb::Div | Symb::Plus | Symb::Times | Symb::Minus => true,
_ => false,
}
}