From cf6ef2bc31ccdf46be70962c2c0f34f3d2a43346 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Thu, 16 Jul 2026 22:26:57 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(simulation):=20district-window=20zoom-?= =?UTF-8?q?ladder=20renderer=20=E2=80=94=20aliveness=5Fprobe=20--render/--?= =?UTF-8?q?window/--anchor=20(T-1123)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renders true-2km district maps (derive_district per cell, NOT the coarse eager grid) as PNGs: morphology (17-zone palette transliterated from atlas_marker_overlay.gd, sync-commented), elev/temp/moisture/vegetation panels, anchor crosshair, km-honest filenames + manifest. believability.rs gains cascade_snapshot_for_body (cascade_for_body now a thin wrapper); probe re-derives the D-203-freed TerrainAnalysis via run_layer1 only under --render. Determinism proven: 5/5 panels byte-identical across passes AND cross-process (md5s reproduced in a fresh run). ~8us/district debug. First-ever look at the district layer produced the T-1125/T-1126/T-1127 findings (D-227 seam ends at ~500km; ocean-blind vegetation; unrendered glaciation). Co-Authored-By: Claude Fable 5 --- server/src/atlas/believability.rs | 27 +- server/src/bin/aliveness_probe.rs | 423 +++++++++++++++++++++++++++++- 2 files changed, 440 insertions(+), 10 deletions(-) diff --git a/server/src/atlas/believability.rs b/server/src/atlas/believability.rs index 2dbce82f9..1cf14b7f1 100644 --- a/server/src/atlas/believability.rs +++ b/server/src/atlas/believability.rs @@ -45,9 +45,9 @@ use serde::{Deserialize, Serialize}; use crate::atlas::attractor_matching::CityRecord; use crate::atlas::body_params_reader::BodyParamsReader; use crate::atlas::body_world_state::BodyWorldState; -use crate::atlas::cascade::{run_cascade_from_heightmap, CascadeLayer}; +use crate::atlas::cascade::{run_cascade_from_heightmap, CascadeLayer, CascadeSnapshot}; use crate::atlas::chunk_context::derive_chunk_context; -use crate::atlas::district_profile::DistrictProfile; +use crate::atlas::district_profile::{BodyParams, DistrictProfile}; use crate::atlas::heightmap::{load_heightmap_png, GRID_H, GRID_W}; use crate::atlas::scale::{ChunkPos, DistrictPos, CHUNKS_PER_DISTRICT, CHUNK_M}; use crate::atlas::voxel::{derive_voxel_column, Vegetation, Water}; @@ -508,9 +508,26 @@ pub fn seed_to_u64(seed: &str) -> u64 { /// /// `Err` if `systems.db` or the body's `heightmap.png` cannot be found, or the body /// has no params — callers (the regression harness) may *skip* on that rather than -/// fail, so this is the one believability entry point that does I/O. Tries both the -/// repo-root and `server/`-relative paths so it works from either CWD. +/// fail, so the `*_for_body` loaders are the believability entry points that do I/O. +/// Tries both the repo-root and `server/`-relative paths so it works from either CWD. pub fn cascade_for_body(world_seed: u64, body_id: &str) -> Result { + cascade_snapshot_for_body(world_seed, body_id) + .map(|(snapshot, _params)| snapshot.into_body_world_state()) +} + +/// Like [`cascade_for_body`], but returns the raw [`CascadeSnapshot`] — which still +/// carries the transient `TerrainAnalysis` — together with the body's [`BodyParams`]. +/// +/// This is the entry point for callers that need the *on-demand* district-derivation +/// inputs (`district_profile::derive_district` wants `&TerrainAnalysis` + +/// `&BodyParams`), e.g. the aliveness probe's `--render` district-window mode +/// (T-1123). [`cascade_for_body`] is this plus the `BodyWorldState` conversion, +/// which drops the TerrainAnalysis per the D-203/T-1048 size budget — take it off +/// the snapshot *before* converting. +pub fn cascade_snapshot_for_body( + world_seed: u64, + body_id: &str, +) -> Result<(CascadeSnapshot, BodyParams), String> { let db = first_existing(&["server/data/systems.db", "data/systems.db"]) .ok_or_else(|| "systems.db not found".to_string())?; let hm_path = @@ -538,7 +555,7 @@ pub fn cascade_for_body(world_seed: u64, body_id: &str) -> Result` — district-window maps (T-1123) +//! +//! Renders an N×N window (`--window`, default 64) of **true 2 km districts** +//! centred on the body's principal settlement, one PNG per attribute panel +//! (morphology / elev / temp / moisture / veg), via the pure on-demand +//! [`derive_district`] path. The orbit→jet-plane bridge demonstrator: nobody +//! had ever *seen* the district layer before this. +//! +//! ```sh +//! cargo run --bin aliveness_probe -- --body GJ380c --seed 42 \ +//! --render /tmp/district-windows --window 64 +//! ``` use std::collections::BTreeMap; use settled_reach_server::atlas::believability::{ - analyze, cascade_for_body, evaluate_criteria, seed_to_u64, BelievabilityReport, + analyze, cascade_snapshot_for_body, evaluate_criteria, seed_to_u64, BelievabilityReport, }; +use settled_reach_server::atlas::body_world_state::BodyWorldState; use settled_reach_server::atlas::chunk_context::derive_chunk_context; -use settled_reach_server::atlas::district_profile::DistrictProfile; +use settled_reach_server::atlas::district_profile::{ + derive_district, BodyParams, ClimateConstants, DistrictProfile, VegetationClass, +}; +use settled_reach_server::atlas::features::TerrainAnalysis; use settled_reach_server::atlas::scale::{ self, ChunkPos, DistrictPos, CHUNKS_PER_DISTRICT, CHUNK_M, }; use settled_reach_server::atlas::voxel::derive_voxel_column; +use settled_reach_server::seed::SeedChain; +use settled_reach_server::simulation::generator::MorphologyZone; /// 150 chunks (the original question's offset) in metres = 9 600 m. const ANCHOR_OFFSET_CHUNKS: i32 = 150; @@ -40,13 +59,29 @@ fn main() { args.body, args.seed, args.probes ); - let bws = match cascade_for_body(world_seed, &args.body) { - Ok(b) => b, + let (mut snapshot, body_params) = match cascade_snapshot_for_body(world_seed, &args.body) { + Ok(x) => x, Err(e) => { eprintln!("cannot run cascade for {}: {e}", args.body); std::process::exit(1); } }; + // The transient TerrainAnalysis is the bilinear-envelope input the on-demand + // derive_district path (--render) needs for true 2 km districts. The cascade + // frees it once its own consumers (DistrictProfile + RoadGraph) have run + // (D-203/T-1048 size budget), so at Region depth `take()` yields None and we + // re-derive from the working heightmap — the path CascadeSnapshot documents + // ("callers that need it after the cascade must re-derive from run_layer1"). + // Must happen BEFORE into_body_world_state() moves the heightmap out. + let terrain = match snapshot.terrain_analysis.take() { + Some(ta) => Some(ta), + None if args.render.is_some() => { + Some(settled_reach_server::atlas::layer1::run_layer1(&snapshot.heightmap).1) + } + None => None, + }; + let body_seed = snapshot.seed; + let bws = snapshot.into_body_world_state(); let districts = &bws.districts; if districts.is_empty() { @@ -110,6 +145,16 @@ fn main() { } println!("\n(district tier — voxel addressing is not production-wired yet; voxel rows are illustrative ground-truth for a representative chunk of each district.)"); + + // ── --render: district-window PNG maps (T-1123) ────────────────────────── + if let Some(out_dir) = &args.render { + match &terrain { + Some(ta) => render_district_windows(out_dir, &args, body_seed, &body_params, ta, &bws), + None => eprintln!( + "--render: cascade retained no TerrainAnalysis — cannot render district windows." + ), + } + } } /// Print the body-level believability report + the advisory D-245 criteria — the @@ -254,6 +299,354 @@ fn nearest_present( None } +// --------------------------------------------------------------------------- +// --render: district-window PNG maps (T-1123) +// --------------------------------------------------------------------------- +// +// N×N windows of TRUE 2 km districts around the principal settlement, one PNG +// per attribute panel, straight from the pure on-demand `derive_district` +// path. Deliberately NOT `bws.districts`: that eager map is the coarse +// working-grid pseudo-grid (gcpr = 8 heightmap cells per "district", tens of +// km per cell) — the whole point here is true 2 km fidelity. + +/// `MorphologyZone` discriminant → RGB. Transliterated from the client overlay +/// palette — `client/ui/implant/apps/atlas/atlas_marker_overlay.gd`, +/// `MORPHOLOGY_COLORS` (D-239 §6 discriminant order, T-1046) — which is the +/// source of truth; **keep the two in sync** (same order, same 17 entries). +/// The client's 0.55 alpha is dropped: these PNGs are opaque, no heightmap +/// underlay to blend with. +const MORPHOLOGY_RGB: [[u8; 3]; 17] = [ + [26, 51, 115], // 0 OpenOcean + [51, 102, 166], // 1 Lake + [115, 140, 128], // 2 TidalFlat + [217, 199, 115], // 3 DuneStrand + [128, 128, 140], // 4 CliffCoast + [77, 102, 128], // 5 Fjord + [102, 166, 153], // 6 Delta + [77, 140, 140], // 7 Estuarine + [77, 153, 77], // 8 AlluvialPlain + [115, 179, 102], // 9 RiverBank + [89, 153, 128], // 10 MeanderReach + [140, 153, 115], // 11 BraidedPlain + [128, 140, 77], // 12 ValleyFloor + [140, 115, 77], // 13 MountainPass + [204, 209, 217], // 14 Alpine + [115, 38, 31], // 15 Volcanic + [64, 115, 102], // 16 Wetland +]; + +/// The five attribute panels, in output order. +const PANEL_NAMES: [&str; 5] = ["morphology", "elev", "temp", "moisture", "veg"]; + +/// Render the district window around the principal settlement and write one PNG +/// per panel to `out_dir`, plus a one-line manifest per image and a determinism +/// spot-proof (two full derive+encode passes must be byte-identical — D-010 in +/// miniature). +fn render_district_windows( + out_dir: &str, + args: &Args, + body_seed: SeedChain, + params: &BodyParams, + ta: &TerrainAnalysis, + bws: &BodyWorldState, +) { + // Anchor: `--anchor ` (case-insensitive name match) when given, + // else the capital placement when one exists, else the most populous, + // else the best-scoring. + let named = args.anchor.as_ref().and_then(|pat| { + let pat_lower = pat.to_lowercase(); + let found = bws + .placements + .iter() + .find(|p| p.name.to_lowercase().contains(&pat_lower)); + if found.is_none() { + eprintln!( + "--anchor \"{pat}\": no placement name matches — using the principal settlement" + ); + } + found + }); + let Some(anchor) = named.or_else(|| { + bws.placements + .iter() + .max_by_key(|p| (p.is_capital, p.population, p.score)) + }) else { + eprintln!("--render: body has no settlement placements — nothing to anchor a window on."); + return; + }; + let centre = true_district_of_pixel(anchor.position, ta.w, ta.h, params.body_radius_km); + if let Err(e) = std::fs::create_dir_all(out_dir) { + eprintln!("--render: cannot create {out_dir}: {e}"); + return; + } + + let n = args.window.max(2); + let extent_km = n as f64 * scale::DISTRICT_M as f64 / 1000.0; + let climate = ClimateConstants::default(); + + println!("\n---- DISTRICT WINDOW RENDER (T-1123) ----"); + println!( + "anchor: {} (capital={}, pop={}) @ working pixel {:?} → true district ({}, {})", + anchor.name, anchor.is_capital, anchor.population, anchor.position, centre.0, centre.1 + ); + println!( + "window: {n}×{n} districts = {extent_km:.1}×{extent_km:.1} km @ 2.048 km/px (seed \"{}\")", + args.seed + ); + + // Determinism spot-proof: two independent full passes, byte-compared. + let t0 = std::time::Instant::now(); + let first = render_window_panels(body_seed, &bws.body_id, params, ta, centre, n, &climate); + let pass1_ms = t0.elapsed().as_millis(); + let t1 = std::time::Instant::now(); + let second = render_window_panels(body_seed, &bws.body_id, params, ta, centre, n, &climate); + let pass2_ms = t1.elapsed().as_millis(); + let identical = first + .iter() + .zip(second.iter()) + .filter(|((_, a), (_, b))| a == b) + .count(); + + let body_lower = args.body.to_lowercase(); + for (panel, png_bytes) in &first { + let file = format!("{out_dir}/{body_lower}_w{n}_{panel}.png"); + if let Err(e) = std::fs::write(&file, png_bytes) { + eprintln!("--render: write {file}: {e}"); + continue; + } + println!( + "RENDER body={} seed=\"{}\" window={n} extent_km={extent_km:.1} \ + anchor_city=\"{}\" anchor_district=({},{}) panel={panel} file={file}", + args.body, args.seed, anchor.name, centre.0, centre.1 + ); + } + println!( + "DETERMINISM: {identical}/{} panels byte-identical across two full derive+encode passes \ + [{}] — pass1 {pass1_ms} ms, pass2 {pass2_ms} ms ({} district derivations/pass)", + first.len(), + if identical == first.len() { + "OK" + } else { + "MISMATCH" + }, + (n as u64) * (n as u64), + ); +} + +/// Derive every district in the window and encode the five attribute panels +/// as PNG byte vectors (encode included so the determinism proof covers the +/// full derive→encode pipeline). +fn render_window_panels( + seed: SeedChain, + body_id: &str, + params: &BodyParams, + ta: &TerrainAnalysis, + centre: DistrictPos, + n: u32, + climate: &ClimateConstants, +) -> Vec<(&'static str, Vec)> { + let n_i = n as i32; + let half = n_i / 2; + let mut bufs: [Vec; 5] = std::array::from_fn(|_| vec![0u8; (n * n) as usize * 3]); + for row in 0..n_i { + for col in 0..n_i { + // Row 0 = northmost: smaller dy = further north (derive_district maps + // negative wy to negative lat_frac = northern latitudes). + let dp = (centre.0 - half + col, centre.1 - half + row); + let prof = derive_district(seed, body_id, params, ta, dp, climate); + let i = ((row * n_i + col) * 3) as usize; + set_rgb(&mut bufs[0], i, morphology_rgb(prof.morphology_zone)); + set_rgb(&mut bufs[1], i, gray_rgb(prof.elev_q)); + set_rgb(&mut bufs[2], i, temperature_rgb(prof.temperature_c)); + set_rgb(&mut bufs[3], i, moisture_rgb(prof.moisture_q)); + set_rgb(&mut bufs[4], i, vegetation_rgb(prof.vegetation_class)); + } + } + for buf in &mut bufs { + draw_anchor_crosshair(buf, n_i, half, half); + } + PANEL_NAMES + .iter() + .zip(bufs) + .map(|(name, buf)| (*name, encode_rgb_png(n, n, &buf))) + .collect() +} + +/// Map a working-grid settlement pixel `(row, col)` to the TRUE metric 2 km +/// district containing it — the inverse of `derive_district`'s forward mapping +/// (district → fractional working-grid position via the body radius, the D-204 +/// elastic seam). NOT `scale::heightmap_pixel_to_district`: that addresses the +/// eager gcpr = 8 pseudo-grid, whose cells span tens of km. +fn true_district_of_pixel( + pixel: (u16, u16), + ta_w: usize, + ta_h: usize, + radius_km: Option, +) -> DistrictPos { + let (row, col) = (pixel.0 as f64, pixel.1 as f64); + match radius_km { + Some(r) if r > 0.0 => { + let circumference_m = std::f64::consts::TAU * r * 1000.0; + let meridian_m = std::f64::consts::PI * r * 1000.0; + let wx = col / ta_w.max(1) as f64 * circumference_m; + let lat_frac = if ta_h > 1 { + row / (ta_h - 1) as f64 - 0.5 + } else { + 0.0 + }; + let wy = lat_frac * meridian_m; + ( + (wx / scale::DISTRICT_M as f64).round() as i32, + (wy / scale::DISTRICT_M as f64).round() as i32, + ) + } + // No radius: the district grid IS the working grid (derive_district's + // tiny-test-body fallback) — the pixel is the district. + _ => (col as i32, row as i32), + } +} + +#[inline] +fn set_rgb(buf: &mut [u8], i: usize, c: [u8; 3]) { + buf[i..i + 3].copy_from_slice(&c); +} + +fn morphology_rgb(zone: MorphologyZone) -> [u8; 3] { + // Out-of-palette discriminant (enum grew, palette didn't) → magenta sentinel, + // and the unit test below fails until both palettes are extended. + *MORPHOLOGY_RGB + .get(zone as u8 as usize) + .unwrap_or(&[255, 0, 255]) +} + +/// `elev_q` 0–100 → 8-bit grayscale. +fn gray_rgb(elev_q: i32) -> [u8; 3] { + let g = (elev_q.clamp(0, 100) * 255 / 100) as u8; + [g, g, g] +} + +/// Heat ramp over −60…+40 °C (clamped; piecewise blue→cyan→yellow→red). +/// Airless bodies (`temperature_c == None`, D-239 §2) render as a magenta +/// sentinel — "no atmosphere" must never be readable as a temperature. +fn temperature_rgb(t: Option) -> [u8; 3] { + const STOPS: [(f32, [f32; 3]); 4] = [ + (-60.0, [0.0, 0.0, 255.0]), + (-20.0, [0.0, 255.0, 255.0]), + (10.0, [255.0, 255.0, 0.0]), + (40.0, [255.0, 0.0, 0.0]), + ]; + let Some(t) = t else { return [255, 0, 255] }; + let t = t.clamp(STOPS[0].0, STOPS[STOPS.len() - 1].0); + for w in STOPS.windows(2) { + let (t0, c0) = w[0]; + let (t1, c1) = w[1]; + if t <= t1 { + return lerp_rgb(c0, c1, ((t - t0) / (t1 - t0)).clamp(0.0, 1.0)); + } + } + [255, 0, 0] +} + +/// Dry→wet ramp: parched tan → grass green → deep water blue (`moisture_q` 0–100). +fn moisture_rgb(q: i32) -> [u8; 3] { + let q = q.clamp(0, 100) as f32; + if q <= 50.0 { + lerp_rgb([150.0, 110.0, 70.0], [90.0, 160.0, 90.0], q / 50.0) + } else { + lerp_rgb([90.0, 160.0, 90.0], [30.0, 90.0, 200.0], (q - 50.0) / 50.0) + } +} + +/// Categorical vegetation palette ([`VegetationClass`], D-239 §8 ladder). +fn vegetation_rgb(v: VegetationClass) -> [u8; 3] { + match v { + VegetationClass::Absent => [40, 40, 40], // airless — near-black + VegetationClass::Barren => [160, 140, 110], // bare-ground tan + VegetationClass::Scrub => [140, 160, 80], // olive + VegetationClass::Forest => [30, 110, 40], // closed canopy + VegetationClass::RiparianScrub => [70, 170, 120], // waterway band (open) + VegetationClass::RiparianThicket => [10, 130, 90], // waterway band (dense) + } +} + +fn lerp_rgb(a: [f32; 3], b: [f32; 3], t: f32) -> [u8; 3] { + let mix = |i: usize| (a[i] + (b[i] - a[i]) * t).round().clamp(0.0, 255.0) as u8; + [mix(0), mix(1), mix(2)] +} + +/// White crosshair (±3 px arms) with darkened diagonal shoulders at the anchor +/// pixel, so the marker stays visible on dark (ocean) and light (alpine) panels +/// alike and the window orients against the planetary Atlas shots. +fn draw_anchor_crosshair(buf: &mut [u8], n: i32, cx: i32, cy: i32) { + let mut put = |x: i32, y: i32, c: [u8; 3]| { + if x >= 0 && x < n && y >= 0 && y < n { + set_rgb(buf, ((y * n + x) * 3) as usize, c); + } + }; + for (dx, dy) in [(-1, -1), (1, -1), (-1, 1), (1, 1)] { + put(cx + dx, cy + dy, [0, 0, 0]); + } + for d in -3i32..=3 { + put(cx + d, cy, [255, 255, 255]); + put(cx, cy + d, [255, 255, 255]); + } +} + +/// Encode an 8-bit RGB buffer as PNG bytes (pattern from `heightmap.rs`). +fn encode_rgb_png(w: u32, h: u32, rgb: &[u8]) -> Vec { + let mut out = Vec::new(); + { + let mut enc = png::Encoder::new(&mut out, w, h); + enc.set_color(png::ColorType::Rgb); + enc.set_depth(png::BitDepth::Eight); + let mut writer = enc.write_header().expect("png header"); + writer.write_image_data(rgb).expect("png data"); + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + + /// The palette is indexed by `MorphologyZone` discriminant (repr(u8), + /// append-only). Pinning the LAST variant's discriminant against the + /// palette length forces a palette extension — here AND in the client + /// source of truth (`atlas_marker_overlay.gd::MORPHOLOGY_COLORS`) — when + /// a zone is appended. + #[test] + fn morphology_palette_covers_every_zone() { + assert_eq!(MorphologyZone::Wetland as usize + 1, MORPHOLOGY_RGB.len()); + } + + /// `true_district_of_pixel` must invert `derive_district`'s forward mapping + /// (district → fractional working-grid position via the body radius) to + /// within the half-district rounding error — ≲0.01 working pixel at + /// planetary radii. + #[test] + fn true_district_inverts_forward_mapping() { + let (w, h) = (128usize, 64usize); + let r_km = 6371.0; + let circ_m = std::f64::consts::TAU * r_km * 1000.0; + let mer_m = std::f64::consts::PI * r_km * 1000.0; + for (row, col) in [(10u16, 100u16), (32, 0), (5, 127), (63, 64)] { + let (dx, dy) = true_district_of_pixel((row, col), w, h, Some(r_km)); + let px = ((dx as f64 * scale::DISTRICT_M as f64) / circ_m).rem_euclid(1.0) * w as f64; + let py = (0.5 + ((dy as f64 * scale::DISTRICT_M as f64) / mer_m).clamp(-0.5, 0.5)) + * (h - 1) as f64; + assert!((px - col as f64).abs() < 0.05, "px {px} vs col {col}"); + assert!((py - row as f64).abs() < 0.05, "py {py} vs row {row}"); + } + } + + /// Without a radius the fallback maps pixel → district 1:1 (mirrors + /// `derive_district`'s tiny-test-body branch). + #[test] + fn true_district_fallback_is_identity() { + assert_eq!(true_district_of_pixel((7, 3), 128, 64, None), (3, 7)); + } +} + // --------------------------------------------------------------------------- // Args // --------------------------------------------------------------------------- @@ -262,6 +655,13 @@ struct Args { body: String, seed: String, probes: u32, + /// `--render ` — write district-window PNG panels (T-1123). + render: Option, + /// `--window ` — window side in districts (default 64 ≈ 131 km). + window: u32, + /// `--anchor ` — anchor the window on the placement whose name + /// matches (case-insensitive); default: capital, else most populous. + anchor: Option, } impl Args { @@ -269,15 +669,28 @@ impl Args { let mut body = "GJ338Bd".to_string(); let mut seed = "yolo".to_string(); let mut probes = 5u32; + let mut render = None; + let mut window = 64u32; + let mut anchor = None; let mut it = args.peekable(); while let Some(a) = it.next() { match a.as_str() { "--body" => body = it.next().unwrap_or(body), "--seed" => seed = it.next().unwrap_or(seed), "--probes" => probes = it.next().and_then(|s| s.parse().ok()).unwrap_or(probes), + "--render" => render = it.next(), + "--window" => window = it.next().and_then(|s| s.parse().ok()).unwrap_or(window), + "--anchor" => anchor = it.next(), _ => {} } } - Args { body, seed, probes } + Args { + body, + seed, + probes, + render, + window, + anchor, + } } } From 5e48c85a9a273e86b1b5ea90c3a7614351c78da4 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Thu, 16 Jul 2026 22:30:10 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fix(simulation):=20move=20probe=20tests=20m?= =?UTF-8?q?odule=20below=20Args=20=E2=80=94=20clippy=20items=5Fafter=5Ftes?= =?UTF-8?q?t=5Fmodule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- server/src/bin/aliveness_probe.rs | 84 +++++++++++++++---------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/server/src/bin/aliveness_probe.rs b/server/src/bin/aliveness_probe.rs index 2d0aabe19..74d5e1cad 100644 --- a/server/src/bin/aliveness_probe.rs +++ b/server/src/bin/aliveness_probe.rs @@ -605,48 +605,6 @@ fn encode_rgb_png(w: u32, h: u32, rgb: &[u8]) -> Vec { out } -#[cfg(test)] -mod tests { - use super::*; - - /// The palette is indexed by `MorphologyZone` discriminant (repr(u8), - /// append-only). Pinning the LAST variant's discriminant against the - /// palette length forces a palette extension — here AND in the client - /// source of truth (`atlas_marker_overlay.gd::MORPHOLOGY_COLORS`) — when - /// a zone is appended. - #[test] - fn morphology_palette_covers_every_zone() { - assert_eq!(MorphologyZone::Wetland as usize + 1, MORPHOLOGY_RGB.len()); - } - - /// `true_district_of_pixel` must invert `derive_district`'s forward mapping - /// (district → fractional working-grid position via the body radius) to - /// within the half-district rounding error — ≲0.01 working pixel at - /// planetary radii. - #[test] - fn true_district_inverts_forward_mapping() { - let (w, h) = (128usize, 64usize); - let r_km = 6371.0; - let circ_m = std::f64::consts::TAU * r_km * 1000.0; - let mer_m = std::f64::consts::PI * r_km * 1000.0; - for (row, col) in [(10u16, 100u16), (32, 0), (5, 127), (63, 64)] { - let (dx, dy) = true_district_of_pixel((row, col), w, h, Some(r_km)); - let px = ((dx as f64 * scale::DISTRICT_M as f64) / circ_m).rem_euclid(1.0) * w as f64; - let py = (0.5 + ((dy as f64 * scale::DISTRICT_M as f64) / mer_m).clamp(-0.5, 0.5)) - * (h - 1) as f64; - assert!((px - col as f64).abs() < 0.05, "px {px} vs col {col}"); - assert!((py - row as f64).abs() < 0.05, "py {py} vs row {row}"); - } - } - - /// Without a radius the fallback maps pixel → district 1:1 (mirrors - /// `derive_district`'s tiny-test-body branch). - #[test] - fn true_district_fallback_is_identity() { - assert_eq!(true_district_of_pixel((7, 3), 128, 64, None), (3, 7)); - } -} - // --------------------------------------------------------------------------- // Args // --------------------------------------------------------------------------- @@ -694,3 +652,45 @@ impl Args { } } } + +#[cfg(test)] +mod tests { + use super::*; + + /// The palette is indexed by `MorphologyZone` discriminant (repr(u8), + /// append-only). Pinning the LAST variant's discriminant against the + /// palette length forces a palette extension — here AND in the client + /// source of truth (`atlas_marker_overlay.gd::MORPHOLOGY_COLORS`) — when + /// a zone is appended. + #[test] + fn morphology_palette_covers_every_zone() { + assert_eq!(MorphologyZone::Wetland as usize + 1, MORPHOLOGY_RGB.len()); + } + + /// `true_district_of_pixel` must invert `derive_district`'s forward mapping + /// (district → fractional working-grid position via the body radius) to + /// within the half-district rounding error — ≲0.01 working pixel at + /// planetary radii. + #[test] + fn true_district_inverts_forward_mapping() { + let (w, h) = (128usize, 64usize); + let r_km = 6371.0; + let circ_m = std::f64::consts::TAU * r_km * 1000.0; + let mer_m = std::f64::consts::PI * r_km * 1000.0; + for (row, col) in [(10u16, 100u16), (32, 0), (5, 127), (63, 64)] { + let (dx, dy) = true_district_of_pixel((row, col), w, h, Some(r_km)); + let px = ((dx as f64 * scale::DISTRICT_M as f64) / circ_m).rem_euclid(1.0) * w as f64; + let py = (0.5 + ((dy as f64 * scale::DISTRICT_M as f64) / mer_m).clamp(-0.5, 0.5)) + * (h - 1) as f64; + assert!((px - col as f64).abs() < 0.05, "px {px} vs col {col}"); + assert!((py - row as f64).abs() < 0.05, "py {py} vs row {row}"); + } + } + + /// Without a radius the fallback maps pixel → district 1:1 (mirrors + /// `derive_district`'s tiny-test-body branch). + #[test] + fn true_district_fallback_is_identity() { + assert_eq!(true_district_of_pixel((7, 3), 128, 64, None), (3, 7)); + } +}