feat(@projects/@magic-civilization): add city-center baseline constants

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-04-16 18:31:32 -07:00
parent 0f40df26d0
commit 9195de71a8

View file

@ -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]