9.3 KiB
9.3 KiB
Economy balance — design brief
A working document for a session on the two commodities whose world-market prices fail to settle: luxury and high-tech.
1. Shared project context (read this first)
Battle for 'Tismo is an HTML5 port of a turn-of-the-millennium strategy game. Pure JavaScript, no build step, no runtime dependencies.
- Server-authoritative.
shared/game_state.js(GameState) is a framework-free model;server/game_server.jsowns one and validates orders. - Shared logic runs in Node and the browser.
shared/stays framework-free (no Node, no DOM). - GameState is mixins.
shared/game_state.jsimports eachshared/game_state/<topic>.jsand adds its methods to the prototype. A new method file must be imported and added there. - The economy ticks hourly (
shared/game_state/resources.js_tickResources); the world can be settled without players viaGameState.warmUp(days)andwarmUpToStability({ maxDays }), which the server calls fromconfigureGame(the--warmupflag). "Steady" means every commodity within 10% over the trailing seven days (pricesAreSteady). - Snapshot performance. The trade graph is cached across ticks and days
under a stamp built from road/railway, territory and storage-node counters;
getCityEconomymust not be memoised outside a snapshot. New per-day walks should be memoised against the counters that actually change them. - Data lives in
shared/data/(barrel:shared/data.js).
Commands
- Install once:
npm install. - One suite:
node tests/run_tests.js --file <name>.js. Never the full suite during development — the commit hook runs it. - A quick way to observe prices is the server's startup settle:
node server/server.js --port 27015 --bind 127.0.0.1(logs how many days it settles,--warmup 0skips it). There is alsotests/warmup_test.js.
Conventions
- 2-space indent; no new libraries.
- Tests in
tests/<topic>_test.jsextendingTestCase. Force RNG withstate._random = () => value. - Commit only when asked; messages are one imperative sentence; the pre-commit hook runs the whole suite.
2. The problem (from ROADMAP.md)
- Luxury and high-tech prices climb to a plateau far above their base (the economy simulator's index reaches about 500-650 for luxury and 200-230 for high-tech) instead of settling; revisit the supply and demand balance of the two rarer goods.
(Index 100 = the base price.) The two rare goods are in permanent structural shortage, so the equilibrium price is high and the easing never reaches steady.
3. How the market works today
- Base prices (
shared/data/resources.jsRESOURCE_MARKET_BASE): energy 0.051 /kWh, steel 10,200 /t, food 5,100 /t, luxury 1,020 /carat, high-tech 13,600 /unit. - Market model (
RESOURCE_MARKET): each day the imbalance sets an equilibrium — the price that clears supply against demand — and the price eases toward it byadjustment(0.2).maxRatio(10) bounds how sharp a shortage may read (and is also the ratio a zero-supply day is treated as). - Demand for the rare goods:
- luxury:
luxuryPerPersonPerDay: 0.01carat per person per day. - high-tech: science buildings spend
highTechPerResearchPoint: 1unit per research point, advanced buildings/units pay high-tech upkeep (upkeepHighTechPerEuro), and construction of science/advanced buildings costsconstructionHighTechPerLevel: 100per level.
- luxury:
- Supply: gold/diamond mines (
energyPerLuxuryCarat), chip foundries (energyPerHighTechUnit). The opening works are generated from the world seed and are fixed per map; nothing in normal play adds supply except a player raising more works. - Energy gates supply: every unit of luxury/high-tech costs a large amount of
electricity, and food synthesis competes for the same power
(
foodSynthesisQuadratic). So a tight electricity balance throttles rare-good supply from above. - Food is the one good a region synthesises from energy, with a reserve-based
price signal (
_tickResourcesgrows towardneed + (stockpileTarget - store) - harvest;_consumeCityResourcesbids the empty-pantry appetite). Rare goods have no such synthesis.
4. Why it plateaus (hypotheses to test)
- Demand scales with population/research, supply does not. Luxury demand
grows with every person; high-tech demand grows with research points and the
advanced-building stock. The seeded mine/foundry supply is a fixed number, so
a mid-game world is structurally short and
maxRatiocaps the price high. - The equilibrium is computed, but the price only eases 20% a day, so if the equilibrium itself is far above base the plateau is simply the equilibrium.
- No elasticity on demand. A very high rare-good price should tempt someone to build more mines/foundries; the AI may not, and the player may not either, so supply never answers the signal.
- Energy competition. The power needed for more supply may be going to food synthesis, pinning supply.
- Stockpile behaviour. Cities aim to hold
stockpileDays: 30of luxury and high-tech, so a one-off demand spike is absorbed then restocked, keeping demand elevated.
5. Design space (settle before coding)
- What is the target? Should luxury/high-tech settle near their base (index ~100), or is a moderate premium intended (they are rare)? The roadmap says "settle", implying they should stop climbing; a stable plateau near, say, 120-200 may be acceptable. Decide the target band first, then tune.
- Make the equilibrium self-correcting. Options: relax
maxRatioso a shortage cannot pin the read; make the equilibrium elastic in supply (higher price → modelled extra production) so the price cannot run away; or add a slow mean-reversion term toward base when demand is met. - Add synthesis for the rare goods (like food) so energy can always buy
more at a rising marginal cost — the food model is the template
(
foodSynthesisQuadratic). - Retune the physical ratios (
energyPerLuxuryCarat,energyPerHighTechUnit, per-person luxury, upkeep/high-tech coefficients) so the world's seeded supply covers its steady-state demand. - Seed more supply at world generation (more mines/foundries) — a blunt fix, but it changes the opening economy, so measure with the warm-up.
- Cap the demand side: e.g. research high-tech spend or advanced upkeep scaled down.
- Warm-up as the acceptance test. After tuning,
warmUpToStabilityshould stop well under the cap with luxury/high-tech within the 10%/7-day band.
6. Suggested order of work
- Write a small measurement harness (a test or script) that runs
warmUpToStabilityon the standard map and prints the final price indices for all five goods and the day count. Use it for every change. - Identify whether the equilibrium is the plateau (hypothesis 2) by logging supply/demand/equilibrium for the two goods over the settle.
- Pick the mechanism (synthesis, elastic equilibrium, retune, or reseed) and tune to the target band.
- Re-run
warmup_test.jsand the economy suites; add a regression test that the rare-good indices stay within a band after a fixed settle. - Tick the roadmap line.
7. Tests to add / extend
- A test that settles the standard map and asserts each good's price index is
within a stated band (e.g. luxury and high-tech both < some multiple of base)
and that
pricesAreSteadyholds. - Unit tests for any new synthesis curve or elastic-equilibrium function (monotonic, as expected).
- Keep
tests/warmup_test.js,economy_test.js,trade_graph_test.js,resources_test.js,stockpile_test.js,food_synthesis_test.js,food_power_test.jsgreen.
8. Gotchas
- Warm-up cost.
warmUpToStabilityruns real simulation days; a tuning that makes it run to the cap will slow every server start and the tests. Keep the settle bounded. - The trade graph cache is keyed on network/territory/storage counters, not tile improvements; do not fold new counters into its stamp without reason.
getCityEconomymust not be memoised outside a snapshot; a test pins that a simulation read reflects a directtilePopulationedit at once.- Prices are global — a change helps/hurts every nation at once; watch the affordability of construction/upkeep, which is priced off the same figures.
- Energy is deliberately cheap (
base.energy: 0.051); do not raise it casually, or every material's energy bill dwarfs its value.
9. Relevant files
shared/data/resources.js— base prices,RESOURCE_MARKET,RESOURCE_RULES(all the ratios),PRICE_INDEX_WINDOW_DAYS,FAMINE.shared/game_state/resources.js—_tickResources,_consumeCityResources, supply/demand/equilibrium, food synthesis,payCombatResources.shared/game_state/economy.js— production, stockpiles, construction costs.server/server.js/server/game_server.js—configureGamewarm-up.- Tests:
tests/warmup_test.js,tests/economy_test.js,tests/resources_test.js,tests/stockpile_test.js,tests/trade_graph_test.js,tests/food_synthesis_test.js.
10. Related design docs
INTELLIGENCE.md, STACKS.md, AIR_MOVEMENT.md, POLITICS_PERFORMANCE.md,
TRAINING_UI.md, ICONS.md (repo root).