use crate::earth_colony_protocol::PlanetaryBody;
use serde::{Deserialize, Serialize};
pub fn synodic_period(from: PlanetaryBody, to: PlanetaryBody) -> u32 {
use PlanetaryBody::*;
let pair = normalize_pair(from, to);
match pair {
(Earth, Moon) | (Moon, Earth) => 0, (Earth, Mars) | (Mars, Earth) => 26, (Moon, Mars) | (Mars, Moon) => 26, (Earth, Europa) | (Europa, Earth) => 13, (Moon, Europa) | (Europa, Moon) => 13,
(Earth, Titan) | (Titan, Earth) => 12, (Moon, Titan) | (Titan, Moon) => 12,
(Mars, Europa) | (Europa, Mars) => 27, (Mars, Titan) | (Titan, Mars) => 24, (Earth, Ceres) | (Ceres, Earth) => 15, _ => 0, }
}
pub fn transfer_time(from: PlanetaryBody, to: PlanetaryBody) -> u32 {
use PlanetaryBody::*;
let pair = normalize_pair(from, to);
match pair {
(Earth, Moon) | (Moon, Earth) => 0, (Earth, Mars) | (Mars, Earth) => 9, (Moon, Mars) | (Mars, Moon) => 9,
(Earth, Europa) | (Europa, Earth) => 33, (Moon, Europa) | (Europa, Moon) => 33,
(Earth, Titan) | (Titan, Earth) => 73, (Moon, Titan) | (Titan, Moon) => 73,
(Mars, Europa) | (Europa, Mars) => 26, (Mars, Titan) | (Titan, Mars) => 59, (Earth, Ceres) | (Ceres, Earth) => 16, _ => 0,
}
}
pub fn hohmann_delta_v(from: PlanetaryBody, to: PlanetaryBody) -> f64 {
use PlanetaryBody::*;
let pair = normalize_pair(from, to);
match pair {
(Earth, Moon) | (Moon, Earth) => 4.0,
(Earth, Mars) | (Mars, Earth) => 4.5,
(Earth, Europa) | (Europa, Earth) => 7.2,
(Earth, Titan) | (Titan, Earth) => 8.4,
(Mars, Europa) | (Europa, Mars) => 3.9,
(Mars, Titan) | (Titan, Mars) => 5.5,
(Earth, Ceres) | (Ceres, Earth) => 5.0,
_ => 3.0, }
}
pub const EMERGENCY_DV_MULTIPLIER: f64 = 2.5;
pub const CONJUNCTION_BLACKOUT_TICKS: u32 = 1;
fn normalize_pair(a: PlanetaryBody, b: PlanetaryBody) -> (PlanetaryBody, PlanetaryBody) {
if (a as u8) <= (b as u8) {
(a, b)
} else {
(b, a)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WindowStatus {
Open,
Closed,
Blackout,
ContinuousAccess,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InterplanetaryRoute {
pub from: PlanetaryBody,
pub to: PlanetaryBody,
pub synodic_period: u32,
pub transfer_time: u32,
pub delta_v: f64,
pub cost_per_kg: f64,
pub status: WindowStatus,
}
impl InterplanetaryRoute {
pub fn new(from: PlanetaryBody, to: PlanetaryBody) -> Self {
let sp = synodic_period(from, to);
let tt = transfer_time(from, to);
let dv = hohmann_delta_v(from, to);
Self {
from,
to,
synodic_period: sp,
transfer_time: tt,
delta_v: dv,
cost_per_kg: dv * dv * 0.001,
status: if sp == 0 {
WindowStatus::ContinuousAccess
} else {
WindowStatus::Closed
},
}
}
pub fn update_status(
&mut self,
current_tick: u32,
established_tick: u32,
has_fusion_drive: bool,
) {
if has_fusion_drive || self.synodic_period == 0 {
self.status = WindowStatus::ContinuousAccess;
return;
}
let elapsed = current_tick.saturating_sub(established_tick);
if elapsed % self.synodic_period == 0 {
self.status = WindowStatus::Open;
} else {
self.status = WindowStatus::Closed;
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InTransitCargo {
pub from: PlanetaryBody,
pub to: PlanetaryBody,
pub resource_type: String,
pub amount_kg: f64,
pub departure_tick: u32,
pub arrival_tick: u32,
pub is_emergency: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_synodic_periods_match_orbital_mechanics() {
assert_eq!(
synodic_period(PlanetaryBody::Earth, PlanetaryBody::Mars),
26
);
assert_eq!(
synodic_period(PlanetaryBody::Earth, PlanetaryBody::Europa),
13
);
assert_eq!(
synodic_period(PlanetaryBody::Earth, PlanetaryBody::Titan),
12
);
assert_eq!(synodic_period(PlanetaryBody::Earth, PlanetaryBody::Moon), 0);
}
#[test]
fn test_transfer_times() {
assert_eq!(transfer_time(PlanetaryBody::Earth, PlanetaryBody::Mars), 9);
assert_eq!(
transfer_time(PlanetaryBody::Earth, PlanetaryBody::Titan),
73
);
}
#[test]
fn test_route_creation() {
let route = InterplanetaryRoute::new(PlanetaryBody::Earth, PlanetaryBody::Europa);
assert_eq!(route.synodic_period, 13);
assert_eq!(route.transfer_time, 33);
assert!(route.delta_v > 7.0);
}
#[test]
fn test_symmetry() {
assert_eq!(
synodic_period(PlanetaryBody::Mars, PlanetaryBody::Europa),
synodic_period(PlanetaryBody::Europa, PlanetaryBody::Mars),
);
}
}