Browse Source

Some world testing, basic agents

main
Adrian Heine 6 months ago
parent
commit
65986a1d22
  1. 96
      src/agent.rs
  2. 15
      src/main.rs
  3. 90
      src/world.rs

96
src/agent.rs

@ -5,19 +5,35 @@ pub struct Position {
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_tagged: bool,
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),
@ -26,5 +42,81 @@ pub enum Move {
pub trait Agent {
fn next_move(&self, view: WorldView) -> Move;
fn get_tagged(&mut self, by: AgentId);
}
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(),
)
}
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()
}
}
}

15
src/main.rs

@ -1,3 +1,16 @@
use gntag::agent::{Agent, Position, SimpleAgent};
use gntag::world::ActualWorld;
fn main() {
println!("Hello, world!");
let mut agents: Vec<(_, Box<dyn Agent>)> = vec![];
for x in 0..5 {
for y in 0..5 {
agents.push(((x, y).into(), Box::new(SimpleAgent)));
}
}
let mut world = ActualWorld::new((80, 40).into(), agents);
loop {
world.do_step();
println!("{}", world.tagged);
}
}

90
src/world.rs

@ -1,5 +1,5 @@
use crate::agent::{Agent, AgentId, Direction, Move, Position, WorldView};
use std::collections::HashMap;
use std::collections::hash_map::{Entry, HashMap};
pub struct ActualWorld {
size: Position,
@ -10,6 +10,28 @@ pub struct ActualWorld {
}
impl ActualWorld {
/// Agents receive incrementing ids starting with 0
/// The last agent is 'It'
pub fn new(size: Position, input_agents: Vec<(Position, Box<dyn Agent>)>) -> Self {
let agent_count = input_agents.len();
assert!(agent_count > 0);
let mut id = 0;
let mut agent_positions = HashMap::with_capacity(agent_count);
let mut agents = HashMap::with_capacity(agent_count);
for (pos, agent) in input_agents.into_iter() {
agent_positions.insert(id, pos);
agents.insert(id, agent);
id += 1;
}
Self {
size,
tagged_by: None,
tagged: id - 1,
agents,
agent_positions,
}
}
pub fn do_step(&mut self) {
let moves: Vec<_> = self
.agents
@ -19,11 +41,14 @@ impl ActualWorld {
(
*id,
agent.next_move(WorldView {
self_tagged: *id == self.tagged,
self_id: *id,
tagged_by: self.tagged_by,
tagged: self.tagged,
bounds_distance: (pos.y, self.size.x - pos.x, self.size.y - pos.y, pos.x),
other_agents: self
.agent_positions
.iter()
.filter(|(other_id, _)| id != *other_id)
.map(|(id, other_pos)| {
(
Direction {
@ -43,15 +68,21 @@ impl ActualWorld {
Move::Noop => {}
Move::TryTag(other_id) => {
// FIXME check distance
// FIXME check tagged_by
self.agents.get_mut(&other_id).unwrap().get_tagged(other_id);
// FIXME update tagged
// FIXME update tagged_by
assert_eq!(self.tagged, id);
assert_ne!(self.tagged_by, Some(other_id));
assert!(
self.agents.contains_key(&other_id),
"Trying to tag a nonexisting agent"
);
self.tagged = other_id;
self.tagged_by = Some(id);
}
Move::TryMove(dir) => {
// FIXME check out of bounds
// FIXME matches!
self.agent_positions.entry(id).and_modify(|e| {
// FIXME check speed
let entry = self.agent_positions.entry(id);
assert!(matches!(entry, Entry::Occupied(_)));
entry.and_modify(|e| {
e.x += dir.x;
e.y += dir.y;
});
@ -60,3 +91,46 @@ impl ActualWorld {
}
}
}
#[cfg(test)]
mod test {
use crate::agent::{Move, NullAgent, Position, ScriptedAgent};
use crate::world::ActualWorld;
#[test]
#[should_panic]
fn empty() {
ActualWorld::new(Position { x: 0, y: 0 }, vec![]);
}
#[test]
fn trivial() {
let mut world = ActualWorld::new(
Position { x: 0, y: 0 },
vec![(Position { x: 0, y: 0 }, Box::new(NullAgent))],
);
world.do_step();
assert_eq!(world.tagged, 0);
assert_eq!(world.tagged_by, None);
}
#[test]
fn scripted_tagging() {
let mut world = ActualWorld::new(
Position { x: 0, y: 0 },
vec![
(
Position { x: 0, y: 0 },
Box::new(ScriptedAgent::new(vec![Move::Noop])),
),
(
Position { x: 0, y: 0 },
Box::new(ScriptedAgent::new(vec![Move::TryTag(0)])),
),
],
);
world.do_step();
assert_eq!(world.tagged, 0);
assert_eq!(world.tagged_by, Some(1));
}
}
Loading…
Cancel
Save