use std::sync::{Arc, RwLock};
use syren::agents::AgentTemplate;
use syren::model::ModelBuilder;
use syren::{advanced::EntityShards, ComponentRegistry, DetRng, FnSystem, QueryBuilder, Welford};
#[derive(Clone, Copy, Default)]
struct Position {
x: i64,
}
const WALKERS: usize = 10_000;
const TICKS: u64 = 50;
const SEED: u64 = 42;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let registry = Arc::new(RwLock::new(ComponentRegistry::new()));
let position_id = registry.write().unwrap().register::<Position>()?;
registry.write().unwrap().freeze();
let population: Vec<Position> = vec![Position { x: 0 }; WALKERS];
let step_query = QueryBuilder::with_registry(Arc::clone(®istry))
.write::<Position>()?
.build()?;
let walk_query = step_query.clone();
let mut model = ModelBuilder::new()
.with_seed(SEED)
.with_component_registry(Arc::clone(®istry))
.with_shards(EntityShards::new(1)?)
.with_agent_template(
AgentTemplate::builder("walker")
.with_component::<Position>(position_id)?
.with_capacity(WALKERS)
.build(),
)?
.with_agent_population("walker", position_id, population)?
.with_system(FnSystem::from_queries(
0,
"random_walk",
&[&step_query],
move |ecs| {
let context = ecs.run_context();
ecs.for_each_entity_w1::<Position>(walk_query.clone(), move |entity, pos| {
let mut rng = DetRng::from_context(context, u64::from(entity.index()));
pos.x += if rng.next_below(2) == 0 { -1 } else { 1 };
})
},
))
.build()?;
model.run(TICKS)?;
let summary_query = QueryBuilder::with_registry(registry)
.read::<Position>()?
.build()?;
let stats = model.ecs().world_ref().reduce_read::<Position, Welford>(
summary_query,
Welford::default,
|acc, pos| acc.push(pos.x as f64),
|acc, other| acc.combine(other),
)?;
println!(
"after {TICKS} ticks: count={} mean={:.4} variance={:.4}",
stats.n,
stats.mean,
stats.variance()
);
Ok(())
}