import { TestCase } from "./framework/test_case.js"; import { smallState, cityOf } from "./framework/helpers.js"; import { key } from "../shared/hex.js"; import { FAMINE, NEWS_FAMINE } from "../shared/data.js"; // A region in famine: its food store has run dry and the day's need went unmet. function starve(state, city) { state._resourceShortages = new Map([[city.id, { food: 1 }]]); } export class FamineTest extends TestCase { test_famine_begins_when_food_runs_out() { const state = smallState(); const city = cityOf(state, 0); this.assertFalse(state.isFamine(city), "a fed region is not in famine"); starve(state, city); state._tickFamine(); this.assertTrue(state.isFamine(city), "running out of food starts a famine"); } test_famine_kills_one_percent_a_day() { const state = smallState(); const city = cityOf(state, 0); const before = state.regionPopulation(city); this.assertGreater(before, 0, "the region has people"); starve(state, city); state._tickFamine(); const after = state.regionPopulation(city); this.assertApprox(after, before * (1 - FAMINE.dailyMortality), before * 0.01); } test_famine_is_announced_once_per_occurrence() { const state = smallState(); const city = cityOf(state, 0); starve(state, city); state._tickFamine(); state._tickFamine(); let news = state.news.filter((entry) => entry.type === NEWS_FAMINE); this.assertEqual(news.length, 1, "a lasting famine is announced only once"); this.assertEqual(news[0].region, city.name, "the region is named"); this.assertEqual(news[0].civ, city.civ, "so is its nation"); // Food comes back: the famine ends silently... state._resourceShortages = new Map(); state._tickFamine(); this.assertFalse(state.isFamine(city), "food ends the famine"); // ...and a later famine is fresh news. starve(state, city); state._tickFamine(); news = state.news.filter((entry) => entry.type === NEWS_FAMINE); this.assertEqual(news.length, 2, "a new famine is announced afresh"); } test_the_snapshot_ships_the_famine() { const state = smallState(); const city = cityOf(state, 0); starve(state, city); state._tickFamine(); const snapshot = state.snapshot(0); const stats = snapshot.cityStats.find((entry) => entry.id === city.id); this.assertTrue(stats.famine, "the famine is shipped to the client"); } test_a_drained_food_store_starts_a_famine() { const state = smallState(); const city = cityOf(state, 0); // The region's people far outnumber what its land can feed, so the day's // harvest and every reachable store fall short of the need. for (const coords of state.regionTiles(city)) { const k = key(coords.x, coords.y); state.tilePopulation.set(k, (state.tilePopulation.get(k) || 0) * 1000); } state._tickResources(); this.assertTrue(state.isFamine(city), "the resource tick starts the famine"); this.assertGreater( state.news.filter((entry) => entry.type === NEWS_FAMINE).length, 0, "the world is told" ); } }