use rand::Rng;
use rdpe::prelude::*;
#[derive(MultiParticle, Clone)]
enum Creature {
Boid {
position: Vec3,
velocity: Vec3,
flock_id: u32,
},
Predator {
position: Vec3,
velocity: Vec3,
hunger: f32,
target_id: u32,
},
}
fn main() {
let mut rng = rand::thread_rng();
let num_boids = 3000;
let num_predators = 30;
let total = num_boids + num_predators;
let particles: Vec<Creature> = (0..total)
.map(|i| {
let is_predator = i >= num_boids;
let spread = if is_predator { 0.4 } else { 0.9 };
let pos = Vec3::new(
rng.gen_range(-spread..spread),
rng.gen_range(-spread..spread),
rng.gen_range(-spread..spread),
);
let vel = Vec3::new(
rng.gen_range(-0.05..0.05),
rng.gen_range(-0.05..0.05),
rng.gen_range(-0.05..0.05),
);
if is_predator {
Creature::Predator {
position: pos,
velocity: vel,
hunger: 1.0,
target_id: 0,
}
} else {
Creature::Boid {
position: pos,
velocity: vel,
flock_id: rng.gen_range(0..3),
}
}
})
.collect();
Simulation::<Creature>::new()
.with_particle_count(total as u32)
.with_bounds(1.0)
.with_spatial_config(0.25, 32)
.with_spawner(move |ctx| particles[ctx.index as usize].clone())
.with_rule(Rule::Typed {
self_type: Creature::BOID,
other_type: Some(Creature::BOID),
rule: Box::new(Rule::Separate {
radius: 0.05,
strength: 2.5,
}),
})
.with_rule(Rule::Typed {
self_type: Creature::BOID,
other_type: Some(Creature::BOID),
rule: Box::new(Rule::Cohere {
radius: 0.15,
strength: 1.2,
}),
})
.with_rule(Rule::Typed {
self_type: Creature::BOID,
other_type: Some(Creature::BOID),
rule: Box::new(Rule::Align {
radius: 0.12,
strength: 1.8,
}),
})
.with_rule(Rule::Evade {
self_type: Creature::BOID,
threat_type: Creature::PREDATOR,
radius: 0.3,
strength: 5.0,
})
.with_rule(Rule::Chase {
self_type: Creature::PREDATOR,
target_type: Creature::BOID,
radius: 0.5,
strength: 3.5,
})
.with_rule(Rule::Typed {
self_type: Creature::PREDATOR,
other_type: Some(Creature::PREDATOR),
rule: Box::new(Rule::Separate {
radius: 0.15,
strength: 1.5,
}),
})
.with_rule(Rule::Custom(
r#"
// Use the generated is_predator helper
if is_predator(p) {
// Predators get hungrier over time
p.hunger = max(0.0, p.hunger - uniforms.delta_time * 0.02);
// Hungry predators move faster
let speed_boost = 1.0 + (1.0 - p.hunger) * 0.5;
p.velocity *= speed_boost;
}
// Could also access boid's flock_id if needed:
// if is_boid(p) { let flock = p.flock_id; }
"#
.into(),
))
.with_rule(Rule::SpeedLimit { min: 0.0, max: 2.5 })
.with_rule(Rule::Drag(2.0))
.with_rule(Rule::BounceWalls { restitution: 1.0 })
.with_visuals(|v| {
v.background(Vec3::new(0.02, 0.02, 0.04));
v.blend_mode(BlendMode::Additive);
v.palette(Palette::Rainbow, ColorMapping::Index);
})
.run().expect("Simulation failed");
}