use jantu::coevolution::{
ArmsRaceTrait, TraitMatchup, encounter_rate, functional_response_type2, red_queen_balance,
trait_pressure,
};
use jantu::migration::{MigrationStrategy, migration_energy_cost, migratory_urge, season_from_day};
use jantu::stress::{StressState, immune_function, stress_drive_modifier};
fn main() {
println!("=== Predator-Prey Arms Race ===\n");
let speed = TraitMatchup::new(ArmsRaceTrait::Speed, 0.7, 0.6);
let camouflage = TraitMatchup::new(ArmsRaceTrait::Detection, 0.5, 0.8);
println!(
"Speed matchup: predator {:.1} vs prey {:.1} → predator advantage {:.2}",
speed.predator_trait,
speed.prey_trait,
speed.predator_advantage()
);
println!(
"Camo matchup: predator {:.1} vs prey {:.1} → prey advantage {:.2}",
camouflage.predator_trait,
camouflage.prey_trait,
camouflage.prey_advantage()
);
let pressure = trait_pressure(0.6, 0.7, 0.8);
println!("Selection pressure on prey speed: {pressure:.3}");
let balance = red_queen_balance(0.05, 0.05);
println!("Red Queen balance (equal rates): {balance:.3} (near zero = stasis)");
let encounters = encounter_rate(2.0, 50.0, 0.3);
let consumption = functional_response_type2(50.0, 0.3, 0.2);
println!("\nEncounters/tick: {encounters:.1}");
println!("Consumption rate (Holling Type II): {consumption:.2} prey/predator/tick");
println!("\n=== Seasonal Migration ===\n");
for day in [80, 170, 260, 350] {
let season = season_from_day(day);
let obligate = migratory_urge(day, 0.5, 0.5, MigrationStrategy::Obligate);
let facultative = migratory_urge(day, 0.3, 0.2, MigrationStrategy::Facultative);
println!(
"Day {day:3} ({season:?}): obligate urge {obligate:.2}, facultative urge {facultative:.2}"
);
}
let fly_cost = migration_energy_cost(0.5, true, 0.0);
let walk_cost = migration_energy_cost(40.0, false, 0.0);
let headwind_cost = migration_energy_cost(0.5, true, 0.8);
println!("\nMigration energy cost:");
println!(" Songbird (0.5kg, flying, calm): {fly_cost:.3}");
println!(" Wildebeest (40kg, walking, calm): {walk_cost:.3}");
println!(" Songbird (0.5kg, flying, headwind): {headwind_cost:.3}");
println!("\n=== Stress Cascade ===\n");
let mut stress = StressState::new();
println!(
"Fresh: acute={:.2}, chronic={:.2}, resilience={:.2}",
stress.acute, stress.chronic, stress.resilience
);
for i in 1..=5 {
stress.apply_stressor(0.6);
let fear_mod = stress_drive_modifier(stress.chronic, true);
let curiosity_mod = stress_drive_modifier(stress.chronic, false);
let immune = immune_function(stress.chronic, stress.resilience);
println!(
"Stressor #{i}: acute={:.2}, chronic={:.2}, resilience={:.2} | fear x{fear_mod:.2}, curiosity x{curiosity_mod:.2}, immune={immune:.2}",
stress.acute, stress.chronic, stress.resilience
);
}
println!("\nRecovering in safe environment...");
for tick in [10.0, 50.0, 100.0] {
let mut recovering = stress.clone();
recovering.recover(tick, 0.9);
println!(
" After {tick:.0} ticks: acute={:.2}, chronic={:.2}",
recovering.acute, recovering.chronic
);
}
}