use crate::earth_colony_protocol::{PlanetaryBody, ResourceManifest};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ISRUCapability {
pub body: PlanetaryBody,
pub water_extraction: f64,
pub oxygen_production: f64,
pub metal_extraction: f64,
pub hydrocarbon_collection: f64,
pub nitrogen_extraction: f64,
pub requires_nuclear: bool,
pub min_tech_level: f64,
}
impl ISRUCapability {
pub fn for_body(body: PlanetaryBody) -> Self {
match body {
PlanetaryBody::Earth => Self {
body,
water_extraction: 1000.0,
oxygen_production: 500.0,
metal_extraction: 100.0,
hydrocarbon_collection: 0.0,
nitrogen_extraction: 100.0,
requires_nuclear: false,
min_tech_level: 0.0,
},
PlanetaryBody::Moon => Self {
body,
water_extraction: 5.0, oxygen_production: 20.0, metal_extraction: 10.0, hydrocarbon_collection: 0.0,
nitrogen_extraction: 0.0,
requires_nuclear: false,
min_tech_level: 1.5,
},
PlanetaryBody::Mars => Self {
body,
water_extraction: 15.0, oxygen_production: 30.0, metal_extraction: 15.0, hydrocarbon_collection: 0.0,
nitrogen_extraction: 2.0, requires_nuclear: false,
min_tech_level: 1.3,
},
PlanetaryBody::Europa => Self {
body,
water_extraction: 500.0, oxygen_production: 100.0, metal_extraction: 1.0, hydrocarbon_collection: 0.0,
nitrogen_extraction: 0.0, requires_nuclear: true,
min_tech_level: 2.0,
},
PlanetaryBody::Titan => Self {
body,
water_extraction: 200.0, oxygen_production: 50.0, metal_extraction: 0.0, hydrocarbon_collection: 1000.0, nitrogen_extraction: 500.0, requires_nuclear: true,
min_tech_level: 2.0,
},
PlanetaryBody::Ceres => Self {
body,
water_extraction: 100.0, oxygen_production: 30.0,
metal_extraction: 20.0, hydrocarbon_collection: 0.0,
nitrogen_extraction: 0.5,
requires_nuclear: false,
min_tech_level: 1.8,
},
PlanetaryBody::OrbitalHabitat => Self {
body,
water_extraction: 0.0,
oxygen_production: 0.0,
metal_extraction: 0.0,
hydrocarbon_collection: 0.0,
nitrogen_extraction: 0.0,
requires_nuclear: false,
min_tech_level: 0.0,
},
}
}
pub fn primary_exports(&self) -> Vec<&'static str> {
match self.body {
PlanetaryBody::Earth => vec!["knowledge", "rare_earth", "silicon"],
PlanetaryBody::Moon => vec!["silicon", "aluminum", "oxygen"],
PlanetaryBody::Mars => vec!["iron", "water", "oxygen"],
PlanetaryBody::Europa => vec!["water", "oxygen"],
PlanetaryBody::Titan => vec!["hydrocarbons", "nitrogen", "carbon"],
PlanetaryBody::Ceres => vec!["iron", "water", "carbon"],
PlanetaryBody::OrbitalHabitat => vec![],
}
}
pub fn primary_imports(&self) -> Vec<&'static str> {
match self.body {
PlanetaryBody::Earth => vec![], PlanetaryBody::Moon => vec!["nitrogen", "carbon", "food"],
PlanetaryBody::Mars => vec!["nitrogen", "rare_earth"],
PlanetaryBody::Europa => vec!["iron", "silicon", "nitrogen", "carbon"],
PlanetaryBody::Titan => vec!["iron", "silicon", "aluminum", "rare_earth"],
PlanetaryBody::Ceres => vec!["nitrogen", "food"],
PlanetaryBody::OrbitalHabitat => vec!["food", "water", "oxygen", "materials"],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BootstrapPower {
pub initial_power_w: f64,
pub current_power_w: f64,
pub decay_rate_per_tick: f64,
}
impl BootstrapPower {
pub fn outer_system_default() -> Self {
Self {
initial_power_w: 440.0,
current_power_w: 440.0,
decay_rate_per_tick: 0.0013, }
}
pub fn tick(&mut self) {
self.current_power_w *= 1.0 - self.decay_rate_per_tick;
}
pub fn power_fraction(&self) -> f64 {
if self.initial_power_w > 0.0 {
self.current_power_w / self.initial_power_w
} else {
0.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_isru_requires_nuclear_for_outer_system() {
assert!(ISRUCapability::for_body(PlanetaryBody::Europa).requires_nuclear);
assert!(ISRUCapability::for_body(PlanetaryBody::Titan).requires_nuclear);
assert!(!ISRUCapability::for_body(PlanetaryBody::Mars).requires_nuclear);
}
#[test]
fn test_titan_exports_hydrocarbons() {
let titan = ISRUCapability::for_body(PlanetaryBody::Titan);
assert!(titan.primary_exports().contains(&"hydrocarbons"));
assert!(titan.primary_imports().contains(&"iron"));
}
#[test]
fn test_rtg_decay() {
let mut rtg = BootstrapPower::outer_system_default();
assert!((rtg.current_power_w - 440.0).abs() < 0.1);
for _ in 0..120 {
rtg.tick();
}
let expected = 440.0 * (1.0 - 0.0013_f64).powi(120);
assert!((rtg.current_power_w - expected).abs() < 1.0);
assert!(rtg.power_fraction() > 0.8); }
#[test]
fn test_complementary_trade() {
let europa = ISRUCapability::for_body(PlanetaryBody::Europa);
let titan = ISRUCapability::for_body(PlanetaryBody::Titan);
assert!(titan.primary_exports().contains(&"nitrogen"));
assert!(europa.primary_imports().contains(&"nitrogen"));
}
}