Files

8.2 KiB

Legacy General AI (GeneralAI)

This document summarizes how legacy/hwe/sammo/GeneralAI.php selects nation and general commands, which data fields it relies on, and how policies shape NPC behavior. It also outlines considerations for an in-memory rewrite of the AI loop.

Entry Points

  • chooseNationTurn(NationCommand $reservedCommand)
  • chooseInstantNationTurn(NationCommand $reservedCommand)
  • chooseGeneralTurn(GeneralCommand $reservedCommand)

All three call updateInstance() first, which caches current state and derives key AI decisions (diplomacy state, general type, policy setup).

Deterministic RNG

GeneralAI uses LiteHashDRBG seeded with:

hiddenSeed + "GeneralAI" + year + month + generalID

This makes AI choices reproducible per turn. All random choices (choice, choiceUsingWeight, nextBool) flow through this RNG.

State Snapshot and Derived Fields

updateInstance() pulls and caches:

  • game_env (via KVStorage): year, month, startyear, turnterm, develcost, init_year/init_month, killturn, global NPC policy defaults.
  • city (current city row, from general -> city).
  • nation (nation row, or fallback for neutral).
  • nation_env (KVStorage): npc policy overrides, prev income, last attackable.
  • nation['aux'] decoded JSON.
  • general primary stats (leadership/strength/intel; both full and adjusted).
  • baseDevelCost and maxResourceActionAmount.
  • dipState, attackable, warTargetNation.
  • genType (type flags derived from stats + policy thresholds).

Derived groupings are computed lazily:

  • categorizeNationCities() => nationCities, frontCities, supplyCities, backupCities with per-city dev and important scores.
  • categorizeNationGeneral() => user/NPC buckets, war/civil buckets, troop leaders, lost generals, chief list.

Diplomacy State (calcDiplomacyState)

Diplomacy state controls war/peace behavior:

  • d평화: no war declarations, no active war.
  • d선포: declaration in progress, no active war.
  • d징병: pre-war recruitment window.
  • d직전: immediate pre-war.
  • d전쟁: active war, or recently lost a front (grace period).

Key inputs:

  • diplomacy rows (state, term) for current nation.
  • front cities with supply=1.
  • year/month relative to startyear (early-game war lockout).

warTargetNation tracks eligible enemies:

  • 2 = currently at war.
  • 1 = declaration in progress / pre-war.
  • 0 is used as "any neighbor" fallback when no active target exists.

General Type (calcGenType)

General type is a bitmask:

  • t무장 (strength-leaning)
  • t지장 (intelligence-leaning)
  • t통솔장 (leadership threshold for war-capable NPCs)

Logic:

  • Strength vs intel decides initial bias.
  • If the weaker stat is within 80%, a probabilistic hybrid type can be added.
  • t통솔장 is set when leadership >= nationPolicy->minNPCWarLeadership.

Policy Inputs (Autorun)

AutorunNationPolicy and AutorunGeneralPolicy are built from:

  • per-user autorun options (env['autorun_user']['options'])
  • nation_env overrides (npc_nation_policy, npc_general_policy)
  • global defaults (env['npc_nation_policy'], env['npc_general_policy'])
  • live nation + env snapshot

Policies provide:

  • priority action lists
  • can{Action} toggles
  • thresholds (min crew, safe population ratio, resource floors, etc.)

Nation Turn Behavior (chooseNationTurn)

Nation-level choices run only for NPCs (npc >= 2) or for autorun users:

  1. Apply quarterly promotions and tax/bill adjustments.
  2. Honor reserved nation commands if valid.
  3. Iterate policy priority, invoking matching do{Action}.
  4. Fall back to neutral (empty) nation command if all fail.

