diff --git a/backend/src/calc/exit.rs b/backend/src/calc/exit.rs index d0c38c5..78a9fb0 100644 --- a/backend/src/calc/exit.rs +++ b/backend/src/calc/exit.rs @@ -50,11 +50,14 @@ mod tests { assert_eq!( calc_exit( &universe, - &universe.star_map[&1], + &universe.star_map[&SolarSystemId(1)], Meters::from_light_years(10.0) ), vec![ - (universe.star_map[&1].clone(), universe.star_map[&2].clone()), + ( + universe.star_map[&SolarSystemId(1)].clone(), + universe.star_map[&SolarSystemId(2)].clone() + ), // via SmartGate // (universe.star_map[&3].clone(), universe.star_map[&2].clone()), ] diff --git a/backend/src/calc/path.rs b/backend/src/calc/path.rs index 877ee95..444fb8f 100644 --- a/backend/src/calc/path.rs +++ b/backend/src/calc/path.rs @@ -129,8 +129,17 @@ mod tests { fn test_path_fuel_prefer_gate_over_jump() { let universe = Universe::tiny_test(); assert_eq!( - call_calc_path(&universe, 1, 4, 25.0, PathOptimize::Fuel, true), - PathResult::Found(vec![universe.star_map[&1].connections[0].clone()]) + call_calc_path( + &universe, + SolarSystemId(1), + SolarSystemId(4), + 25.0, + PathOptimize::Fuel, + true + ), + PathResult::Found(vec![ + universe.star_map[&SolarSystemId(1)].connections[0].clone() + ]) ); } @@ -139,10 +148,17 @@ mod tests { fn test_path_fuel_prefer_more_hops_over_more_fuel() { let universe = Universe::tiny_test(); assert_eq!( - call_calc_path(&universe, 4, 2, 25.0, PathOptimize::Fuel, false), + call_calc_path( + &universe, + SolarSystemId(4), + SolarSystemId(2), + 25.0, + PathOptimize::Fuel, + false + ), PathResult::Found(vec![ - universe.star_map[&4].connections[0].clone(), - universe.star_map[&1].connections[1].clone(), + universe.star_map[&SolarSystemId(4)].connections[0].clone(), + universe.star_map[&SolarSystemId(1)].connections[1].clone(), ]) ); } @@ -152,8 +168,17 @@ mod tests { fn test_path_distance() { let universe = Universe::tiny_test(); assert_eq!( - call_calc_path(&universe, 2, 4, 25.0, PathOptimize::Distance, false), - PathResult::Found(vec![universe.star_map[&2].connections[2].clone(),]) + call_calc_path( + &universe, + SolarSystemId(2), + SolarSystemId(4), + 25.0, + PathOptimize::Distance, + false + ), + PathResult::Found(vec![ + universe.star_map[&SolarSystemId(2)].connections[2].clone(), + ]) ); } @@ -162,8 +187,17 @@ mod tests { fn test_path_hops_jump_if_smart_gate_disabled() { let universe = Universe::tiny_test(); assert_eq!( - call_calc_path(&universe, 4, 3, 25.0, PathOptimize::Hops, false), - PathResult::Found(vec![universe.star_map[&4].connections[4].clone()]) + call_calc_path( + &universe, + SolarSystemId(4), + SolarSystemId(3), + 25.0, + PathOptimize::Hops, + false + ), + PathResult::Found(vec![ + universe.star_map[&SolarSystemId(4)].connections[4].clone() + ]) ); } @@ -172,8 +206,17 @@ mod tests { fn test_path_hops_use_smart_gate_if_smart_gate_enabled() { let universe = Universe::tiny_test(); assert_eq!( - call_calc_path(&universe, 4, 3, 25.0, PathOptimize::Hops, true), - PathResult::Found(vec![universe.star_map[&4].connections[1].clone()]) + call_calc_path( + &universe, + SolarSystemId(4), + SolarSystemId(3), + 25.0, + PathOptimize::Hops, + true + ), + PathResult::Found(vec![ + universe.star_map[&SolarSystemId(4)].connections[1].clone() + ]) ); } } diff --git a/backend/src/cli.rs b/backend/src/cli.rs index 943b9f4..e0f5b42 100644 --- a/backend/src/cli.rs +++ b/backend/src/cli.rs @@ -127,11 +127,13 @@ fn main() -> anyhow::Result<()> { let smart_gates: Vec = serde_json::from_str(&std::fs::read_to_string("data/smartgates.json")?)?; for gate in smart_gates.iter() { - let Some(to_star) = star_map.get(&gate.to).cloned() else { + let from_id = data::SolarSystemId::from(gate.from); + let to_id = data::SolarSystemId::from(gate.to); + let Some(to_star) = star_map.get(&to_id).cloned() else { warn!("Smart gate has unknown target {}", gate.to); continue; }; - let Some(from_star) = star_map.get_mut(&gate.from) else { + let Some(from_star) = star_map.get_mut(&from_id) else { warn!("Smart gate has unknown source {}", gate.from); continue; }; @@ -141,7 +143,7 @@ fn main() -> anyhow::Result<()> { id: conn_count, conn_type: data::ConnType::SmartGate, distance, - target: gate.to, + target: to_id, }); conn_count += 1; } diff --git a/backend/src/data.rs b/backend/src/data.rs index 6d9f40a..6ee8829 100644 --- a/backend/src/data.rs +++ b/backend/src/data.rs @@ -5,7 +5,52 @@ use rkyv::{Archive, Deserialize, Serialize}; use crate::units::Meters; pub type ConnectionId = u32; -pub type SolarSystemId = u32; + +#[derive( + Debug, + Archive, + Deserialize, + Serialize, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Default, + serde::Serialize, + serde::Deserialize, +)] +#[archive(compare(PartialEq, PartialOrd))] +#[archive_attr(derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord))] +pub struct SolarSystemId(pub u32); + +impl std::str::FromStr for SolarSystemId { + type Err = std::num::ParseIntError; + + fn from_str(s: &str) -> Result { + Ok(SolarSystemId::from(s.parse::()?)) + } +} + +impl std::fmt::Display for SolarSystemId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for SolarSystemId { + fn from(id: u32) -> Self { + SolarSystemId(id) + } +} + +impl From for u32 { + fn from(id: SolarSystemId) -> Self { + id.0 + } +} #[derive(Debug, Archive, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[rkyv(compare(PartialEq))] @@ -107,11 +152,11 @@ impl Universe { let json = star_map_json.solar_systems; let star_id_to_name: HashMap = json .iter() - .map(|star| (star.solar_system_id, star.name.clone())) + .map(|star| (SolarSystemId(star.id), star.name.clone())) .collect(); let star_name_to_id: HashMap = json .iter() - .map(|star| (star.name.clone(), star.solar_system_id)) + .map(|star| (star.name.clone(), SolarSystemId(star.id))) .collect(); Ok(Universe { @@ -161,22 +206,22 @@ impl Universe { */ #[rustfmt::skip] let stars = [ - MockStar { id: 1, name: "A", x: 0.0, y: 0.0 }, - MockStar { id: 2, name: "B", x: 10.0, y: 0.0 }, - MockStar { id: 3, name: "C", x: 20.0, y: 0.0 }, - MockStar { id: 4, name: "D", x: 10.0, y: 20.0 }, + MockStar { id: SolarSystemId(1), name: "A", x: 0.0, y: 0.0 }, + MockStar { id: SolarSystemId(2), name: "B", x: 10.0, y: 0.0 }, + MockStar { id: SolarSystemId(3), name: "C", x: 20.0, y: 0.0 }, + MockStar { id: SolarSystemId(4), name: "D", x: 10.0, y: 20.0 }, ]; #[rustfmt::skip] let conns = [ - MockConn { conn_type: ConnType::NpcGate, s1: 1, s2: 4 }, - MockConn { conn_type: ConnType::SmartGate, s1: 4, s2: 3 }, - MockConn { conn_type: ConnType::Jump, s1: 1, s2: 2 }, - MockConn { conn_type: ConnType::Jump, s1: 1, s2: 3 }, - MockConn { conn_type: ConnType::Jump, s1: 1, s2: 4 }, - MockConn { conn_type: ConnType::Jump, s1: 2, s2: 3 }, - MockConn { conn_type: ConnType::Jump, s1: 2, s2: 4 }, - MockConn { conn_type: ConnType::Jump, s1: 3, s2: 4 }, + MockConn { conn_type: ConnType::NpcGate, s1: SolarSystemId(1), s2: SolarSystemId(4) }, + MockConn { conn_type: ConnType::SmartGate, s1: SolarSystemId(4), s2: SolarSystemId(3) }, + MockConn { conn_type: ConnType::Jump, s1: SolarSystemId(1), s2: SolarSystemId(2) }, + MockConn { conn_type: ConnType::Jump, s1: SolarSystemId(1), s2: SolarSystemId(3) }, + MockConn { conn_type: ConnType::Jump, s1: SolarSystemId(1), s2: SolarSystemId(4) }, + MockConn { conn_type: ConnType::Jump, s1: SolarSystemId(2), s2: SolarSystemId(3) }, + MockConn { conn_type: ConnType::Jump, s1: SolarSystemId(2), s2: SolarSystemId(4) }, + MockConn { conn_type: ConnType::Jump, s1: SolarSystemId(3), s2: SolarSystemId(4) }, ]; let mut star_map: HashMap = stars @@ -245,14 +290,14 @@ mod tests { #[test] fn test_distance() { let a = Star { - id: 1, + id: SolarSystemId(1), x: 0.0, y: 0.0, z: 0.0, ..Default::default() }; let b = Star { - id: 2, + id: SolarSystemId(2), x: 1.0, y: 0.0, z: 0.0, @@ -271,37 +316,37 @@ mod tests { id: 1, conn_type: ConnType::Jump, distance: Meters::new(2.0), - target: 1, + target: SolarSystemId(2), }; let b = Connection { id: 2, conn_type: ConnType::NpcGate, distance: Meters::new(2.0), - target: 1, + target: SolarSystemId(1), }; let c = Connection { id: 3, conn_type: ConnType::SmartGate, distance: Meters::new(2.0), - target: 1, + target: SolarSystemId(1), }; let d = Connection { id: 4, conn_type: ConnType::Jump, distance: Meters::new(1.0), - target: 1, + target: SolarSystemId(1), }; let e = Connection { id: 5, conn_type: ConnType::NpcGate, distance: Meters::new(1.0), - target: 1, + target: SolarSystemId(1), }; let f = Connection { id: 6, conn_type: ConnType::SmartGate, distance: Meters::new(1.0), - target: 1, + target: SolarSystemId(1), }; let mut conns = vec![ a.clone(), @@ -318,11 +363,11 @@ mod tests { #[test] fn test_tiny_test_star1_sorting() { let universe = Universe::tiny_test(); - let star1 = &universe.star_map[&1]; + let star1 = &universe.star_map[&SolarSystemId(1)]; // First connection should be the NPC gate to star 4 assert_eq!(star1.connections[0].conn_type, ConnType::NpcGate); - assert_eq!(star1.connections[0].target, 4); + assert_eq!(star1.connections[0].target, SolarSystemId(4)); // All NPC gates should come before all jumps let mut seen_jump = false;