use rand::rngs::StdRng;
use rand::SeedableRng;
use rustsim::prelude::*;
#[derive(Debug, Clone)]
struct Boid {
id: AgentId,
pos: rustsim_spaces::continuous::ContinuousPos,
speed: f64,
}
impl Agent for Boid {
fn id(&self) -> AgentId {
self.id
}
}
impl PositionedAgent for Boid {
type Position = rustsim_spaces::continuous::ContinuousPos;
fn position(&self) -> &Self::Position {
&self.pos
}
fn set_position(&mut self, p: Self::Position) {
self.pos = p;
}
}
type BoidModel = StandardModel<
rustsim_spaces::continuous::ContinuousSpace2D,
Boid,
HashMapStore<Boid>,
(), StdRng,
Fastest,
>;
fn boid_step(
boid: &mut Boid,
_ctx: &mut StepContext<
'_,
rustsim_spaces::continuous::ContinuousSpace2D,
Boid,
(),
StdRng,
Fastest,
>,
) {
let new_x = boid.pos.x + boid.speed;
let new_y = boid.pos.y;
boid.pos = rustsim_spaces::continuous::ContinuousPos::new(new_x.min(99.9), new_y);
}
#[test]
fn external_consumer_full_workflow() {
let space =
rustsim_spaces::continuous::ContinuousSpace2D::new(100.0, 100.0, false, 5.0).unwrap();
let store = HashMapStore::new();
let mut model = BoidModel::new_base(store, space, Fastest::new(), (), StdRng::seed_from_u64(7))
.with_agent_step_ctx(boid_step);
for i in 1..=10 {
let boid = Boid {
id: model.next_id(),
pos: rustsim_spaces::continuous::ContinuousPos::new(i as f64, 50.0),
speed: i as f64 * 0.5,
};
add_agent(&mut model, boid).unwrap();
}
model.step_n(5);
assert_eq!(model.time(), Time::Discrete(5));
let ids: Vec<AgentId> = model.agents().map(|a| a.id()).collect();
let mut snapshots: Vec<(AgentId, f64)> = Vec::new();
let mut model_snaps: Vec<Time> = Vec::new();
collect_step(
&model,
&ids,
Some(&|boid: &Boid, _m: &BoidModel| (boid.id, boid.pos.x)),
Some(&|m: &BoidModel| m.time()),
&mut snapshots,
&mut model_snaps,
);
assert_eq!(snapshots.len(), 10);
assert_eq!(model_snaps, vec![Time::Discrete(5)]);
for (id, x) in &snapshots {
let expected = (*id as f64) + 5.0 * (*id as f64 * 0.5);
let expected = expected.min(99.9);
assert!(
(x - expected).abs() < 1e-9,
"boid {id}: expected x={expected}, got x={x}"
);
}
let path = astar(
(0usize, 0usize),
(3, 3),
|a, b| ((a.0 as f64 - b.0 as f64).powi(2) + (a.1 as f64 - b.1 as f64).powi(2)).sqrt(),
|n| {
let (x, y) = *n;
let mut neighbors = Vec::new();
if x + 1 < 5 {
neighbors.push(((x + 1, y), 1.0));
}
if y + 1 < 5 {
neighbors.push(((x, y + 1), 1.0));
}
neighbors
},
);
assert!(path.is_some());
let _backend = detect_backend();
}