Files
Battle-for-Tismo/tests/rules_test.js

163 lines
6.1 KiB
JavaScript

import { TestCase } from "./framework/test_case.js";
import {
buildingBuildCost,
buildingRefund,
buildingUpkeep,
buildingEffectValue,
governmentChangeCost,
prerequisitesMet,
defenseFactors,
defenseMultiplier,
unitDefense,
productionFactors,
productionFromFactors,
} from "../shared/rules.js";
import { effectValue, TECHNOLOGIES, TERRAIN_TILES } from "../shared/data.js";
function proto(overrides = {}) {
return {
baseCost: 0,
costAlpha: 1.5,
refundFraction: 0.5,
effects: [],
...overrides,
};
}
function terrain(type) {
return TERRAIN_TILES.find((t) => t.terrainType === type);
}
export class RulesTest extends TestCase {
test_build_cost_grows_with_level_and_ignores_gdp() {
const building = proto({ baseCost: 100, costAlpha: 2.0 });
this.assertApprox(buildingBuildCost(building, 0), 100);
this.assertApprox(buildingBuildCost(building, 1), 200);
this.assertApprox(buildingBuildCost(building, 2), 400);
this.assertApprox(buildingBuildCost(building, 3), 100 * Math.pow(2.0, 3));
}
test_upkeep_and_refund_follow_the_build_cost() {
const building = proto({ baseCost: 100, costAlpha: 2.0 });
this.assertEqual(buildingUpkeep(building, 0), 0);
this.assertApprox(buildingUpkeep(building, 1), 1);
this.assertApprox(buildingUpkeep(building, 2), 2);
this.assertApprox(buildingUpkeep(building, 3), 4);
const refunding = proto({ baseCost: 100, refundFraction: 0.5 });
this.assertApprox(buildingRefund(refunding, 1), 50);
this.assertApprox(buildingRefund(refunding, 2), 75);
}
test_effect_is_linear_or_sublinear() {
this.assertApprox(effectValue({ baseValue: 10, exponent: 1 }, 3), 30);
this.assertEqual(effectValue({ baseValue: 10, exponent: 1 }, 0), 0);
this.assertApprox(effectValue({ baseValue: 10, exponent: 0.5 }, 4), 20);
this.assertLess(
effectValue({ baseValue: 10, exponent: 0.5 }, 4),
effectValue({ baseValue: 10, exponent: 1 }, 4)
);
}
test_building_effect_value_sums_matching_stats() {
const building = proto({
effects: [
{ stat: "production", baseValue: 0.04, exponent: 0.75 },
{ stat: "production", baseValue: 0.01, exponent: 1.0 },
{ stat: "culture", baseValue: 1.0, exponent: 1.0 },
],
});
this.assertApprox(buildingEffectValue(building, "production", 1), 0.05);
this.assertApprox(buildingEffectValue(building, "culture", 1), 1.0);
this.assertEqual(buildingEffectValue(building, "research", 1), 0);
this.assertEqual(buildingEffectValue(building, "production", 0), 0);
}
test_government_change_cost() {
const government = { baseCost: 100, costGdpFraction: 0.01 };
this.assertApprox(governmentChangeCost(government, 1000), 110);
this.assertApprox(governmentChangeCost(government, 10000), 200);
this.assertApprox(governmentChangeCost(government, -50), 100);
}
test_prerequisites_met() {
const root = TECHNOLOGIES.find((t) => t.id === "industrial_automation");
this.assertEmpty(root.prerequisites);
this.assertTrue(prerequisitesMet(root, new Set(), TECHNOLOGIES), "a root tech needs nothing");
const space = TECHNOLOGIES.find((t) => t.id === "space_program");
const index = (id) => TECHNOLOGIES.findIndex((t) => t.id === id);
this.assertFalse(prerequisitesMet(space, new Set(), TECHNOLOGIES), "missing all prerequisites");
this.assertFalse(
prerequisitesMet(space, new Set([index("industrial_automation")]), TECHNOLOGIES),
"still missing one"
);
this.assertTrue(
prerequisitesMet(
space,
new Set([index("industrial_automation"), index("quantum_computing")]),
TECHNOLOGIES
),
"all prerequisites met"
);
}
test_defense_factors_and_multiplier() {
const mountain = terrain("Mountain");
const plains = terrain("Land");
this.assertSize(defenseFactors(plains, false), 0, "open land defends normally");
this.assertSize(defenseFactors(null, false), 0, "no tile, no factor");
const mountainFactors = defenseFactors(mountain, false);
this.assertSize(mountainFactors, 1);
this.assertEqual(mountainFactors[0].label, "Mountain");
this.assertApprox(mountainFactors[0].bonus, 1.0);
const cityFactors = defenseFactors(mountain, true);
this.assertSize(cityFactors, 2);
this.assertEqual(cityFactors[1].label, "City");
this.assertApprox(cityFactors[1].bonus, 0.5);
this.assertApprox(defenseMultiplier(plains, false), 1.0);
this.assertApprox(defenseMultiplier(terrain("Forest"), false), 1.33);
this.assertApprox(defenseMultiplier(mountain, false), 2.0);
this.assertApprox(defenseMultiplier(mountain, true), 2.5);
this.assertApprox(unitDefense(100, mountain, false), 200);
this.assertApprox(unitDefense(100, mountain, true), 250);
this.assertApprox(unitDefense(100, null, false), 100);
this.assertApprox(terrain("Forest").defenseBonus, 0.33);
this.assertApprox(terrain("Hills").defenseBonus, 0.33);
this.assertApprox(mountain.defenseBonus, 1.0);
this.assertApprox(plains.defenseBonus, 0);
}
test_production_factors_and_application() {
const plains = terrain("Land");
const base = productionFactors(plains);
this.assertSize(base, 1);
this.assertEqual(base[0].label, "Land");
this.assertApprox(base[0].multiplier, 1);
const city = productionFactors(plains, { cityKind: "city" });
this.assertSize(city, 2);
this.assertEqual(city[1].label, "City");
this.assertApprox(city[1].multiplier, 2);
this.assertApprox(productionFactors(plains, { cityKind: "capital" })[1].multiplier, 2.5);
const near = productionFactors(plains, { nextToCity: true });
this.assertEqual(near[1].label, "Near city");
this.assertApprox(near[1].multiplier, 1.3);
const war = productionFactors(plains, { pillagePenalty: 0.7, battleDeficit: 500 });
this.assertEqual(war[1].label, "Pillaging");
this.assertApprox(war[1].multiplier, 0.7);
this.assertEqual(war[2].label, "Battle damage");
this.assertApprox(war[2].amount, -500);
this.assertApprox(
productionFromFactors(10000, [{ multiplier: 2 }, { amount: -500 }]),
19500
);
this.assertEqual(productionFromFactors(100, [{ amount: -500 }]), 0);
}
}