Some world testing, basic agents
This commit is contained in:
parent
b21806e48d
commit
65986a1d22
3 changed files with 190 additions and 11 deletions
96
src/agent.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
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
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…
Add table
Add a link
Reference in a new issue