Skip to main content

proof_engine/ecology/
migration.rs

1//! Migration patterns — seasonal flow of entity populations.
2
3/// A migration route.
4#[derive(Debug, Clone)]
5pub struct MigrationRoute {
6    pub species_id: u32,
7    pub waypoints: Vec<(f32, f32)>,
8    pub season_start: f32,   // 0.0-1.0 (fraction of year)
9    pub season_end: f32,
10    pub fraction: f64,       // fraction of population that migrates
11}
12
13/// Compute current position along a migration route given time.
14pub fn route_position(route: &MigrationRoute, time_of_year: f32) -> Option<(f32, f32)> {
15    if route.waypoints.len() < 2 { return None; }
16    let t = time_of_year;
17    if t < route.season_start || t > route.season_end { return None; }
18
19    let progress = (t - route.season_start) / (route.season_end - route.season_start);
20    let segment_f = progress * (route.waypoints.len() - 1) as f32;
21    let seg = (segment_f as usize).min(route.waypoints.len() - 2);
22    let local_t = segment_f - seg as f32;
23
24    let (x0, y0) = route.waypoints[seg];
25    let (x1, y1) = route.waypoints[seg + 1];
26    Some((x0 + (x1 - x0) * local_t, y0 + (y1 - y0) * local_t))
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn test_migration_position() {
35        let route = MigrationRoute {
36            species_id: 0,
37            waypoints: vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0)],
38            season_start: 0.2,
39            season_end: 0.8,
40            fraction: 0.5,
41        };
42        let pos = route_position(&route, 0.5).unwrap();
43        assert!(pos.0 > 0.0, "should have moved");
44    }
45}