pub type Distance = isize; #[derive(Clone)] 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<'o> { pub self_id: AgentId, pub tagged: AgentId, pub tagged_by: Option, pub bounds_distance: (Distance, Distance, Distance, Distance), pub other_agents: &'o mut dyn Iterator, } #[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(), ) } 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 { let mut cur = None; for (dist, id) in view.other_agents { if Some(id) == view.tagged_by { continue; } if dist.x.abs() <= 1 && dist.y.abs() <= 1 { cur = Some((id, 0, dist)); break; } let total_dist = dist.x.abs() + dist.y.abs(); let cur_dist = if let Some((_, dist, _)) = cur { dist } else { isize::MAX }; if total_dist < cur_dist { cur = Some((id, total_dist, dist)); } } match cur { Some((other, total_dist, dist)) => { if total_dist == 0 { Move::TryTag(other) } else { Move::TryMove((dist.x.signum(), dist.y.signum()).into()) } } None => random_move_within(view.bounds_distance), } } else { random_move_within(view.bounds_distance) } } }