test(simulation): lock standalone-HQ-as-ordinary-settlement-row invariant (T-1074)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -828,6 +828,76 @@ mod tests {
|
||||
assert!(p2.synthetic);
|
||||
}
|
||||
|
||||
/// D-242: "hubs are the significant cities, standalone HQs are typically
|
||||
/// minor nodes." `match_cities` has no concept of "corp-originated"
|
||||
/// settlement — a Standalone-HQ CityRecord competes on population/class
|
||||
/// exactly like an ordinary pooled city, so a modest-population HQ must
|
||||
/// NOT out-rank a genuinely major city for the best attractor, and a
|
||||
/// major HQ (large population, or NameLocked) DOES win Tier A on the same
|
||||
/// terms any other settlement would. Both directions verified here.
|
||||
#[test]
|
||||
fn standalone_hq_settlement_competes_on_equal_terms_with_pooled_cities() {
|
||||
let major_city = make_city(1, SettlementClass::PopulationBudget, 5_000_000);
|
||||
// A "minor node" Standalone HQ (D-242's own framing) — modest
|
||||
// population, ordinary PopulationBudget class, nothing marking it as
|
||||
// corp-originated (mirrors populate_standalone_hq_settlements' output
|
||||
// shape: an atlas_city_names row with no corp_id, same as any city).
|
||||
let minor_hq = make_city(2, SettlementClass::PopulationBudget, 80_000);
|
||||
let cities = vec![major_city, minor_hq];
|
||||
let attractors = vec![
|
||||
make_attractor(5, 5, AttractorType::RiverMouth, 95), // best
|
||||
make_attractor(10, 10, AttractorType::ValleyFloor, 30), // worst
|
||||
];
|
||||
let matrix = uniform_matrix();
|
||||
let placements = match_cities(
|
||||
&cities,
|
||||
&attractors,
|
||||
&matrix,
|
||||
None,
|
||||
512,
|
||||
256,
|
||||
&TerritorialStatus::FrontierUnclaimed,
|
||||
SeedChain::root(42),
|
||||
);
|
||||
let major = placements.iter().find(|p| p.city_id == 1).unwrap();
|
||||
let minor = placements.iter().find(|p| p.city_id == 2).unwrap();
|
||||
assert_eq!(
|
||||
major.position,
|
||||
(5, 5),
|
||||
"the major city (5M pop) must win the best attractor over an 80k-pop HQ"
|
||||
);
|
||||
assert_eq!(
|
||||
minor.position,
|
||||
(10, 10),
|
||||
"the minor HQ gets whatever's left — no special corp-origin priority"
|
||||
);
|
||||
|
||||
// Flip it: a NameLocked HQ (the T-1075 hero-pin path — e.g. "The Gate
|
||||
// Corporation" in the real data) DOES win Tier A, on the same
|
||||
// NameLocked-priority terms as any hero-pinned city (tier_a_gets_priority,
|
||||
// above) — population size never mattered for NameLocked, corp-origin
|
||||
// doesn't add or subtract anything either.
|
||||
let hero_hq = make_city(3, SettlementClass::NameLocked, 200_000);
|
||||
let ordinary_city = make_city(4, SettlementClass::PopulationBudget, 3_000_000);
|
||||
let cities2 = vec![hero_hq, ordinary_city];
|
||||
let placements2 = match_cities(
|
||||
&cities2,
|
||||
&attractors,
|
||||
&matrix,
|
||||
None,
|
||||
512,
|
||||
256,
|
||||
&TerritorialStatus::FrontierUnclaimed,
|
||||
SeedChain::root(42),
|
||||
);
|
||||
let hero = placements2.iter().find(|p| p.city_id == 3).unwrap();
|
||||
assert_eq!(
|
||||
hero.position,
|
||||
(5, 5),
|
||||
"NameLocked HQ wins Tier A over a larger ordinary city — same rule as any NameLocked settlement"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_cities_placed() {
|
||||
let cities: Vec<CityRecord> = (1..=5)
|
||||
|
||||
Reference in New Issue
Block a user