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.
5.2 KiB
5.2 KiB
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.jsdefinesRESOURCE_MARKET_BASE(per-unit euro prices) andRESOURCE_MARKET/nextMarketPrice, which eases a drifting price toward a supply/demand equilibrium each day.shared/game_state/resources.jsholdsresourcePrices,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
resourcePricesfigures 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 at1_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 byqty, 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,50000per order (both directions). Validate the lot server-side; never trust the client. - Order: add
market_tradetoGameServer.handleOrder(server/game_server.js), e.g.{ t: "market_trade", side: "buy"|"sell", resource, lot }, mapping tostate.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 returnmarketPrice(id)in simple mode so every existing caller (upkeep valuation, budget quotes) automatically reads the right figure. Drop the daily supply/demand price drift andcommodityInflationfrom 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,getResourcePriceoverride,requestMarketTrade,getMarketView.shared/game_state/resources.js— the hard_tickResourcesmarket block must be skipped in simple mode (override the tick or the price update).server/game_server.js— themarket_tradehandler.shared/game_state/serialization.js— market stock/price in the snapshot.client/js/modals/nation.js— the market UI (see09-ui.md, approval gate).tests/simple_market_test.js— new.
Acceptance criteria
- At
stock = 1_000_000,marketPrice(id) === RESOURCE_MARKET_BASE[id]. - Buying
qtylowers stock and raises the price; selling raises stock and lowers the price; the pool and treasury move by exactlyqtyandqty × 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_000reads 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
^-1formula 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.