Central banks now redeem the national money foreign banks hold, sell reserves to defend a weak rate, and cover part of strategic imports from reserves. Five repeatable technologies raise each commodity's output per unit of energy. Hostile troops on a road or sea lane sever trade, power and unit supply. Middlemen buy food to resell to neighbours at a markup, and starving regions buy before those topping up their buffers, which clears the landlocked famines. The economic map's trade graph gains a colour legend.
115 lines
4.3 KiB
JavaScript
115 lines
4.3 KiB
JavaScript
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"
|
|
);
|
|
}
|
|
}
|