magicciv/.project/objectives/p3-13d-anomalous-events.md
Natalie b38827d48e fix(@projects/@magic-civilization): 🐛 update p2-46 status to done
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-07 16:54:54 -07:00

5.3 KiB

id title priority status scope owner updated_at evidence blocked_by
p3-13d Anomalous events — aurora, fog_bank, thermal_anomaly p3 done game1 unassigned 2026-05-07
src/simulator/crates/mc-climate/src/anomalous.rs:33-66 (AnomalousEvent enum)
src/simulator/crates/mc-climate/src/anomalous.rs:165 (derive_anomalous_events)
src/simulator/crates/mc-climate/src/anomalous.rs:171-179 (aurora latitude gate)
src/simulator/crates/mc-climate/src/anomalous.rs:228-249 (thermal z-score gate)
cargo test -p mc-climate anomalous: 4/4 pass (test_aurora_high_latitude_low_humidity, test_fog_bank_humid_cool_with_cooldown, test_thermal_anomaly_z_threshold_and_volcanic_bonus, thresholds_load_from_spec_json)
src/simulator/crates/mc-observation/src/fog.rs (apply_fog, is_fogged, prune_expired, ActiveFog)
cargo test -p mc-observation -- fog: 4/4 pass (apply_fog_reduces_sight, fog_dissipates_over_turns, apply_fog_extends_existing_expiry, prune_expired_keeps_active_entries)
src/simulator/crates/mc-sim/src/event_dispatch.rs:159-172 — FogBank → apply_fog(flat_idx, duration, current_turn, &mut state.fog_map) dispatch wired in mc-sim; GameState::fog_map added (HashMap<u16, ActiveFog>, persisted in save)
cargo test -p mc-sim p3_13_event_dispatch_anomalous_fog_populates_fog_map: PASS

Context

The fourth event family in public/games/age-of-dwarves/docs/terrain/CLIMATE.md covers anomalous/atmospheric phenomena that surface as flavour and observation hooks (per WEATHER_HISTORY.md's anomaly lens) without significant gameplay damage in Game 1: aurora (high-latitude visual), fog_bank (visibility reduction over N turns on humid tiles), thermal_anomaly (statistical outlier flagged by the anomaly lens).

Acceptance

  • mc-climate::anomalous::derive_anomalous_events(grid, thresholds, turn, seed, temp_history) -> Vec<AnomalousEvent> emits the three event types per documented rules. Source: src/simulator/crates/mc-climate/src/anomalous.rs:165 (function declaration).
  • ✓ Typed enum AnomalousEvent with variants Aurora { col, row }, FogBank { col, row, duration }, ThermalAnomaly { col, row, z_score }. Source: src/simulator/crates/mc-climate/src/anomalous.rs:33-66. (Lives in mc-climate rather than mc-core::events — mirrors the sibling p3-13a WeatherEvent placement; mc-core::events module does not yet exist. See follow-up.)
  • ✓ Fog bank applies a temporary visibility reduction via mc-observation::apply_fog(tile_idx, duration, current_turn, fog); expires automatically via lazy expiry on is_fogged query. Source: src/simulator/crates/mc-observation/src/fog.rs. Verified by cargo test -p mc-observation -- fog: 4/4 pass. Consumer dispatch wired in mc-sim::event_dispatch::dispatch_world_events — FogBank → apply_fog(flat_idx, duration, current_turn, &mut state.fog_map). GameState::fog_map: HashMap<u16, ActiveFog> added to mc-turn (mc-observation has no mc-turn dep, so no cycle). Covered by p3_13_event_dispatch_anomalous_fog_populates_fog_map. (src/simulator/crates/mc-sim/src/event_dispatch.rs:159-172)
  • ✓ Thermal anomaly emits only when the tile's temperature z-score exceeds the configured threshold. z-score is computed from a temp_history: Option<&[Vec<f32>]> arg supplied by the caller (an explicit slice rather than the &ObservationStore plumb-through described in the original acceptance — see follow-up). Source: src/simulator/crates/mc-climate/src/anomalous.rs:228-249. Threshold gate verified by test_thermal_anomaly_z_threshold_and_volcanic_bonus.
  • ✓ Aurora rolls only on tiles above the latitudinal cutoff. Source: src/simulator/crates/mc-climate/src/anomalous.rs:171-179. Cutoff loaded from climate_spec.json → anomalous.thresholds.aurora_latitude_min (default 0.75 = polar). Verified by test_aurora_high_latitude_low_humidity. (Config currently lives in climate_spec.json rather than climate/_config.json; see follow-up.)
  • cargo test -p mc-climate green; 4 new tests under anomalous::tests: test_aurora_high_latitude_low_humidity, test_fog_bank_humid_cool_with_cooldown, test_thermal_anomaly_z_threshold_and_volcanic_bonus, thresholds_load_from_spec_json. The three name-mandated tests cover each variant + determinism + cooldown + volcanic bonus + spec loading.

Source-of-truth rails

  • Rust crate: mc-climate::anomalous owns event derivation and threshold loading.
  • JSON path: climate_spec.json → anomalous.thresholds (loaded via AnomalousThresholds::from_spec). Sibling WeatherThresholds uses the same spec file under a sibling key.
  • Determinism: splitmix64 det_roll(seed, turn, col, row, channel) with channels 101/102/103 (disjoint from weather channels 1-6). FogBank uses a cooldown_bucket = turn / cooldown_turns so a fired tile cannot re-fire within the cooldown window without changing the deterministic roll.

Out of scope

  • Magical anomalies (leylines etc.) — Game 2/3.
  • Player ability to dispel fog — magical ticket.
  • Aurora-as-victory-condition — out of scope.
  • Wiring mc-observation::apply_fog and the 30-day rolling-mean plumb-through — separate tickets (see follow-ups).

References

  • public/games/age-of-dwarves/docs/terrain/CLIMATE.md
  • public/games/age-of-dwarves/docs/terrain/WEATHER_HISTORY.md
  • Sibling: p3-13a (closed; mc-climate::weather template followed here), p3-13b, p3-13c