rustsim-core 0.0.1

Core ABM engine: agents, models, stores, schedulers, stepping, data collection
Documentation
use rand::rngs::StdRng;
use rand::SeedableRng;
use rustsim_core::{
    interaction::{self, PositionedAgent, SpaceInteraction},
    prelude::{Agent, ById, Fastest, HashMapStore, StandardModel},
    types::AgentId,
};
mod support;
use support::{Grid2D, GridPos2};

#[derive(Debug, Clone)]
struct GridBot {
    id: AgentId,
    pos: GridPos2,
}

impl Agent for GridBot {
    fn id(&self) -> AgentId {
        self.id
    }
}

impl PositionedAgent for GridBot {
    type Position = GridPos2;
    fn position(&self) -> &GridPos2 {
        &self.pos
    }
    fn set_position(&mut self, pos: GridPos2) {
        self.pos = pos;
    }
}

type BotModel = StandardModel<Grid2D, GridBot, HashMapStore<GridBot>, (), StdRng, Fastest>;

#[test]
fn add_remove_move_via_interaction_api() {
    let store = HashMapStore::new();
    let grid = Grid2D::new(5, 5, false);

    let mut model = BotModel::new(
        store,
        grid,
        Fastest::new(),
        (),
        StdRng::seed_from_u64(0),
        None,
        None,
        true,
    );

    let bot = GridBot {
        id: model.next_id(),
        pos: (1, 1),
    };
    interaction::add_agent(&mut model, bot).unwrap();
    assert!(model.agent(1).is_some());
    assert_eq!(*model.agent(1).unwrap().position(), (1, 1));

    interaction::move_agent(&mut model, 1, (3, 3)).unwrap();
    assert_eq!(*model.agent(1).unwrap().position(), (3, 3));

    let ids = <Grid2D as SpaceInteraction<GridBot>>::nearby_ids(model.space(), &(3, 3), 0);
    assert!(ids.contains(&1));

    let old_ids = <Grid2D as SpaceInteraction<GridBot>>::nearby_ids(model.space(), &(1, 1), 0);
    assert!(!old_ids.contains(&1));

    let removed = interaction::remove_agent(&mut model, 1).unwrap();
    assert!(removed.is_some());
    assert!(model.agent(1).is_none());
}

#[test]
fn nearby_agents_finds_correct_set() {
    let store = HashMapStore::new();
    let grid = Grid2D::new(10, 10, false);

    let mut model = BotModel::new(
        store,
        grid,
        Fastest::new(),
        (),
        StdRng::seed_from_u64(0),
        None,
        None,
        true,
    );

    let bots = vec![
        GridBot { id: 1, pos: (5, 5) },
        GridBot { id: 2, pos: (6, 5) },
        GridBot { id: 3, pos: (0, 0) },
    ];

    for bot in bots {
        interaction::add_agent(&mut model, bot).unwrap();
    }

    let nearby = interaction::nearby_agents(&model, &(5, 5), 1);
    let nearby_ids: Vec<AgentId> = nearby.iter().map(|a| a.id()).collect();
    assert!(nearby_ids.contains(&1));
    assert!(nearby_ids.contains(&2));
    assert!(!nearby_ids.contains(&3));
}

#[test]
fn step_spatial_applies_deferred_actions_to_store_and_space() {
    type SpatialModel = StandardModel<Grid2D, GridBot, HashMapStore<GridBot>, (), StdRng, ById>;

    let store = HashMapStore::new();
    let grid = Grid2D::new(10, 10, false);
    let mut model = SpatialModel::new_with_agent_step(
        store,
        grid,
        ById::new(),
        (),
        StdRng::seed_from_u64(1),
        |agent, ctx| {
            if agent.id == 1 {
                ctx.defer_remove_agent(2);
                ctx.defer_insert_agent(GridBot { id: 3, pos: (4, 4) });
            }
        },
        true,
    );

    model
        .insert_positioned_agent(GridBot { id: 1, pos: (1, 1) })
        .unwrap();
    model
        .insert_positioned_agent(GridBot { id: 2, pos: (2, 2) })
        .unwrap();

    model.step_spatial().unwrap();
    model.validate_space_index().unwrap();

    assert!(model.agent(1).is_some());
    assert!(model.agent(2).is_none());
    assert!(model.agent(3).is_some());

    let old_ids = <Grid2D as SpaceInteraction<GridBot>>::nearby_ids(model.space(), &(2, 2), 0);
    assert!(!old_ids.contains(&2));

    let new_ids = <Grid2D as SpaceInteraction<GridBot>>::nearby_ids(model.space(), &(4, 4), 0);
    assert_eq!(new_ids, vec![3]);
}