diff --git a/src/simulator/crates/mc-ecology/src/engine.rs b/src/simulator/crates/mc-ecology/src/engine.rs index ff6565ed..68e6cecc 100644 --- a/src/simulator/crates/mc-ecology/src/engine.rs +++ b/src/simulator/crates/mc-ecology/src/engine.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use mc_core::algorithms::hex; use mc_core::grid::GridState; @@ -418,7 +418,10 @@ impl EcologyEngine { // Pre-pass: count T10 slots per diet across all tiles. // Diets exceeding the cap get their terrain_tier_cap reduced to 9 this tick. - let mut t10_count_by_diet: HashMap = HashMap::new(); + // BTreeMap/BTreeSet used instead of HashMap/HashSet so iteration order is + // deterministic across processes — RandomState hashing would otherwise + // vary turn-to-turn and perturb float accumulation in downstream systems. + let mut t10_count_by_diet: BTreeMap = BTreeMap::new(); for slots in self.tile_populations.values() { for slot in slots { if slot.tier >= 10 { @@ -428,7 +431,7 @@ impl EcologyEngine { } } } - let saturated_diets: HashSet = t10_count_by_diet + let saturated_diets: BTreeSet = t10_count_by_diet .iter() .filter(|(_, &count)| count >= Self::GLOBAL_T10_PER_DIET_CAP) .map(|(&diet, _)| diet) diff --git a/src/simulator/crates/mc-ecology/src/traits.rs b/src/simulator/crates/mc-ecology/src/traits.rs index 3d22c224..6fcbe9e9 100644 --- a/src/simulator/crates/mc-ecology/src/traits.rs +++ b/src/simulator/crates/mc-ecology/src/traits.rs @@ -10,7 +10,7 @@ pub enum Size { Huge, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Diet { Producer,