clone

an rts arena for scripts: submit tribe strategies over the web or mcp, run deterministic matches, watch them

main
tribes / packages / engine / README.md
README.md 19.9K rendered
1# @tribes/engine23Deterministic v2 simulation engine for Tribes. Implements `createWorld:4CreateWorld` from `@tribes/protocol`. Tribes are dense populations (1,2005citizens at spawn) simulated as cohorts under per-mille policies — there are6no per-citizen entities. Individual entities: settlements, warbands, caravans,7settler wagons, threat camps.89All numbers live in `src/constants.ts`; this file is the human-readable10reference for them.1112## Determinism (binding)1314- All quantities are integers; rates/shares are per-mille (0..1000).15- Fractional flows use fixed-point accumulators **in hashed state**:16 `out = (base*rate + acc) div D; acc' = (base*rate + acc) mod D`. `D` is17 1000 for simple flows and 1,000,000 for extraction (worker-permille ×18 per-mille rate; documented denominator variant of the accumulator rule).19 Input-starved refinement clamps `acc < 1000` (starved capacity is lost, it20 does not bank).21- Allocation across a set uses largest-remainder apportionment22 (`src/apportion.ts`), ties broken by ascending index/id.23- PRNG (splitmix32, `src/rng.ts`) is consumed only by: mapgen (construction),24 threat placement (phase 11), and ±5% battle variance25 (`power × (950 + rng(101)) / 1000`, attacker drawn before defender), each in26 fixed iteration order.27- Slow systems run every `SLOW_TICK_INTERVAL = 10` ticks ("slow tick" =28 every step where `(tick+1) % 10 == 0`).29- `SimEvent`s are per-step narration, fog-filtered in `viewFor()`, and NOT30 part of `stateHash()`.3132## Phase order per `step()`33341. **Actions → policies** — tribes ascending id, first `ACTIONS_PER_TICK`35 (64) orders each; invalid orders dropped silently.362. **Territory & census** *(slow)* — per-settlement biome census within its37 territory radius (Chebyshev).383. **Labor apportionment** — see below.394. **Extraction** — farmers/fishers/woodcutters/miners (+ tool wear; herders40 tend herds).415. **Refinement & construction** — workshop chains in profession enum order,42 then builders advance the build queue.436. **Herds** *(slow)* — growth, fodder, wool, slaughter, horses.447. **Logistics** — caravans & settler wagons move 1 tile/tick (greedy step,45 slide-around, ocean impassable, rivers fordable); routes & food46 equalization *(slow)*.478. **Consumption & needs** — food, ale ration, cold-biome clothes;48 starvation.499. **Population & morale** *(slow)* — morale drift, births/deaths/emigration,50 tier upgrades.5110. **Military** — warband movement (ascending id), field battles, camp52 assaults, raids; sieges & supply *(slow)*.5311. **Threats** *(slow)* — camp growth, spawn attempt, camp raids.5412. **Trade, faith & tech** *(slow)* — knowledge/faith accrual, research55 completion, rite expiry, peddler, offer matching.5613. **Elimination** — a tribe with zero settlements AND zero warbands is57 eliminated (its caravans/wagons/offers/routes/effects vanish).5814. **Sightings, metrics** — `sighted` events (per observer/enemy pair,59 200-tick cooldown); `tick++`; a `MetricsFrame` is computed when the new60 tick is a multiple of 10 and on the final tick, then interval flows reset.6162## Map (64×64 default, seeded; band/blob/vein densities scale with size)6364Terrain chars: `.` plains - `f` forest - `h` hills - `m` mountain - `w` ocean65(impassable) - `r` river (passable) - `s` swamp - `t` steppe - `a` arctic -66`v` volcano - `O` ore vein - `G` gold vein (both hills-class). Layout: ocean67border (solid 2, ragged to 4), arctic caps (10 rows) + steppe bands (6 rows)68north/south, forest/hills/mountain/swamp blobs, up to 2 volcanoes, 5 lakes,691–2 meandering rivers, ≤35 ore + ≤8 gold veins on hills/mountains. Cold70tiles (clothes upkeep): `t`, `a`, `m`.7172Spawns sit on a ring (integer 16-point circle — no trig) at radius73`3/8 × size`. **Fairness (terraformed deterministically)**: within Chebyshev74radius 8 of every founding site: plains ≥ 20, forest ≥ 10, water-adjacent75land ≥ 3, hills-or-mountain ≥ 6 with ≥ 1 ore vein.7677Each tribe starts with one settlement: pop 1200 (tier 3, radius 12), morale78600, herds 40, horses 10; buildings hall, 10 house, granary, warehouse,7910 farm, 2 pasture, 2 lumber_camp, mine, smithy, market, shrine, library,80barracks; stores grain 2000, meat 100, hides 50, timber 400, stone 250,81coin 100, metal 40, leather 40, tools 250, weapons 100, armor 20,82clothes 100.8384## Territory, tiers, storage, housing8586- Tier from pop (never downgrades): 0 camp / 1 village ≥100 / 2 town ≥400 /87 3 city ≥1000. Territory radius 4/6/9/12 (Chebyshev square).88- Store cap per **resource**: `2000 + 2000×granary + 2000×warehouse` (one89 shared cap — documented simplification; overflow is lost).90- Housing: hall `300 + 300×tier`, house 60. Births stop at 100% occupancy91 and taper from 80%.9293## Labor9495`allocate` sets tribe-wide per-mille per profession; `allocate` with96`settlementId` sets a per-settlement override (persistent; a settlement with97**any** override switches wholly to local apportionment).9899- **Tribe pool** (settlements without overrides): total pop is apportioned100 across professions + idle by the tribe's permilles (largest remainder, ties101 in profession enum order; weight sums > 1000 normalize). Each profession's102 total is then apportioned across settlements **by capacity** (ties by103 ascending settlement id), capped by capacity and remaining pop.104- **Local** (overridden settlements): the settlement's pop is apportioned by105 its effective vector, capped by capacity; the rest is idle.106107Capacity = building slots × count, extraction also capped by biome census:108109| profession | slots | biome cap |110| --- | --- | --- |111| farmer | farm 40 | plains × 8 |112| fisher | dock 30 | water tiles × 10 |113| herder | pasture 10 | — |114| woodcutter | lumber_camp 30 | forest × 8 |115| miner | mine 30 | hills-class × 8 |116| miller / baker | mill 20 / bakery 20 | — |117| brewer | brewery 15 | — |118| smelter / smith | smelter 20 / smithy 20 | — |119| carpenter | lumber_camp 10 + hall 5 | — |120| tanner / weaver / tailor | 15 each | — |121| scholar | library 10 + academy 25 | — |122| priest | shrine 10 + church 25 | — |123| builder / hauler | uncapped | — |124125## Extraction & tools126127Rates are ‰ units per worker-tick at full tool coverage: farmer → grain 100128(+200‰ with crop_rotation, +100‰ under a harvest rite), fisher → fish 80,129woodcutter → timber 80, miner → stone 60 always, +ore 40 with ≥1 `O` in130territory, +coin 15 with ≥1 `G`.131132**Tool rule**: extractive professions (farmer/fisher/herder/woodcutter/miner)133work at 600‰ yield without tools, 1000‰ with. Coverage =134`min(stores.tools, extractive workers)`, assigned in profession enum order.135Tools wear 1 per **200** covered worker-ticks (accumulator), consumed from136settlement stores.137138## Refinement (per output unit; ‰ per worker-tick)139140| worker | rate | recipe |141| --- | --- | --- |142| miller | 400 | 1 grain → 1 flour (gated by `craft mill` share of grain) |143| baker | 300 | 1 flour → 1 bread |144| brewer | 200 | 2 grain → 1 ale (gated by `craft brew`) |145| smelter | 150 | 2 ore + 1 timber → 1 metal |146| smith | 100 | 1 metal + 1 timber → 1 tools/weapons; armor adds 1 leather |147| carpenter | 200 | 1 timber → 1 plank |148| tanner | 200 | 1 hide → 1 leather |149| weaver | 150 | 2 wool → 1 cloth |150| tailor | 100 | 1 cloth + 1 leather → 1 clothes |151152The smithy split (`craft tools/weapons/armor`) apportions output by largest153remainder; weapons need `weaponsmithing`, armor needs `plate_armor` (locked154targets weigh 0; all-zero weights default to tools). Grain gates (`craft155mill/brew/fodder`) are shares of current grain stock per slow phase.156157## Consumption, morale158159- Food demand: **2 satiation points per capita per 10 ticks** at160 `rations = 1000` (decree 0..2000); per tick161 `(pop×rations + acc) div 50000`. Eat order and satiation: bread 3 >162 fish 2 = meat 2 > grain 1 ("1 food per 10 ticks per capita" holds exactly163 for fish/meat). Calibration: 40% of pop farming grain feeds the tribe even164 toolless (480 × 0.1 × 0.6 = 28.8 pts vs 24 needed).165- Shortfall → starvation: deaths/tick = `min(shortfall, max(1, pop/50))`,166 −30 morale, `starvation` event.167- `ale_ration` ‰: 1 ale per capita per 10t at 1000‰; fully served → morale168 bonus.169- Cold settlements (on/adjacent `t`/`a`/`m`): clothes wear `pop/10000` per170 tick; shortage → morale penalty.171- Morale (0..1000) drifts 1/4 of the gap to its target each slow tick.172 Target = 500 + 100 (≥2 food kinds this interval) + 100 (ale served) + 150173 (blessing) − 150 (clothes short) − 300 (starved).174175## Population (slow)176177- births/10t = `pop × 8‰ × housing × morale × surplus` (factors ‰; surplus178 saturates at 50 ticks of stored food).179- natural deaths 2‰/10t; emigration 10‰/10t while morale < 300.180181## Herds (slow)182183Herders tend 5 herds each (tool-affected). Growth 30‰/10t of tended herds184(60‰ for the fodder-fed fraction: `fodder` decree ‰ of herds, 1 grain each185from the `craft fodder` gate), capped at 20 herds/pasture. Wool 100‰ per186tended herd/10t. `slaughter` decree ‰/10t → 3 meat + 1 hide each. Horses:18720‰/10t growth, capped 10/pasture; cavalry needs 1/soldier.188189## Construction190191`build` debits materials immediately and enqueues (queue cap 12, count ≤ 5192per order). Builders add 1 point each per tick; one active site per 10193builders (min 1), points split largest-remainder over active sites in queue194order. `demolish` removes one instance instantly, no refund.195196| building | cost (t=timber, p=planks, s=stone) | points | gate |197| --- | --- | --- | --- |198| house | 20t | 40 | |199| granary | 40t | 80 | |200| warehouse | 40t 20s | 100 | |201| farm | 30t | 60 | plains in territory |202| dock | 40t | 80 | water in territory |203| pasture | 20t | 50 | |204| lumber_camp | 20t | 50 | forest in territory |205| mine | 30t 20s | 100 | hills in territory |206| mill / bakery | 30t 10s / 30t 20s | 100 | milling |207| brewery | 30t 10s | 100 | brewing |208| smelter | 20t 40s | 120 | smelting |209| smithy | 30t 20s | 120 | |210| tannery / weavers_hall / tailor_shop | 20t / 30t / 20t | 80 | |211| market | 50t 20s | 120 | |212| shrine | 20t 20s | 80 | |213| church | 60s 40p | 200 | liturgy |214| library | 50t 20s | 120 | |215| academy | 60s 40p | 200 | scholarship |216| barracks | 50t 20s | 120 | |217| walls L1 | 80t | 150 | |218| walls L2 | 150s | 250 | masonry |219| walls L3 (castle) | 200s 50p | 400 | castellany |220| tower | 30t 40s | 120 | walls ≥ 1 |221222The `walls` building count IS the wall level (max 3). The hall is not223buildable (one per settlement, from founding).224225## Tech & faith226227Scholars produce 150‰ knowledge each per 10t; priests 100‰ faith. Research228completes on a slow tick when the pool covers229`base × (4 + techsOwned) / 4` (escalation applied at completion).230231| tech | base cost | prereq | effect |232| --- | --- | --- | --- |233| milling / brewing / smelting | 60 / 60 / 80 | — | unlock mill+bakery / brewery / smelter |234| weaponsmithing | 150 | smelting | smith may craft weapons |235| plate_armor | 200 | weaponsmithing | smith may craft armor |236| masonry | 80 | — | walls L2 |237| castellany | 200 | masonry | walls L3 |238| coinage | 100 | — | peddler rate 250‰ → 400‰ |239| crop_rotation | 100 | — | +200‰ farm yield |240| horsemanship | 100 | — | cavalry muster |241| scholarship | 120 | — | academy |242| liturgy | 80 | — | church |243| siegecraft | 250 | weaponsmithing | rams (see sieges) |244245Rites (one-shot faith spends): blessing 20 (own settlement, +150 morale246target, 50t) - harvest 25 (own settlement, +100‰ farm, 50t) - warding 40247(tribe-wide, halves threat raid power, 100t) - war 30 (own warband, +100‰248power for its next battle, expires after 200t). Timed effects live in hashed249state.250251## Military252253- `muster` (needs barracks): debits pop (min size 10, ≥50 civilians remain),254 **weapons 1/soldier (hard)**, armor up to 1/soldier (optional), horses255 1/soldier for cavalry (horsemanship). Supply auto-fills up to 3256 food/soldier from stores (grain→fish→meat→bread). With siegecraft and257 ≥20 planks + ≥5 metal at the settlement, the warband gets a ram (siege258 flag; materials debited).259- **Power** = `size × (10 + armed(10) + armorRatio‰/100 + veterancy/100 +260 situational)`; situational = defender terrain (forest +2, hills +3,261 mountain/volcano +4, swamp −2) and `wallLevel × 5` when defending a262 settlement.263- **Battles**: both powers get ±5% PRNG variance (attacker first). Higher264 wins (tie → defender). With `r = winnerPower×1000/loserPower`: loser loses265 `min(500, 150 + r/5)`‰ (min 1), winner `min(300, max(50, 350000/r))`‰.266 Winner +50 veterancy, loser +20; loser routs toward home (−100 morale).267 Field battles trigger between warbands within 1 tile when either tribe's268 stance is hostile; each warband fights at most once per tick, ascending id.269- **Orders**: `garrison` (defend a settlement; adds power incl. wall bonus to270 its defense) - `patrol` (intercept hostile warbands/camps within 6 of the271 anchor) - `march` `{x,y}` - `raid` (settlement or caravan; not allies) -272 `besiege` - `escort` (own caravan; defends it against raiders) - `disband`273 (walks to the nearest own settlement, returns pop + gear; supply returns as274 grain).275- **Raid**: within 1 tile, once: vs `pop/10 militia + wall×100 + towers×50 +276 garrisons`. Win → steal up to 2 units/soldier (loot order: coin, food,277 tools, weapons, armor, metal, cloth, clothes, timber, stone; hauled home278 instantly — abstracted return trip), small pop loss, `raid` event; at ≥2×279 defense also destroys one building. Then the warband routs home.280- **Siege** (slow ticks, within 1 tile): garrisons must be beaten first (one281 battle per slow tick vs lowest-id garrison, walls counted). Then walls:282 damage = power per slow tick, 2000 × level to drop a level; **without a283 ram vs walls ≥ 2 damage is quartered**. At walls 0 and no garrison: storm284 vs militia; win captures the settlement (`settlement_captured`, ownership285 transfers, morale 300, overrides cleared).286- **Supply**: 1 food/soldier per 10t. Inside own territory warbands287 auto-refill from that settlement. Outside with empty supply: 1‰ desertion288 per 10t (accumulator) and −20 morale.289290## Threats291292Camps spawn on slow ticks (PRNG placement, ≤8 tries) in wilderness 10–25293tiles from a settlement, while below `min(6, 1 + tick/1500)` camps. Kind by294terrain: swamp → skeleton_barrow, mountain → ogre_den, volcano after tick2956000 → demon_gate, else goblin_camp. Strength = base (20/30/50/90) +296tick/250 + nearbyWealth/5000; +1 per 10t (cap 4× base). Every 100t a camp297raids the nearest settlement within 25: power = strength×8 (halved by an298active warding rite) vs settlement defense; success steals up to2992×strength units and emits a `raid` event with **`by = -1` (threat300convention — documented here)**; ≥2× defense also destroys a building301(`building_destroyed` with `by: null`). Repelled raids cost the camp 1/4302strength. A warband standing on a camp that wins its battle clears it303(`threat_cleared`, +50 veterancy, coin = 2×strength delivered home);304`battle` events against camps use `defender: null`.305306## Logistics & trade307308- **Routes** (`route`, ≤24): each slow tick moves309 `permille × (surplus above keep-level 100)` from → to, capped by hauler310 throughput (20 units per hauler at the source per 10t) and dest space.311 Setting permille 0 deletes the route.312- **Food equalization** (default, needs ≥1 hauler and ≥2 settlements):313 donors above their pop-proportional share of food points push314 grain/fish/meat/bread to settlements below it, through the donors' hauler315 budget.316- **Trade offers** (≤16): matched on slow ticks, canonical ascending offer id,317 one match per offer per slow tick. Requirements: complementary resources,318 non-hostile both ways, counterparty constraints, both tribes have a market319 (market settlement = lowest-id own settlement with one), price cross320 `giveA×giveB ≥ wantA×wantB`. Quantity `q = min(wantA, giveB)`; payment =321 midpoint price × q. Goods travel as caravans (1 tile/tick) owned by the322 **receiving** tribe between the two market settlements; caravans can be323 raided (`caravan_raided`; escorts defend). Non-repeat offers are consumed324 by their first match, `repeat` offers persist.325- **Peddler**: an offer with `tribe: null` and `want: 'coin'` sells instantly326 each slow tick at 25% of base value (40% with coinage) — no caravan, no327 risk. Repeat peddler offers sell every slow tick. Base values per unit:328 grain/fish/timber/stone 1 - meat/hides/wool/flour/planks/bread 2 -329 ore/leather/ale 3 - cloth 4 - metal 6 - tools/clothes 8 - weapons 10 -330 armor 12.331332## Fog & vision333334Settlement vision = territoryRadius + 2; warbands, caravans and wagons 6335(Chebyshev). The explored mask persists per tribe (terrain string shows `?`336elsewhere). Views include foreign warbands/settlements/caravans/threats337inside current vision, plus foreign offers via an own market. `sighted`338events fire per observer/enemy pair at most every 200 ticks. Event delivery:339involved (tribe/by/enemy/attacker/defender) OR `say` OR located inside340current vision; ≤60 newest per view.341342## Score & end343344`score = pop/10 + 40×settlements + 25×techs + warbandPower/20 + coin/20 +3452×buildingsCompleted` (cumulative completions). Elimination at zero346settlements AND zero warbands. Winner: last standing, else highest score at347the tick cap (tie → null).348349## Metrics350351`latestMetrics()` returns the frame computed on the most recent352slow boundary (and on the final tick). `production`/`consumption` are353this-interval flows (reset each frame); births/deaths/starvation likewise.354Battle/trade/caravan counters are cumulative. `toolCoverage` is the ‰ of355extractive workers covered on the last tick. Consumption counts eaten food,356inputs to refinement, wear, build materials, muster gear and warband supply;357loot and trade transfers move stores without counting as either.358359## Documented conventions & deviations360361- Threat raids: `raid.by = -1`; threat battles: `defender: null`.362- Settler wagons (from `found`) appear in views as caravans carrying their363 grain supplies; `found` sources from the own settlement with the largest364 pop (tie lowest id), min 50 settlers, site ≥6 tiles from any settlement.365 A blocked arrival merges the wagon into the nearest own settlement.366- Trade caravans deliver to the destination tile even if the settlement was367 captured en route (the captor receives the goods).368- Granary/warehouse both feed one shared per-resource cap.369- Allocation overrides cannot be unset (set them equal to the tribe value).370- Research switching is free; cost is paid at completion.371- Demolishing walls lowers the level; no refunds anywhere.372- A tribe whose last asset is a settler wagon is still eliminated (spec:373 settlements AND warbands only).374375## Tests & performance376377`npx vitest run` in this package: golden replay (seed 123456789, 4 tribes,378128×128, scripted policy/build/muster/trade/research scenario; pinned hashes379at ticks 0/100/1000), dual-run identity (hashes + event streams + metrics),380apportionment, economy/tools/starvation/population, muster/battle/capture,381threats, trade/peddler, fog/spawn fairness, invalid-order no-divergence,382metrics sanity, JSON round-trips, and a perf gate: 8 tribes × 10,000 ticks383with active policies in well under 10 s (measured ~0.6 s; asserted < 20 s).384385## Organic territory (claims)386387Multi-source bucket Dijkstra from every settlement, recomputed each slow388tick. Claim cost per tile: plains/steppe 1, forest/arctic 2, hills/ore/gold3892, swamp 3, mountain 5, water/river/volcano impassable. Budget by tier390(`TIER_CLAIM_BUDGET` = 5/8/12/16 accumulated cost). Ties: earlier-founded391(lower id) settlement wins. Census (and thus extraction capacity) derives392from claimed tiles; water access stays adjacency-based for docks.393394## Ages (prestige ladder)395396`advance` burns, drawn across your settlements (ascending id):397wandering -> settled: 800 grain, 400 timber, 100 hides;398settled -> crafting: 400 bread, 300 planks, 150 metal, 100 cloth, 100 coin;399crafting -> high: 800 bread, 300 metal, 60 armor, 300 ale, 400 coin, 150 clothes.400Effects per age: +100‰ on every extraction/refinement rate, +3 warband power401per soldier, +60 flat score, and the total score is multiplied by402(1000 + age x 250)‰.