- Five resources (energy, steel, food, luxury, high-tech) with per-city stores, a world market, delivery and material upkeep. - Resource-producing tile works built outside cities; every nation opens with the works its economy needs. - Real game-icons.net art replacing the dummy icons for the new resources and works, with the credits updated.
105 lines
5.1 KiB
JavaScript
105 lines
5.1 KiB
JavaScript
import { TestCase } from "./framework/test_case.js";
|
|
import { smallState } from "./framework/helpers.js";
|
|
import { ECONOMY } from "../shared/data.js";
|
|
import { HOURS_PER_DAY } from "../shared/game_state/constants.js";
|
|
import { parseKey } from "../shared/hex.js";
|
|
|
|
// Natural production growth: a constant daily amount measured from each
|
|
// tile's opening figure, so the relative growth rate tapers over the years.
|
|
export class GdpGrowthTest extends TestCase {
|
|
firstBaseline(state) {
|
|
const k = state.tileProductionBaseline.keys().next().value;
|
|
return { k, baseline: state.tileProductionBaseline.get(k), coords: parseKey(k) };
|
|
}
|
|
|
|
test_a_day_adds_a_constant_share_of_the_opening_figure() {
|
|
const state = smallState();
|
|
// Measure natural growth alone; migration would move people between regions
|
|
// and shift the per-capita on its own.
|
|
state._migrateForIncome = () => {};
|
|
const { k, baseline, coords } = this.firstBaseline(state);
|
|
this.assertTrue(baseline > 0, "the tile has an opening production per head");
|
|
const opening = state.getTileGdpPerCapita(coords);
|
|
this.assertApprox(opening, baseline, 1e-9, "day zero is the baseline");
|
|
for (let i = 0; i < HOURS_PER_DAY; i++) state.tickHour();
|
|
const dayOne = state.getTileGdpPerCapita(coords);
|
|
const step = baseline * ECONOMY.naturalProductionGrowthPerDay;
|
|
this.assertTrue(dayOne > opening, "the tile has grown after a day");
|
|
// The total also tracks market prices, which drift as production meets
|
|
// demand; the natural-growth term itself is what adds exactly one step.
|
|
this.assertApprox(state._naturalTileProductionGrowth(k), step, 1e-6, "one day adds one step");
|
|
// Thirty more days add exactly thirty more steps: the natural-growth term is
|
|
// a constant. The tile's total also tracks production and market prices,
|
|
// which drift on their own, so the term itself is what stays linear.
|
|
state.totalHours = 31 * HOURS_PER_DAY;
|
|
this.assertApprox(state._naturalTileProductionGrowth(k), 31 * step, 1e-6, "the daily amount never grows");
|
|
this.assertEqual(k, state.tileProductionBaseline.keys().next().value, "the baseline never moves");
|
|
}
|
|
|
|
test_the_relative_growth_rate_falls_as_the_tile_grows() {
|
|
const state = smallState();
|
|
const { baseline, coords } = this.firstBaseline(state);
|
|
const step = baseline * ECONOMY.naturalProductionGrowthPerDay;
|
|
const earlyRate = step / baseline;
|
|
state.totalHours = 366 * HOURS_PER_DAY;
|
|
state._gdpPerCapitaCache.clear();
|
|
const value = state.getTileGdpPerCapita(coords);
|
|
// The same constant step, a year later, is a smaller share of a bigger
|
|
// figure: the relative rate has fallen.
|
|
const laterRate = step / (value - step);
|
|
this.assertTrue(value > baseline, "the tile is richer than it began");
|
|
this.assertTrue(laterRate < earlyRate, "the same gain is now a smaller share");
|
|
}
|
|
|
|
test_growth_is_measured_from_january_2000_in_the_first_year() {
|
|
const state = smallState();
|
|
// Hold migration still so the day's change is natural growth alone.
|
|
state._migrateForIncome = () => {};
|
|
const growth = state.viewerStats(0).growth;
|
|
this.assertNotNull(growth, "the viewer carries growth figures");
|
|
this.assertTrue(growth.sinceStart, "the first year measures from January 2000");
|
|
this.assertApprox(growth.gdpPerCapita, 0, 1e-9, "nothing has grown yet");
|
|
for (let i = 0; i < HOURS_PER_DAY; i++) state.tickHour();
|
|
const after = state.viewerStats(0).growth;
|
|
this.assertTrue(after.sinceStart, "still the first year");
|
|
this.assertTrue(after.gdpPerCapita > 0, "a day of growth is reported");
|
|
this.assertTrue(after.gdp > after.gdpPerCapita, "GDP also carries population growth");
|
|
}
|
|
|
|
test_growth_becomes_year_on_year_after_the_first_year() {
|
|
const state = smallState();
|
|
const current = state._monthIndexOf(state.totalHours);
|
|
// A year on, the same month last year is the baseline. Halve it so the
|
|
// comparison has an unmistakable answer.
|
|
const past = state._captureMonthlyStats();
|
|
for (const entry of past) {
|
|
entry.population = Math.floor(entry.population / 2);
|
|
entry.gdp = entry.gdp / 2;
|
|
entry.gdpPerCapita = entry.gdpPerCapita / 2;
|
|
}
|
|
state._monthIndexNow = current;
|
|
state._monthStartHistory = [
|
|
{ index: current - 13, stats: past },
|
|
{ index: current - 12, stats: past },
|
|
{ index: current, stats: state._captureMonthlyStats() },
|
|
];
|
|
const growth = state._growthRates(0);
|
|
this.assertFalse(growth.sinceStart, "with a year of history it is year on year");
|
|
this.assertApprox(growth.population, 1, 1e-6, "the population doubled");
|
|
this.assertApprox(growth.gdp, 1, 1e-6, "the GDP doubled");
|
|
}
|
|
|
|
test_the_snapshot_ships_the_growth_baseline_once() {
|
|
const state = smallState();
|
|
const first = state.snapshot(0);
|
|
this.assertSize(
|
|
first.productionBaseline,
|
|
state.tileProductionBaseline.size,
|
|
"every baseline tile travels"
|
|
);
|
|
this.assertNotNull(first.viewerStats.growth, "the viewer's growth travels too");
|
|
// The version never moves, so the delta wire drops the baseline afterwards.
|
|
this.assertEqual(first.versions.productionBaseline, 0, "the baseline version is static");
|
|
}
|
|
}
|