fix(simulation): one absolute-metre derive core (D-256, T-1174)
derive_district_profile is now a thin wrapper over the shared derive_at_metres_with_riparian core at survey-cell-centre world metres — one derive core, two position sets. The batch pseudo-grid and the true D-243 district grid were two coordinate systems sharing one bare (i32,i32) type; the new SurveyCellPos newtype re-keys every batch product (BodyWorldState.districts, Layer1Output.survey_basin_dirs) so the compiler rejects cross-namespace passing. Fixes two latent same-position divergences the T-1174 investigation surfaced: three inconsistent latitude conventions collapse into the core's single inverse mapping, and the region-climate baseline now floor-divides true world metres instead of collapsing the whole body onto region (0,0)'s baseline — batch climate becomes latitude/region graded (D-245 direction: every changed believability metric increased). Binding preservations per D-256(c): basin_direction rides a post-call override with the true L1 D8 survey-cell aggregate (layer1's map re-keyed to SurveyCellPos, identity lookup — a floor-divide lookup against the pseudo-keyed map would have silently defaulted every cell North); the riparian verdict comes from near_perennial_water_at, never the empty-slice default (which would have flipped riverside vegetation_class). Quarter-skeleton morphology_zone now resolves at the settlement's exact world position via derive_at_metres at work-item execution (where TerrainAnalysisCache lives), replacing the survey-cell-centre map lookup (D-256(d)); settlement_district_pos fixed to true-district floor-division in passing (same doc/impl mismatch class). Second pixel-vs-metre conflation fixed in aliveness_probe's anchor-walk math. Window path byte-unchanged (window_derivation_golden 6/6 byte- identical); derivation_harness golden untouched; believability golden regenerated. Full lib + integration suites green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,11 +6,15 @@
|
||||
//! `atlas::believability` so this binary and `tests/believability_harness` measure the
|
||||
//! exact same thing.
|
||||
//!
|
||||
//! ## Why the *district* tier
|
||||
//! ## Why the *survey-cell* tier (D-256(b))
|
||||
//!
|
||||
//! No production code maps a world chunk → its covering `DistrictProfile` yet (the voxel
|
||||
//! layer is a walking skeleton), so analysis is addressed in district space; the voxel
|
||||
//! sample inside each district is illustrative ground-truth of what its 1 m tiles derive to.
|
||||
//! layer is a walking skeleton), so the body-level report is addressed in survey-cell
|
||||
//! space — a coarse planning raster, NOT the true D-243 district grid (see
|
||||
//! `settled_reach_server::atlas::scale::SurveyCellPos`); the voxel sample inside each
|
||||
//! survey cell (at its own D-256(b) centre world metres) is illustrative ground-truth of
|
||||
//! what its 1 m tiles derive to. The `--render` mode below is the true-district
|
||||
//! counterpart, at real 2 km fidelity via the on-demand `derive_district` path.
|
||||
//!
|
||||
//! ```sh
|
||||
//! cargo run --bin aliveness_probe -- --body GJ338Bd --seed yolo --probes 5
|
||||
@@ -37,13 +41,11 @@ use settled_reach_server::atlas::believability::{
|
||||
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::{
|
||||
derive_district, BodyParams, ClimateConstants, DistrictProfile, GlaciationGrade,
|
||||
self, derive_district, BodyParams, ClimateConstants, DistrictProfile, GlaciationGrade,
|
||||
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::scale::{self, ChunkPos, DistrictPos, SurveyCellPos, CHUNK_M};
|
||||
use settled_reach_server::atlas::voxel::derive_voxel_column;
|
||||
use settled_reach_server::seed::SeedChain;
|
||||
use settled_reach_server::simulation::generator::MorphologyZone;
|
||||
@@ -89,9 +91,9 @@ fn main() {
|
||||
eprintln!("cascade produced no districts — cannot probe (body params missing?). Aborting.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let keys: Vec<DistrictPos> = districts.keys().copied().collect();
|
||||
let keys: Vec<SurveyCellPos> = districts.keys().copied().collect();
|
||||
eprintln!(
|
||||
"cascade: {} districts, {} settlement placements",
|
||||
"cascade: {} survey cells, {} settlement placements",
|
||||
districts.len(),
|
||||
bws.placements.len()
|
||||
);
|
||||
@@ -102,38 +104,79 @@ fn main() {
|
||||
);
|
||||
|
||||
// ── Body-level believability report — the D-245 enforcer verdict ────────
|
||||
let report = analyze(world_seed, &args.body, districts);
|
||||
let report = analyze(
|
||||
world_seed,
|
||||
&args.body,
|
||||
districts,
|
||||
bws.heightmap_width,
|
||||
bws.heightmap_height,
|
||||
body_params.body_radius_km,
|
||||
);
|
||||
print_report(&report);
|
||||
|
||||
// ── Anchored probe: ≈150 chunks west of the principal settlement ────────
|
||||
// (highest placement score; per-city population is 0 in the DB).
|
||||
let anchor_district = bws.placements.iter().max_by_key(|p| p.score).map(|p| {
|
||||
let dp = scale::heightmap_pixel_to_district(p.position);
|
||||
// (highest placement score; per-city population is 0 in the DB). D-256(b):
|
||||
// `heightmap_pixel_to_survey_cell` — a survey-raster position, NOT a true
|
||||
// district — so "west" is walked in survey-cell units (each cell spans
|
||||
// HEIGHTMAP_CELLS_PER_DISTRICT working pixels), never true DISTRICT_M.
|
||||
let anchor_cell = bws.placements.iter().max_by_key(|p| p.score).map(|p| {
|
||||
let cell = scale::heightmap_pixel_to_survey_cell(p.position);
|
||||
println!(
|
||||
"\nprincipal settlement: city {} @ pixel {:?} → district {:?} (placement score {})",
|
||||
p.city_id, p.position, dp, p.score
|
||||
"\nprincipal settlement: city {} @ pixel {:?} → survey cell {:?} (placement score {})",
|
||||
p.city_id, p.position, cell, p.score
|
||||
);
|
||||
let west_districts = (ANCHOR_OFFSET_CHUNKS * CHUNK_M) / scale::DISTRICT_M;
|
||||
(dp.0 - west_districts.max(1) - 1, dp.1)
|
||||
// 150 chunks (9 600 m) ÷ one survey cell's ACTUAL metric width at this
|
||||
// row — measured directly via two adjacent cells' D-256(b) centre
|
||||
// world metres (the same bridge derive_district_profile uses), NOT
|
||||
// `HEIGHTMAP_CELLS_PER_DISTRICT * DISTRICT_M` (that formula silently
|
||||
// treats a working pixel as if it were DISTRICT_M metres wide, which
|
||||
// is only true in the no-radius fallback — on a real body a working
|
||||
// pixel's metric width is `circumference / heightmap_width`, unrelated
|
||||
// to DISTRICT_M; the earlier version was the SAME pseudo-grid/true-grid
|
||||
// conflation this ticket's basin-dirs fix corrected). Diagnostic-only
|
||||
// "west" nudge — exact enough to land roughly the requested distance
|
||||
// away, never used for anything position-authoritative.
|
||||
let gcpr = scale::HEIGHTMAP_CELLS_PER_DISTRICT;
|
||||
let (x0, _) = district_profile::survey_cell_centre_world_m(
|
||||
cell,
|
||||
gcpr,
|
||||
bws.heightmap_width as usize,
|
||||
bws.heightmap_height as usize,
|
||||
body_params.body_radius_km,
|
||||
);
|
||||
let (x1, _) = district_profile::survey_cell_centre_world_m(
|
||||
SurveyCellPos(cell.0 + 1, cell.1),
|
||||
gcpr,
|
||||
bws.heightmap_width as usize,
|
||||
bws.heightmap_height as usize,
|
||||
body_params.body_radius_km,
|
||||
);
|
||||
let cell_width_m = (x1 - x0).abs().max(1.0);
|
||||
let cell_width_chunks = (cell_width_m / CHUNK_M as f64).round() as i32;
|
||||
let west_cells = (ANCHOR_OFFSET_CHUNKS / cell_width_chunks.max(1)).max(1);
|
||||
SurveyCellPos(cell.0 - west_cells - 1, cell.1)
|
||||
});
|
||||
|
||||
if let Some(adp) = anchor_district {
|
||||
if let Some(acell) = anchor_cell {
|
||||
println!(
|
||||
"\n---- ANCHORED PROBE: ≈{ANCHOR_OFFSET_CHUNKS} chunks (9.6 km) west of the principal city ----"
|
||||
);
|
||||
match nearest_present(districts, adp) {
|
||||
Some((dp, prof)) => probe(
|
||||
match nearest_present(districts, acell) {
|
||||
Some((cell, prof)) => probe(
|
||||
&args.body,
|
||||
world_seed,
|
||||
dp,
|
||||
cell,
|
||||
bws.heightmap_width,
|
||||
bws.heightmap_height,
|
||||
body_params.body_radius_km,
|
||||
prof,
|
||||
if dp == adp {
|
||||
if cell == acell {
|
||||
""
|
||||
} else {
|
||||
"(nearest land district — target is off-map / open ocean)"
|
||||
"(nearest land survey cell — target is off-map / open ocean)"
|
||||
},
|
||||
),
|
||||
None => println!(" target {adp:?} and neighbours are off-map; nothing to sample."),
|
||||
None => println!(" target {acell:?} and neighbours are off-map; nothing to sample."),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,11 +184,20 @@ fn main() {
|
||||
println!("\n---- {} RANDOM PROBES ----", args.probes);
|
||||
for i in 0..args.probes {
|
||||
let pick = (seed_to_u64(&format!("{}/{i}", args.seed)) % keys.len() as u64) as usize;
|
||||
let dp = keys[pick];
|
||||
probe(&args.body, world_seed, dp, &districts[&dp], "");
|
||||
let cell = keys[pick];
|
||||
probe(
|
||||
&args.body,
|
||||
world_seed,
|
||||
cell,
|
||||
bws.heightmap_width,
|
||||
bws.heightmap_height,
|
||||
body_params.body_radius_km,
|
||||
&districts[&cell],
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
println!("\n(district tier — voxel addressing is not production-wired yet; voxel rows are illustrative ground-truth for a representative chunk of each district.)");
|
||||
println!("\n(survey-cell tier — voxel addressing is not production-wired yet; voxel rows are illustrative ground-truth for a representative chunk at each cell's centre.)");
|
||||
|
||||
// ── --render: district-window PNG maps (T-1123) ──────────────────────────
|
||||
if let Some(out_dir) = &args.render {
|
||||
@@ -201,12 +253,32 @@ fn print_report(r: &BelievabilityReport) {
|
||||
println!(" → {}/{} criteria pass", passes, crit.len());
|
||||
}
|
||||
|
||||
/// Probe one district: print its profile + a derived voxel-column sample for colour.
|
||||
fn probe(body: &str, world_seed: u64, dp: DistrictPos, prof: &DistrictProfile, note: &str) {
|
||||
// Representative chunk at the district centre (32 chunks / district).
|
||||
/// Probe one survey cell: print its profile + a derived voxel-column sample for
|
||||
/// colour, at the TRUE chunk covering the cell's own D-256(b) centre world
|
||||
/// metres (never the pseudo-grid coordinates scaled by `CHUNKS_PER_DISTRICT`
|
||||
/// directly — that arithmetic silently assumed the survey grid WAS the true
|
||||
/// district grid, exactly the D-256 namespace collision).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn probe(
|
||||
body: &str,
|
||||
world_seed: u64,
|
||||
dp: SurveyCellPos,
|
||||
heightmap_width: u32,
|
||||
heightmap_height: u32,
|
||||
body_radius_km: Option<f64>,
|
||||
prof: &DistrictProfile,
|
||||
note: &str,
|
||||
) {
|
||||
let (world_x_m, world_y_m) = district_profile::survey_cell_centre_world_m(
|
||||
dp,
|
||||
scale::HEIGHTMAP_CELLS_PER_DISTRICT,
|
||||
heightmap_width as usize,
|
||||
heightmap_height as usize,
|
||||
body_radius_km,
|
||||
);
|
||||
let chunk: ChunkPos = (
|
||||
dp.0 * CHUNKS_PER_DISTRICT + CHUNKS_PER_DISTRICT / 2,
|
||||
dp.1 * CHUNKS_PER_DISTRICT + CHUNKS_PER_DISTRICT / 2,
|
||||
(world_x_m / CHUNK_M as f64).floor() as i32,
|
||||
(world_y_m / CHUNK_M as f64).floor() as i32,
|
||||
);
|
||||
let ctx = derive_chunk_context(world_seed, body, prof, chunk, None);
|
||||
|
||||
@@ -232,7 +304,7 @@ fn probe(body: &str, world_seed: u64, dp: DistrictPos, prof: &DistrictProfile, n
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n• district {dp:?} {note}");
|
||||
println!("\n• survey cell {dp:?} {note}");
|
||||
println!(
|
||||
" zone={:?} elev_q={} slope_q={} ocean%q={} moisture_q={} temp={} precip={:?} veg_class={:?} glaciation={:?} basin={:?}",
|
||||
prof.morphology_zone,
|
||||
@@ -279,18 +351,18 @@ fn fmt_hist(h: &BTreeMap<String, u32>, n: i64) -> String {
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
/// Find the district at `dp`, else the nearest present district within a small ring.
|
||||
/// Find the survey cell at `dp`, else the nearest present cell within a small ring.
|
||||
fn nearest_present(
|
||||
districts: &BTreeMap<DistrictPos, DistrictProfile>,
|
||||
dp: DistrictPos,
|
||||
) -> Option<(DistrictPos, &DistrictProfile)> {
|
||||
districts: &BTreeMap<SurveyCellPos, DistrictProfile>,
|
||||
dp: SurveyCellPos,
|
||||
) -> Option<(SurveyCellPos, &DistrictProfile)> {
|
||||
if let Some(p) = districts.get(&dp) {
|
||||
return Some((dp, p));
|
||||
}
|
||||
for r in 1..=8 {
|
||||
for dx in -r..=r {
|
||||
for dy in -r..=r {
|
||||
let q = (dp.0 + dx, dp.1 + dy);
|
||||
let q = SurveyCellPos(dp.0 + dx, dp.1 + dy);
|
||||
if let Some(p) = districts.get(&q) {
|
||||
return Some((q, p));
|
||||
}
|
||||
@@ -493,8 +565,9 @@ fn render_window_panels(
|
||||
/// 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.
|
||||
/// elastic seam). NOT `scale::heightmap_pixel_to_survey_cell` (D-256(b)
|
||||
/// rename): that addresses the eager gcpr = 8 survey raster, whose cells span
|
||||
/// tens of km — a coarse planning grid, not the true district.
|
||||
fn true_district_of_pixel(
|
||||
pixel: (u16, u16),
|
||||
ta_w: usize,
|
||||
|
||||
Reference in New Issue
Block a user