Commit graph

4 commits

Author SHA1 Message Date
lane-l4
4a5c218a35 L4: read the AI's command block out of the running game
New shim module src/shim/hooks/ai_orders.{h,cpp}: one register-transparent entry
stub on StrategySim::ApplyTurnCommandBatch dumps every submitted TurnCommands block
(six gates, 27 list lengths, element bytes) at the point where all of them are
complete in memory; sixteen entry probes, with RunTaskList's stub recording the
pass so every later hit is pass-attributed.

Two workloads on VM145, one End Turn each. The rule-19 control passed with all
seventeen detours installed: both autosaves byte-identical to the published oracle.

What the AI actually emits, and three things no reading had produced:
  - a list-23 element on EVERY turn, the first element ever observed in the free
    half of the cost table -- and both turns still cost the measured 12;
  - the ids in AI commands are client-allocated and travel in the command (design
    18, fleet 34; neither exists in the input save);
  - pass 0 emits nothing, measured from element counts rather than inferred.

tests/game_ai/test_live_blocks.cpp rebuilds both captured blocks through the public
OrderClient API and asserts the list profile, element values, gate counts and
ModCount total: 44 checks. Kept separate from test_orders.cpp, which stays the
record of what static reading predicted.

Gates: clean_room_check OK, host ctest 55/55, CT111 shim cross-build exit 0.
2026-09-08 18:01:19 -04:00
alex
3ca010978c game/ai: the order block, the turn's phase spine, and what a command costs
Adds the half of the AI's turn that is arithmetic rather than judgement: what an
order looks like in the command block, which orders advance the save's
modification counter and by how much, and the phase/pass skeleton the decisions
hang in.

The counter's per-command cost turns out to have a sharp boundary. Applying an
element of command lists 1..16 advances it; applying an element of lists 17..27
does not, and one of the six flag-gated single commands is free as well. So a
uniform per-element cost model is wrong on any turn that touches the free half.

Two behaviours here are not conveniences and change the output:

  * every submitted block costs at least one, because the send-buffer build sets
    the research-rate gate unconditionally whatever the player did. On a quiet
    board that is the largest term in the turn's delta -- four of the ten command
    bumps on the reference turn are exactly this, and one of the four is the
    human's;
  * an AI fleet order costs three where the interface's costs two, because the
    AI's bridge issues the fleet-task command twice, mode 0 then mode 1, and the
    adder keys on (fleet, mode).

The phase spine records the one thing a literal port gets wrong: the turn submits
at phase 28 of 34, the submit latches the client closed before it builds the send
buffer, and every order the last five phases issue -- one of which is a colonize
order -- is refused. Tested through the client rather than by asserting a flag.

The task walk reproduces the two passes: rank once, walk twice, and refuse every
write in the first pass at the client rather than trusting the caller to check.

Nothing here decides anything. Which tasks exist and what each one wants are
questions about the board, and no part of this models the board; the module
supplies the order API, the pass gate and the cost function, and a caller
supplies the decisions.

game/ai tests 233 -> 423 checks; ctest 51/51 -> 53/53. Not linked into the
standalone driver, whose divergence on the reference pair is unchanged at 128.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARBgSooAfokKUy6wKUKEyZ
2026-09-08 16:27:38 -04:00
alex
cc77de3429 merge lane W2: watchpoints + multiplayer Tier 0 (CMake list and main.cpp union-resolved to keep both instruments; header regenerated) 2026-09-08 15:28:28 -04:00
alex
29ea2b65fa game/ai: the strategic AI's task vocabulary and its ordering policy
First module of game/ai, and the first piece of Rung B that is not scaffolding. It is the two
halves of the AI's task selection that are pure: the 33-value task type enumeration and the
ranking that decides which goal the AI acts on first.

  * the priority table, verbatim -- higher runs first, and it is the entire default policy;
  * the five overrides, kept out of the table on purpose. The two artifact tasks ignore their
    table entries (1 and 2) and return 1260/1261; a port that only copied the table would rank
    them last instead of fourth and fifth. The two tuned invade priorities are INPUTS
    (TaskPriorityPolicy), not constants, because their loader is not yet identified;
  * Rank() as a stable descending sort. The original sorts a std::list, so stability is the
    behaviour, not a choice -- ties keep creation order;
  * CreationOrder(species, policyNonZero), because that is what breaks the ties. Four arms: the
    NPC species builds nothing, Hiver is the only arm with the gate families, Zuul the only one
    with NodeBore, everyone else shares a fourth. Both defensive families are gated on the
    player's policy value and DefendGateIncoming is Hiver-only on top of that.

193 checks in tests/game_ai, every expected value read off the original's tables rather than
produced by running this code. ctest 46/46 -> 47/47; clean-room check OK.

Derivation: sots-re findings/subsystems/ai-task-system.md (lane AI2), sections 1-3.
2026-09-08 14:43:51 -04:00