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, 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>, } impl ScriptedAgent { pub fn new(mut moves: Vec) -> 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(), ) } 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(), } } else { random_move() } } }