The simple economy is now the default and the old simulation is frozen as the hard model, selectable per game. A nation keeps one resource pool available in every region, fed by public production works rather than private per-region stores, and construction and training begin at once, paced by the region's spare power instead of gathering materials over days. Money is euro only: a per-inhabitant daily tax funds a treasury spent in a global market, where buy and sell lots trade against the world stock and move its price. The hard economy keeps its per-nation currencies, central banks and exchange rates. Simple games skip the price-settling warmup and economic migration; the snapshot carries the economy model, the market and the pool figures the client draws. Added the matching test suites and the design briefs under docs/simple_economy.
141 lines
5.0 KiB
JavaScript
141 lines
5.0 KiB
JavaScript
// The economy-model switch introduced by 01-decoupling-hard-economy.md: the
|
|
// simple model is the default, the hard model is the original simulation kept
|
|
// selectable, and the snapshot tells the client which one is running.
|
|
|
|
import { TestCase } from "./framework/test_case.js";
|
|
import { GameState } from "../shared/game_state.js";
|
|
import { GameServer } from "../server/game_server.js";
|
|
import { smallConfig, withServer, SEED } from "./framework/helpers.js";
|
|
import { CIVILISATIONS } from "../shared/data.js";
|
|
|
|
function civs() {
|
|
return ["france", "britain"].map((id) => CIVILISATIONS.find((c) => c.id === id));
|
|
}
|
|
|
|
// A configured state on the small fixture map. `options` is passed straight to
|
|
// `configure`, so a test can pin the model or leave it out entirely.
|
|
function stateWithModel(options) {
|
|
const state = new GameState();
|
|
state.mapConfig = smallConfig();
|
|
state.configure(civs(), SEED, options);
|
|
return state;
|
|
}
|
|
|
|
function makeServer(setup = {}) {
|
|
const network = { getPeerIds: () => [], send: () => {} };
|
|
const server = new GameServer(network);
|
|
server.configureGame({
|
|
seed: SEED,
|
|
player_civ: 0,
|
|
civilisations: ["france", "britain"],
|
|
mapConfig: smallConfig(),
|
|
...setup,
|
|
});
|
|
return server;
|
|
}
|
|
|
|
export class EconomyModelTest extends TestCase {
|
|
test_a_new_state_defaults_to_the_simple_economy() {
|
|
const state = new GameState();
|
|
this.assertEqual(state.economyModel, "simple", "the constructor defaults to simple");
|
|
this.assertTrue(state.isSimpleEconomy(), "and reports it");
|
|
}
|
|
|
|
test_configure_without_options_defaults_to_simple() {
|
|
const state = new GameState();
|
|
state.mapConfig = smallConfig();
|
|
// The two-argument call is the existing one and must keep working.
|
|
state.configure(civs(), SEED);
|
|
this.assertEqual(state.economyModel, "simple");
|
|
this.assertTrue(state.isSimpleEconomy());
|
|
}
|
|
|
|
test_configure_accepts_each_known_model_verbatim() {
|
|
this.assertEqual(stateWithModel({ economyModel: "simple" }).economyModel, "simple");
|
|
const hard = stateWithModel({ economyModel: "hard" });
|
|
this.assertEqual(hard.economyModel, "hard");
|
|
this.assertFalse(hard.isSimpleEconomy());
|
|
}
|
|
|
|
test_unknown_models_coerce_to_simple() {
|
|
for (const value of ["", "SIMPLE", "rich", null, 42, {}]) {
|
|
const state = stateWithModel({ economyModel: value });
|
|
this.assertEqual(state.economyModel, "simple", `coerced from ${JSON.stringify(value)}`);
|
|
this.assertTrue(state.isSimpleEconomy(), "and is treated as simple");
|
|
}
|
|
}
|
|
|
|
test_the_snapshot_carries_the_model() {
|
|
this.assertEqual(stateWithModel({}).snapshot(0).economyModel, "simple");
|
|
this.assertEqual(stateWithModel({ economyModel: "hard" }).snapshot(0).economyModel, "hard");
|
|
}
|
|
|
|
test_a_simple_game_runs_a_day_without_throwing() {
|
|
const state = stateWithModel({ economyModel: "simple" });
|
|
for (let hour = 0; hour < 24; hour++) state.advanceHour();
|
|
this.assertEqual(state.totalHours, 24, "the day advanced");
|
|
}
|
|
|
|
test_a_hard_game_runs_a_day_without_throwing() {
|
|
const state = stateWithModel({ economyModel: "hard" });
|
|
for (let hour = 0; hour < 24; hour++) state.advanceHour();
|
|
this.assertEqual(state.totalHours, 24, "the day advanced");
|
|
}
|
|
|
|
test_the_server_defaults_to_simple() {
|
|
this.assertEqual(makeServer().state.economyModel, "simple");
|
|
}
|
|
|
|
test_the_server_accepts_the_hard_model() {
|
|
this.assertEqual(makeServer({ economyModel: "hard" }).state.economyModel, "hard");
|
|
}
|
|
|
|
test_the_server_coerces_an_unknown_model() {
|
|
this.assertEqual(makeServer({ economyModel: "bogus" }).state.economyModel, "simple");
|
|
}
|
|
|
|
async test_start_server_forwards_the_model() {
|
|
await withServer(async (port, server) => {
|
|
this.assertEqual(server.gameServer.state.economyModel, "hard");
|
|
}, { economyModel: "hard" });
|
|
}
|
|
|
|
// 04-warmup.md: the simple model never announces or runs a settle, and says
|
|
// so when `--warmup` was asked for explicitly; the hard model still settles.
|
|
async test_start_server_skips_the_settle_for_simple() {
|
|
const lines = [];
|
|
await withServer(async (port, server) => {
|
|
this.assertEqual(server.gameServer.settledDays, 0, "no settle ran");
|
|
this.assertEqual(server.gameServer.state.totalHours, 0, "the clock never advanced");
|
|
}, {
|
|
economyModel: "simple",
|
|
warmupDays: 150,
|
|
warmupExplicit: true,
|
|
log: (message) => lines.push(message),
|
|
});
|
|
this.assertFalse(
|
|
lines.some((line) => /settling the world/.test(line)),
|
|
"no settle was announced"
|
|
);
|
|
this.assertTrue(
|
|
lines.some((line) => /needs no warm-up/.test(line)),
|
|
"the ignored flag is reported rather than dropped in silence"
|
|
);
|
|
}
|
|
|
|
async test_start_server_still_settles_for_hard() {
|
|
const lines = [];
|
|
await withServer(async (port, server) => {
|
|
this.assertGreater(server.gameServer.settledDays, 0, "the hard settle ran");
|
|
}, {
|
|
economyModel: "hard",
|
|
warmupDays: 2,
|
|
log: (message) => lines.push(message),
|
|
});
|
|
this.assertTrue(
|
|
lines.some((line) => /settling the world/.test(line)),
|
|
"the settle is announced"
|
|
);
|
|
}
|
|
}
|