Files
Battle-for-Tismo/docs/simple_economy/06-global-market.md
T
adrien d5305da449 Gave the world a simple economy: one pool per nation and a global market
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.
2026-09-25 14:58:49 +02:00

5.2 KiB
Raw Blame History

06 — The global market

Depends on: 01-, 02-, 05-.

Goal

In the nation view, the Resources tab acts as a global market. Players can sell and buy resources in fixed lots of 100, 2 000 and 50 000. The market starts with a stockpile of 1,000,000 of each resource, and the price is

price = initial_price × (available / 1_000_000) ^ -1

Current behaviour (hard economy)

  • shared/data/resources.js defines RESOURCE_MARKET_BASE (per-unit euro prices) and RESOURCE_MARKET / nextMarketPrice, which eases a drifting price toward a supply/demand equilibrium each day.
  • shared/game_state/resources.js holds resourcePrices, getResourcePrice, _marketUnitCost, and the daily market set-up inside _tickResources (supply/demand per resource, previousPrices, commodityInflation).
  • There are no buy/sell orders today. The Resources tab (client/js/modals/nation.js) is read-only: stores, consumption, production, trade, prices.
  • The resourcePrices figures are used all over the hard simulation (upkeep valuation, construction quotes, combat). Simple mode must not destabilise them mid-tick.

Target behaviour

  • A global market (shared by all players, per the price formula's global available) holds a stock per resource, starting at 1_000_000.
    • Market.stock[id] for the storable resources. Energy is non-storable in the hard model; decide whether the market includes it (see decisions).
    • marketPrice(id) = RESOURCE_MARKET_BASE[id] * Math.pow(stock[id] / 1_000_000, -1) (clamp for a zero/very low stock so it cannot explode or go NaN). Cache it per stock change so the price is one source of truth.
  • Buy: the player pays qty × marketPrice(id) from the treasury, and the goods go into the player's pool (02-); the market stock decreases by qty, so the price rises. Reject if the treasury is short.
  • Sell: the goods leave the player's pool and the treasury is credited qty × marketPrice(id); the market stock increases, so the price falls. Reject if the pool is short.
  • Lots: only 100, 2000, 50000 per order (both directions). Validate the lot server-side; never trust the client.
  • Order: add market_trade to GameServer.handleOrder (server/game_server.js), e.g. { t: "market_trade", side: "buy"|"sell", resource, lot }, mapping to state.requestMarketTrade(civ, resource, side, qty). The request validates civ, resource, side, lot, funds/stock, then mutates the pool, market stock and treasury atomically and returns a boolean.
  • Prices in simple mode come from the market formula, not nextMarketPrice. getResourcePrice(id) should return marketPrice(id) in simple mode so every existing caller (upkeep valuation, budget quotes) automatically reads the right figure. Drop the daily supply/demand price drift and commodityInflation from the simple tick.
  • Market stock must be part of the snapshot so every client shows the same price/availability, and versioned (see 10-snapshot-and-orders.md).

Files

  • shared/data/simple_economy.js (new) — MARKET_START_STOCK = 1_000_000, MARKET_LOTS = [100, 2000, 50000], price helper.
  • shared/game_state/simple_economy.js — _initSimpleMarket, marketPrice, getResourcePrice override, requestMarketTrade, getMarketView.
  • shared/game_state/resources.js — the hard _tickResources market block must be skipped in simple mode (override the tick or the price update).
  • server/game_server.js — the market_trade handler.
  • shared/game_state/serialization.js — market stock/price in the snapshot.
  • client/js/modals/nation.js — the market UI (see 09-ui.md, approval gate).
  • tests/simple_market_test.js — new.

Acceptance criteria

  • At stock = 1_000_000, marketPrice(id) === RESOURCE_MARKET_BASE[id].
  • Buying qty lowers stock and raises the price; selling raises stock and lowers the price; the pool and treasury move by exactly qty and qty × price.
  • Only the three lots are accepted; other quantities are refused.
  • The client sees one consistent market price across players.
  • Hard economy prices still drift with supply/demand.

Notes / decisions

  • Global vs per-player market. The formula's available / 1_000_000 reads as one shared world stock. The phrase "between players" could instead mean a player-to-player order book. Recommendation: a single shared global market pool; it matches the starting stock and formula literally. Confirm.
  • Energy. If energy stays a regional flow (02-), it cannot be bought into a pool. Recommendation: exclude energy from the buyable market, or give the simple economy a storable energy commodity. Confirm before building the UI.
  • Food/famine interplay. Selling all food could starve the nation; decide whether to warn or block. Recommendation: allow it (the tax/market loop is the point) and surface a warning.
  • Price flooring. With the ^-1 formula a near-empty stock sends the price to infinity. Define a minimum stock (or a max price) so a rounding error cannot break the market.
  • Currency. 05- makes everything euro; the market is the one place money is spent, which is exactly this module.