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.
156 lines
3.2 KiB
156 lines
3.2 KiB
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<AgentId>,
|
|
pub bounds_distance: (Distance, Distance, Distance, Distance),
|
|
pub other_agents: &'o mut dyn Iterator<Item = (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 {
|
|
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)
|
|
}
|
|
}
|
|
}
|