A simple actor-based simulation of tag, implemented in rust.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
2.6 KiB

pub type Distance = isize;
pub struct Position {
pub x: Distance,
pub y: Distance,
}
impl From<(Distance, Distance)> for Position {
fn from((x, y): (Distance, Distance)) -> Self {
Self { x, y }
}
}
#[derive(Debug)]
pub struct Direction {
pub x: Distance,
pub y: Distance,
}
impl From<(Distance, Distance)> for Direction {
fn from((x, y): (Distance, Distance)) -> Self {
Self { x, y }
}
}
pub type AgentId = usize;
pub struct WorldView {
pub self_id: AgentId,
pub tagged: AgentId,
pub tagged_by: Option<AgentId>,
pub bounds_distance: (Distance, Distance, Distance, Distance),
pub other_agents: Vec<(Direction, AgentId)>,
}
#[derive(Debug)]
pub enum Move {
Noop, // Why not an Option?
TryTag(AgentId),
TryMove(Direction),
}
pub trait Agent {
fn next_move(&self, view: WorldView) -> Move;
}
pub struct NullAgent;
impl Agent for NullAgent {
fn next_move(&self, _view: WorldView) -> Move {
Move::Noop
}
}
use std::cell::RefCell;
pub struct ScriptedAgent {
moves: RefCell<Vec<Move>>,
}
impl ScriptedAgent {
pub fn new(mut moves: Vec<Move>) -> Self {
moves.reverse();
Self {
moves: RefCell::new(moves),
}
}
}
impl Agent for ScriptedAgent {
fn next_move(&self, _view: WorldView) -> Move {
self
.moves
.borrow_mut()
.pop()
.expect("ScriptedAgent has no moves left to make")
}
}
pub struct SimpleAgent;
fn rand(exclusive_upper_bound: u32) -> u32 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.subsec_micros()
% exclusive_upper_bound
}
fn random_move() -> Move {
Move::TryMove(
match rand(8) {
0 => (-1, -1),
1 => (0, -1),
2 => (1, -1),
3 => (1, 0),
4 => (1, 1),
5 => (0, 1),
6 => (-1, 1),
7 => (-1, 0),
_ => unreachable!(),
}
.into(),
)
}
fn random_move_within(
(top, right, bottom, left): (Distance, Distance, Distance, Distance),
) -> Move {
loop {
let mv = random_move();
if let Move::TryMove(Direction { x, y }) = mv {
if top + y > 0 && bottom - y > 0 && left + x > 0 && right - x > 0 {
return mv;
}
}
}
}
impl Agent for SimpleAgent {
fn next_move(&self, view: WorldView) -> Move {
if view.self_id == view.tagged {
match view
.other_agents
.iter()
.find(|(dist, id)| dist.x.abs() <= 1 && dist.y.abs() <= 1 && Some(*id) != view.tagged_by)
{
Some((_, other)) => Move::TryTag(*other),
None => random_move_within(view.bounds_distance),
}
} else {
random_move_within(view.bounds_distance)
}
}
}