Major Action Groups

  • Troop movement
    • do부대전방발령, do부대후방발령, do부대구출발령
    • user/NPC versions to move generals between front/back/supply cities
    • checks frontCities, supplyCities, last발령, and war route.
  • Resource distribution
    • do유저장포상, doNPC포상, doNPC몰수
    • uses resource floors (reqNation*, reqNPC*, reqHuman*)
    • sorts by each general's gold/rice, excludes inactive (killturn <= 5) targets, and preserves the legacy geometric-mean amounts and candidate weights.
  • Diplomacy
    • do불가침제의: respond to assistance requests with NAP offer.
    • do선전포고: probabilistic declaration when strong enough.
  • Capital relocation
    • do천도: moves capital based on population, dev, and connectivity.

General Turn Behavior (chooseGeneralTurn)

General-level decisions are layered:

  1. NPC message broadcast if npcmsg and RNG triggers.
  2. Reserved command is honored if valid (unless 휴식).
  3. Immediate recovery if injury > cureThreshold.
  4. Special cases:
    • A nationless NPC troop leader shortens killturn and keeps its reserved command; an affiliated type-5 leader refreshes killturn and uses 집합.
    • wandering lords decide on founding, one-edge movement toward a cached target, or disbanding.
  5. Iterate policy priority, invoking do{Action}.
  6. Fallback to do중립.

Major Action Groups

  • Domestic development
    • do일반내정, do전쟁내정, do긴급내정
    • weighted by city dev rates and general type flags.
  • War preparation
    • do징병, do전투준비, do출병
    • strict checks on crew, train, atmos, population and diplomacy state.
  • Mobility
    • do전방워프, do후방워프, do내정워프, do귀환
    • uses front/supply/backup cities and population thresholds.
  • Resource handling
    • do금쌀구매 (trade), doNPC헌납 (donation).
  • Neutral behavior
    • do중립 selects between 물자조달, 인재탐색, 견문.

Data Fields Accessed (Representative)

GeneralAI reads or writes:

  • Nation: nation, capital, gold, rice, tech, level, type, chief_set, rate, bill, aux.
  • City: city, nation, supply, front, pop, pop_max, trust, agri/comm/secu/def/wall and *_max, trade, level, region.
  • General: npc, officer_level, officer_city, killturn, injury, troop, crew, train, atmos, defence_train, gold, rice, belong, permission, npcmsg, dex1..dex5, armType, affinity, makelimit.
  • KVStorage: npc_*_policy, prev_income_*, last_attackable, last천도Trial, resp_assist*.

In-Memory Rewrite Considerations

To port the AI to an in-memory state model without behavior drift:

  • Snapshot-first
    • Build a per-turn GameSnapshot containing env, nation, cities, generals, diplomacy, and nation_env. GeneralAI should read only from this snapshot.
  • Derived caches
    • Cache DiplomacyState, CityBuckets, GeneralBuckets, and WarRoute.
    • Use lazy recalculation and invalidate only the affected region/city/general after a command is applied.
  • Deterministic ordering
    • For candidate lists, sort by ID before weighted RNG to preserve parity.
    • RNG seeding should keep the exact per-turn seed scheme to ensure replay.
  • Command evaluation
    • Keep hasFullConditionMet() semantics intact by providing the same generalized context (general, city, nation, dest*).
  • Policy snapshots
    • Cache policy values per turn and avoid reading KVStorage per action.
    • When policies change, treat it as an explicit state transition.

These guidelines mirror the current "derive once, then select via priority" pattern and minimize resimulation deltas in the rewrite.

Migrated decision-parity regression

app/game-engine/test/generalAiLegacyDecisionParity.test.ts records focused final-command expectations extracted from ref/sam ng_compare@fe9ae978. Its matrix varies diplomacy/war state, city development and population, technology/year ceilings, general gold/rice and casualty ranks, stats and affinity, reserved/special NPC state, nation treasury reserves, and command availability. It also asserts RNG-sensitive candidate weights where consuming the same random branch is part of the final decision.

This is compatibility evidence for the represented decision branches. The long-running NPC scenario suites remain smoke tests and are not a substitute for this branch-level matrix.