import { TestCase } from "./framework/test_case.js"; import { smallState } from "./framework/helpers.js"; import { key } from "../shared/hex.js"; import { RESOURCE_IDS, RESOURCE_EFFICIENCY_EFFECT, TECHNOLOGIES, } from "../shared/data.js"; import { producerEnergyPerDay } from "../shared/resources.js"; // Puts a resource building on the first free land tile of `civ`. function placeProducer(state, id, civ = 0) { for (const coords of state._territoryByCiv.get(civ) || []) { if (state.cityAt(coords)) continue; if (!state._isLand(coords)) continue; const k = key(coords.x, coords.y); if (state.tileImprovements.has(k)) continue; state.tileImprovements.set(k, id); state.tileImprovementOwner.set(k, civ); state.tileImprovementHp.set(k, 1000); state._ensureResourceStock(k); state._ensureBuildingAgent(k); return { coords, k }; } return null; } // Studies one level of a repeatable technology for a nation. function research(state, civ, id) { const index = state.technologies.findIndex((tech) => tech.id === id); state.researched.get(civ).add(index); const counts = state.repeatCounts.get(civ); counts.set(id, (counts.get(id) || 0) + 1); state._touchModifiers(); } export class ResourceEfficiencyDataTest extends TestCase { test_there_is_exactly_one_efficiency_technology_per_resource() { for (const resource of RESOURCE_IDS) { const stat = RESOURCE_EFFICIENCY_EFFECT[resource]; const matching = TECHNOLOGIES.filter( (tech) => (tech.effects || []).some((e) => e.stat === stat) ); this.assertSize(matching, 1, `one technology raises ${resource} output per input`); this.assertTrue(matching[0].repeatable, `${resource} efficiency is repeatable`); } } } export class ResourceEfficiencyModelTest extends TestCase { test_efficiency_is_zero_until_the_technology_is_researched() { const state = smallState(); this.assertApprox(state.resourceEfficiency(0, "steel"), 0); research(state, 0, "advanced_metallurgy"); this.assertApprox(state.resourceEfficiency(0, "steel"), 0.05); research(state, 0, "advanced_metallurgy"); this.assertApprox(state.resourceEfficiency(0, "steel"), 0.10, 1e-9); this.assertApprox(state.resourceEfficiency(1, "steel"), 0, 1e-9, "research is national"); } test_steel_efficiency_cuts_the_mill_s_energy_draw() { const state = smallState(); const placed = placeProducer(state, "steel_mill"); const components = state._resourceLandComponents(); const componentId = components.get(placed.k); const draw = () => state._buildEnergyLedger(components).get(componentId).converters .find((entry) => entry.k === placed.k).amount; const before = draw(); this.assertGreater(before, 0); research(state, 0, "advanced_metallurgy"); const after = draw(); this.assertApprox(before / after, 1.05, 1e-6, "the same steel costs 5% less power"); } test_energy_efficiency_cuts_the_plant_s_fuel() { const state = smallState(); const placed = placeProducer(state, "coal_power_plant"); const components = state._resourceLandComponents(); const componentId = components.get(placed.k); const fuel = () => state._buildEnergyLedger(components).get(componentId).fuelEnergy; const before = fuel(); this.assertGreater(before, 0); research(state, 0, "efficient_generation"); const after = fuel(); this.assertApprox(before / after, 1.05, 1e-6, "the same power burns 5% less fuel"); } test_every_efficiency_technology_reads_back_on_its_own_resource() { const state = smallState(); const byResource = { energy: "efficient_generation", steel: "advanced_metallurgy", food: "precision_agriculture", luxury: "automated_extraction", hightech: "advanced_lithography", }; for (const [resource, tech] of Object.entries(byResource)) { research(state, 0, tech); this.assertApprox(state.resourceEfficiency(0, resource), 0.05, 1e-9, resource); } } test_food_efficiency_cuts_the_harvest_s_energy() { const state = smallState(); this.assertApprox( state._foodHarvestEnergy(0, 1000), producerEnergyPerDay("food", 1000) ); research(state, 0, "precision_agriculture"); this.assertApprox( state._foodHarvestEnergy(0, 1000), producerEnergyPerDay("food", 1000) / 1.05, 1e-6, "better farming grows the same food for less power" ); } }