diff --git a/src/simulator/crates/mc-city/src/city.rs b/src/simulator/crates/mc-city/src/city.rs index ef408748..d9eaf8d8 100644 --- a/src/simulator/crates/mc-city/src/city.rs +++ b/src/simulator/crates/mc-city/src/city.rs @@ -105,6 +105,36 @@ impl TileYield { /// median p0_pop_peak ≥ 30 at T300 (Civ5-like capital size). pub const FOOD_PER_POP: f64 = 1.0; +// ── City-center baseline yields ───────────────────────────────────────────── +// +// Every city center tile produces these yields regardless of worked-tile data. +// The food baseline is set to 4 (not 3) so pop-1 cities have +3 surplus against +// FOOD_PER_POP=1, reaching growth threshold 15 in 5 turns — the tempo Civ5 +// ships. Science baseline 5 (not 1) calibrates tech flow so the ≥20-techs-by- +// T150 checklist target hits from a single city without requiring a library. + +/// City-center baseline food yield — ensures pop-1 cities grow at a Civ5 +/// tempo without needing any tile yields attached. +pub const CITY_CENTER_BASELINE_FOOD: f64 = 4.0; + +/// City-center baseline production yield — 2 lets pop-1 cities queue a +/// warrior or worker without tile yields; any boost comes from tiles. +pub const CITY_CENTER_BASELINE_PRODUCTION: f64 = 2.0; + +/// City-center baseline gold yield — 3 keeps a fresh city cash-positive +/// against unit upkeep until marketplace/tile income takes over. +pub const CITY_CENTER_BASELINE_GOLD: f64 = 3.0; + +/// City-center baseline science yield — calibrated to hit the ≥20 techs / +/// 150 turns checklist target from a single city. Was 1.0, bumped to 5.0 +/// when tech costs scale 20..320 across the 50-tech tree; at 1/turn capital +/// empires capped at ~7 techs by T150. +pub const CITY_CENTER_BASELINE_SCIENCE: f64 = 5.0; + +/// City-center baseline culture yield — drives first border expansion at +/// roughly 5 turns against the linear threshold `5+n`. +pub const CITY_CENTER_BASELINE_CULTURE: f64 = 2.0; + /// Fraction of the previous growth threshold retained as stored food on /// growth (Civ5-style always-on granary effect). Each new pop starts with a /// head-start toward the next pop, cutting the cumulative food needed to @@ -313,23 +343,17 @@ impl City { /// Compute aggregate city yields from worked tile yields plus building bonuses. /// The `tile_yields` slice should contain yields for all owned tiles. /// Only tiles in `worked_tiles` are summed (citizens working them). - /// The city center tile always produces a base 3 food, 2 production, 1 gold, - /// 1 science, 2 culture even if no tile yield data is provided for it. + /// The city center tile always produces a baseline {food, production, + /// gold, science, culture} regardless of tile-yield data — see the + /// `CITY_CENTER_*` constants below for the values and their rationale. pub fn get_yields(&self, tile_yields: &[TileYield]) -> CityYields { let mut yields = CityYields::default(); - // City center baseline: 3f/2p/1g/1s/2c. The 3 food ensures pop-1 cities - // have +1.0 surplus (FOOD_PER_POP=2.0) so they always grow. 2 culture - // means first border expansion in ~5 turns. - yields.food += 3.0; - yields.production += 2.0; - yields.gold += 3.0; - // Science baseline 5.0 (was 1.0): tech costs in age-of-dwarves scale - // 20..320 across a 50-tech tree, so 1 sci/turn/city caps empires at - // ~7 techs by T150. 5 sci/turn/city reaches the ≥20 techs/150-turn - // target from a single city even without library; library adds +2. - yields.science += 5.0; - yields.culture += 2.0; + yields.food += CITY_CENTER_BASELINE_FOOD; + yields.production += CITY_CENTER_BASELINE_PRODUCTION; + yields.gold += CITY_CENTER_BASELINE_GOLD; + yields.science += CITY_CENTER_BASELINE_SCIENCE; + yields.culture += CITY_CENTER_BASELINE_CULTURE; // Sum worked tile yields for wt in &self.worked_tiles { @@ -736,12 +760,12 @@ mod tests { city.worked_tiles = vec![(5, 5), (6, 5)]; let ty = sample_tile_yields(); let yields = city.get_yields(&ty); - // Center baseline: 4f + 2p + 3g + 1s - // Center tile yield: 2f + 1p + 0g - // (6,5) tile: 3f + 0p + 1g - assert_eq!(yields.food, 3.0 + 2.0 + 3.0); - assert_eq!(yields.production, 2.0 + 1.0 + 0.0); - assert_eq!(yields.gold, 3.0 + 0.0 + 1.0); + // Center baseline: 4f + 2p + 3g + 5s + 2c + // Center tile yield (worked): 2f + 1p + 0g + // (6,5) tile yield (worked): 3f + 0p + 1g + assert_eq!(yields.food, CITY_CENTER_BASELINE_FOOD + 2.0 + 3.0); + assert_eq!(yields.production, CITY_CENTER_BASELINE_PRODUCTION + 1.0 + 0.0); + assert_eq!(yields.gold, CITY_CENTER_BASELINE_GOLD + 0.0 + 1.0); } #[test]