335 lines
21 KiB
Markdown
335 lines
21 KiB
Markdown
# What an AI turn emits, and what each command costs in `ModCount`
|
||
|
||
Lane AI4, 2026-09-08. Program `sots` / "Sword of the Stars.exe", ImageBase 0x00400000, all addresses VAs.
|
||
Static reading of the image plus the corpus saves and **lane W2's watchpoint log**; no VM time of my own.
|
||
|
||
Continues `ai-stepping-and-passes.md` (AI3), `ai-task-system.md` (AI2), `ai-turn-logic.md` (AI1) on the
|
||
emission side, and `alliance-mask-and-modcount.md` (A2) / `watchpoints-modcount-status.md` (W2) on the
|
||
counting side. **The join between those two halves is what this lane is.**
|
||
|
||
Addresses: `ghidra/addresses.d/lane-ai4.json` (validated with `tools/gen_addresses.py` to a scratch path;
|
||
1,138 → 1,152, no duplicate names).
|
||
|
||
---
|
||
|
||
## 0. The three answers, up front
|
||
|
||
**1. `ModCount`'s per-command cost is a property of the command's list, and there is a clean boundary at
|
||
sixteen: applying an element of lists 1-16 bumps the counter, applying an element of lists 17-27 does not.** `ApplyTurnCommandBatch 0x0088f9b0` is not a loop over players containing a loop over
|
||
lists — it is a **flat, hand-written sequence of twenty-seven per-list loops** (`for each player block: for
|
||
each element: apply`), followed by three per-player gate loops, in a fixed order that is neither list order
|
||
nor offset order. Reading the sequence out and asking, at each of the 27 list sites and 6 gate sites,
|
||
whether a `ModCount` bump is on that path gives a complete cost table (§1). A2's rule "one bump per command
|
||
applied" is right in shape and **wrong in detail**: applying a list-17…27 element applies the command and
|
||
bumps nothing, and one of the six prologue gates is free as well.
|
||
|
||
**2. Lane W2's four unnamed handler EIPs are all named, and so are the two inlined ones — and the answer
|
||
makes the ten measured bumps decompose exactly, with no residual.** On `ref-turn2.sav` — which is
|
||
**`turn2-state.sav` under another name**, so this *is* the reference game — the ten command bumps are:
|
||
|
||
| bumps | EIP W2 recorded | what it is |
|
||
|---:|---|---|
|
||
| **4** | `0x00821a87` | the **research-rate gate**, `0x00821a80`, applied once per submitted block |
|
||
| 1 | `0x0086c3e9` | **list 5**, system rates — `0x0086c3e0` |
|
||
| 1 | `0x0084946e` | **list 3**, build order — `0x00849460` |
|
||
| 1 | `0x0088bf01` | **list 10** — `0x0088bed0` |
|
||
| **2** | `0x0089046f` | **list 14**, inlined at `0x0089046c` — the AI's fleet order |
|
||
| 1 | `0x008657ad` | **list 8**, fleet move — `StrategySim::MoveFleetCommand` |
|
||
|
||
**3. That confirms AI2's P1 from the `ModCount` side, at zero VM cost.** AI2 predicted that
|
||
`AI_IssueFleetTask` deposits **two** list-14 elements per fleet where the UI deposits one. List 14's applier
|
||
fired **exactly twice** in a turn in which exactly **one** fleet was moved (one list-8 bump). The UI control
|
||
is `human-turn2-orders.sav`: one UI fleet move, **one** list-14 element. Two producers, two different
|
||
element counts, in the direction predicted. **P1 holds.**
|
||
|
||
---
|
||
|
||
## 1. `ApplyTurnCommandBatch` — the order the twenty-seven lists are applied in
|
||
|
||
`ApplyAllTurnCommands 0x0078f6a0` computes `n = (S->+0x178 - S->+0x174)/0x1b4` and tail-calls
|
||
`0x0088f9b0(blocks, n)` with `ecx = S+4` (the `StrategySim`). The batch's body is a run of near-identical
|
||
blocks:
|
||
|
||
```
|
||
edi = blocks + <listMemberOffset>
|
||
loop over player blocks (stride 0x1b4):
|
||
for (node = list head; node != head; node = node->next)
|
||
playerId = *(int*)(edi - (listMemberOffset - 4)) ; block+0x04
|
||
<handler>(sim, playerId, &node->+0x8)
|
||
edi += 0x1b4
|
||
```
|
||
|
||
so the member offset is written literally in an `add edi, imm8/imm32`, and the player id is fetched with the
|
||
matching negative displacement. That pair identifies each block's list unambiguously. Applied order:
|
||
|
||
| # | list | member | per-element handler | bumps `ModCount`? |
|
||
|---:|---:|---|---|---|
|
||
| 1 | 6 | +0xac | `0x008784e0` | **yes** @`0x008784e3` |
|
||
| 2 | 11 | +0xe8 | `0x00821c30` | **yes** @`0x00821c33` |
|
||
| 3 | 20 | +0x154 | `0x008202e0` | no |
|
||
| 4 | 19 | +0x148 | `0x00821ed0` | no |
|
||
| 5 | 17 | +0x130 | `0x00865a70` | no |
|
||
| 6 | 18 | +0x13c | `0x00865bd0` | no |
|
||
| 7 | 5 | +0xa0 | `0x0086c3e0` | **yes** @`0x0086c3e6` |
|
||
| 8 | 23 | +0x178 | `0x008220b0` | no |
|
||
| 9 | 24 | +0x184 | `0x00822180` | no |
|
||
| — | **gate loop A** | +0x38 | §1.1 | mixed |
|
||
| 10 | 1 | +0x70 | `0x00882910` | **yes** @`0x0088291a` |
|
||
| 11 | 4 | +0x94 | `0x00842f40` | **yes** @`0x00842f43` |
|
||
| 12 | 3 | +0x88 | `0x00849460` | **yes** @`0x0084946b` |
|
||
| 13 | 21 | +0x160 | `0x008659a0` | no |
|
||
| 14 | 2 | +0x7c | `0x0083d5d0` | **yes** @`0x0083d5d3` |
|
||
| 15 | 22 | +0x16c | *(inline)* | no |
|
||
| 16 | 9 | +0xd0 | `0x0085b6d0` | **yes** @`0x0085b70a` |
|
||
| 17 | 10 | +0xdc | `0x0088bed0` | **yes** @`0x0088befe` |
|
||
| 18 | 12 | +0xf4 | *(inline)* | **yes** @`0x008902fe` |
|
||
| 19 | 13 | +0x100 | *(inline)* | **yes** @`0x008903b9` |
|
||
| 20 | **14** | +0x10c | *(inline)* | **yes** @`0x0089046c` |
|
||
| 21 | 15 | +0x118 | `0x00821d70` | **yes** @`0x00821d73` |
|
||
| 22 | 16 | +0x124 | `0x00821e20` | **yes** @`0x00821e23` |
|
||
| 23 | 7 | +0xb8 | *(inline)* + `0x00849280` | **yes** @`0x008905c8` |
|
||
| 24 | 8 | +0xc4 | `0x00865780` `MoveFleetCommand` | **yes** @`0x008657aa` |
|
||
| 25 | 25 | +0x190 | `0x00865d30` | no |
|
||
| 26 | 27 | +0x1a8 | `0x00867980` | no |
|
||
| 27 | 26 | +0x19c | `0x00871030` | no |
|
||
| — | **gate loops B, C** | +0x20, +0x24 | §1.1 | **yes** both |
|
||
|
||
**The rule is clean, and it is a boundary at sixteen: lists 1-16 each cost one bump per element, lists
|
||
17-27 cost nothing.** Not one exception in either direction. Eleven free lists, not the ten a partial read
|
||
suggested; the sixteen paying ones include four whose bump is *inlined into the batch* rather than sitting
|
||
in a handler (7, 12, 13, 14), which is why a call-graph sweep for `ModCount` writers under-counts them.
|
||
|
||
The 1-16 / 17-27 split looks like a real design boundary rather than an accident — the paying half is the
|
||
half whose members lane Q's element records describe as orders against a game object, and the free half is
|
||
where the settings-shaped payloads (weapon groups, defence layouts, populations, raid targets, civilian
|
||
ratios) live.
|
||
|
||
*Honest bound:* "no bump" is read from the handler's own body swept to the next function start (rule 17)
|
||
plus the absence of an inline `inc [reg+4]` in that block. A bump inside a deeper callee of one of those
|
||
handlers would be invisible to this reading; the live control is that on two measured turns exactly six
|
||
distinct sites trapped and all six are in the "yes" column.
|
||
|
||
### 1.1 The three per-player gate loops, and which gates are free
|
||
|
||
The six prologue gates are **not** applied together. Three loops, at three different points in the batch:
|
||
|
||
| loop | at | gate tested | payload | bumps? |
|
||
|---|---|---|---|---|
|
||
| **A** | `0x0088fdb0`, `esi = block+0x38` | `+0x3c` — the three floats | written straight to `player+0x224/+0x228/+0x22c` | **no** |
|
||
| A | `0x0088fdfa` | `+0x14` — **research target** | `+0x10` techId | **yes**, inline @`0x0088fe0a` |
|
||
| A | `0x0088ffb6` | `+0x0c` — **research rate** | `+0x08` float → `0x00821a80` | **yes** @`0x00821a84` |
|
||
| **B** | `0x008907b1`, `esi = block+0x20` | `+0x20` — **research boost** | `+0x18` spend, `+0x1c` fraction → `0x00820560` | **yes**, inline @`0x008907bc` |
|
||
| **C** | `0x0089080a`, `esi = block+0x24` | `+0x2c` — group 4 | `+0x24` bool, `+0x28` int → `0x00821b90` | **yes** @`0x00821b93` |
|
||
| — | *not found in this function* | `+0x6c` — `CivilianRatios` | — | **unknown** |
|
||
|
||
Two things worth pulling out.
|
||
|
||
**The `+0x3c` gate is free, it is AI-only, and it is Hiver-only.** AI2 §4 records `0x00762ca0` (which sets
|
||
it) as having no UI caller, and AI1 §3.1 puts its producer at **Process Turn phase 2**. Reading phase 2's
|
||
body, `0x006c57e0`, adds the third restriction: its **fourth** instruction after the prologue is
|
||
|
||
```
|
||
006c580a mov eax,[esi+0x10] ; the StrategyClient
|
||
006c580d mov eax,[eax+0x150] ; the ClientPlayer
|
||
006c5813 cmp DWORD PTR [eax+0x5c],1 ; Species
|
||
006c5817 jne 0x006c5a28 ; -> straight to the epilogue
|
||
```
|
||
|
||
`player+0x5c` is `Species` and 1 is **Hiver**, so the whole of phase 2 — including a `cl_Chance` draw at
|
||
`0x006c592a` and the `0x00762ca0` call at `0x006c59b6` — is skipped for every other species. That is a
|
||
**fifth** independent cross-check on AI2's species reading (after the Hiver gate creators, the Zuul
|
||
node-bore, the empty NPC arm and AI3's Zuul range exemption), and it explains cleanly why no save has ever
|
||
carried the `+0x3c` gate: the corpus has no Hiver player.
|
||
|
||
**The `+0x6c` `CivilianRatios` gate has no applier in `ApplyTurnCommandBatch`.** AI2 §4 already found it is
|
||
UI-only (`0x007813b0`, from `0x007f1820`). Where it is consumed is open; its `ModCount` cost is **unknown**,
|
||
not zero, and §4 flags it.
|
||
|
||
---
|
||
|
||
## 2. The reference game, decomposed
|
||
|
||
`turn2-state.sav` and `ref-turn2.sav` are **the same file** (`determinism-oracle.md`, and both sha256
|
||
`ab4ac2d7e2977260…`, 66,739 B). So W2's watchpoint run is a measurement of *this* game, and the corpus
|
||
deltas and the trap log describe the same turns.
|
||
|
||
Eight players. Read straight off `turn1-state.sav`:
|
||
|
||
| idx | id | name | `Species` | `Elim` | `Status` | `plcy` | `Status` in `turn2-state` |
|
||
|---:|---:|---|---:|---|---:|---:|---|
|
||
| 0 | 16 | `re` | 0 | F | 0 | 2 | **4** |
|
||
| 1 | 32 | Fane Lao | 2 | F | 0 | 2 | **4** |
|
||
| 2 | 496 | Singularity | 0 | F | 0 | 2 | **4** |
|
||
| 3 | 512 | Singularity | 2 | F | 0 | 2 | **4** |
|
||
| 4-7 | 528/544/560/576 | Alien Menace / Peacekeeper / Von Neumann / Independent Colony | **4** | F | 0 | 2 | 0 |
|
||
|
||
**Exactly four players end their turn, and exactly four research-rate bumps fire.** The four that do are
|
||
indices 0-3; the four that do not are exactly the four `Species == 4` factions. That is a fourth independent
|
||
cross-check on AI2's species reading, from a completely different instrument (W2 §4 recorded
|
||
`MarkPlayerTurnEnded` for players 0 and 1 and no `Status` write for 4-7), and it is the first direct evidence
|
||
that **the NPC/monster factions have no `StrategyClient` at all** — not merely an empty task list.
|
||
|
||
**Three of those four are AI players, not one.** `ai-turn-logic.md` and `alliance-mask-and-modcount.md` both
|
||
describe the reference game as having "one AI player". It has three: 32, 496 and 512. Players 496 and 512
|
||
own nothing (`Sav = 0`, no colonies, no fleets, no systems — `events.md` §5 records that they never hold an
|
||
event) but they **run**: on turn 1→2 all three set `ResRate 0.25 → 0.8` and picked a research target
|
||
(`IND_Waldo`, `DRV_PlsFiss`, `BIO_GnMod`). That is a correction to two published findings, and it is the
|
||
term that makes the arithmetic close.
|
||
|
||
### 2.1 Turn 2→3 (W2's measured window), fully attributed
|
||
|
||
| bumps | source | who |
|
||
|---:|---|---|
|
||
| 4 | research-rate gate | one per submitted block: 16, 32, 496, 512 |
|
||
| 1 | list 5 — system rates | 32 |
|
||
| 1 | list 3 — build order | 32 |
|
||
| 1 | list 10 | 32 |
|
||
| 2 | list 14 — one AI fleet order | 32 |
|
||
| 1 | list 8 — that order's route | 32 |
|
||
| 2 | `ProcessTurn` entry, `OnAllCombatDone_Tail` | — |
|
||
| **12** | | |
|
||
|
||
Zero residual. The human (16) issued nothing and still costs 1, because `BuildTurnCommands 0x00783780`
|
||
sets the research-rate gate **unconditionally** from live player state (AI1 §4.1). 496 and 512 cost 1 each
|
||
for the same reason and nothing else — they have nothing to command.
|
||
|
||
The save agrees: `turn2-state → turn3-state` shows `NMnx 109 → 111` (two ids: ship 1760, fleet 1776),
|
||
`NumDes` unchanged, `srb[0] 1 → 2`, `Maint 500 → 1000`. **A build of an existing design.** No `ResTNm`
|
||
change on any player, which is why the research-target gate never traps in W2's log.
|
||
|
||
---
|
||
|
||
## 3. Predictions (rule 2) — written before the engine module was built
|
||
|
||
### P1 — the cost table generalises: lists 1-16 pay, lists 17-27 are free
|
||
|
||
**Prediction:** on any turn, `ModCount` delta `= 2 + A + Σ_blocks [ rate + target + boost + group4 +
|
||
Σ_{L = 1..16} |list L| ]`, where `A` is the number of systems with `Abdn` set, each gate term is 1 when set
|
||
and 0 when clear, and **lists 17-27 contribute nothing**.
|
||
*Falsified if:* a turn whose block carries an element in one of the eleven free lists moves `ModCount` by
|
||
more than this. *Cheapest test:* not a VM run — a save whose block populates one of the eleven. None exists;
|
||
lists 17-27 have never been observed non-empty (lane Q), so that half of the table is a **hypothesis in the
|
||
rule-6 sense** even though it is read from the instruction stream.
|
||
*Boundary case to check by hand (rule 23):* a block whose only content is the always-set rate gate must cost
|
||
exactly **1**, not 0 — and that is the case that carries four of the ten bumps on this save.
|
||
|
||
### P2 — the composition of `turn1-state`'s twelve
|
||
|
||
`turn1-state → turn2-state` is also 12, but it is **not the same twelve**, and that is the interesting part.
|
||
Three terms are forced by the saves themselves:
|
||
|
||
* **4** research-rate gates — the same four submitting players (all four go `Status 0 → 4`);
|
||
* **3** research-target gates — 32, 496 and 512 all move `ResTNm '' → <tech>`; the human does not;
|
||
* **2** drivers.
|
||
|
||
That is 9, leaving **exactly three** for player 32's real orders. Its observable outcome that turn is a new
|
||
design (`Des[18 "Honor Lance"]`, `NumDes 5 → 6`), one destroyer built at its home system Ke'Dolarra
|
||
(`srb[0] 0 → 1`, `Maint 0 → 500`, `PvSav 50,000 → 38,100`, i.e. 11,900 spent before spine phase 0 — lane
|
||
B6's missing money), and one new fleet (1744, "Alpha Fleet").
|
||
|
||
**Prediction: the three are `list 1` (the new design), `list 3` (the build order) and `list 5` (system
|
||
rates) — and player 32 issues NO fleet order on turn 1: no list 8, no list 14, no list 10.** Equivalently,
|
||
the AI's turn-1 emission is its turn-2 emission minus the whole fleet-order group (list 8 + 2 × list 14 +
|
||
list 10) plus the design command.
|
||
|
||
*Test:* the W2 watchpoint module, unchanged, armed on one End Turn from `turn1-state.sav`. The predicted
|
||
trap multiset is
|
||
`{0x0088fe0a ×3, 0x00821a87 ×4, 0x0088291a ×1, 0x0084946e ×1, 0x0086c3e9 ×1, 0x007dc6f3 ×1, 0x007d92cd ×1}`
|
||
— note it contains **two EIPs W2 has never seen** (`0x0088fe0a`, the research-target gate, and `0x0088291a`,
|
||
list 1) and **omits three it did** (`0x0088bf01`, `0x0089046f`, `0x008657ad`).
|
||
*Falsified if:*
|
||
(a) `0x00821a87` fires other than 4× ⇒ "one block per turn-ending player" is wrong, and the whole per-player
|
||
base cost is wrong with it;
|
||
(b) `0x0088fe0a` fires other than 3× ⇒ the AI's research target is not a `TurnCommands` gate, and `ResTNm`
|
||
is being written by something server-side;
|
||
(c) `0x0089046f` or `0x008657ad` fires ⇒ the AI *did* move a fleet on turn 1, and one of my three is wrong
|
||
— most likely list 5, which is the softest of the three;
|
||
(d) `0x0088291a` does not fire ⇒ **the AI's new design is not a command at all**; it would then be created
|
||
client-side and carried inside the build order, and a design costs zero.
|
||
*Why (d) is the valuable one:* it is the difference between "the engine must emit a design command" and
|
||
"the engine must emit a design *object* the build order names", which are very different amounts of work.
|
||
|
||
### P3 — a Hiver AI writes a command every turn that `ModCount` cannot see
|
||
|
||
Process Turn phase 2 (`0x006c57e0` → `0x00762ca0`) sets prologue gate `+0x3c`; §1.1 shows that gate's
|
||
applier does not bump, and that phase 2 returns immediately unless the player is **Hiver**.
|
||
**Prediction:** a **Hiver** AI player's `TurnCommands` block carries gate `+0x3c` set with three floats,
|
||
every turn, and no `ModCount` accounting will ever reveal it; a non-Hiver AI never sets it. Corollary: the
|
||
corpus can never show this gate, because it has no Hiver player — which is *why* lane Q has it as
|
||
"never seen set". The workload that would settle it is a Hiver AI game, and lane V has already shown
|
||
(rule 6) that manufacturing a save for an unexercised path beats weakening the claim.
|
||
*Falsified if:* a non-Hiver AI capture shows it set ⇒ some other producer reaches `0x00762ca0`; or a Hiver
|
||
AI capture shows it clear ⇒ there is a second gate inside phase 2 that I read past.
|
||
|
||
### P4 — Process Turn phases 29-33 emit nothing, ever
|
||
|
||
`StrategyClient::EndTurn 0x00783be0` sets `this->+0x15c = 1` at **`0x00783c59`**, *before* it calls
|
||
`BuildTurnCommands 0x00783780` (at `0x00783cd7`) and `SendEndTurn`. Every order method opens
|
||
`if (this->+0x15c) return false` (AI1 §4.2). `cl_EndTurn` is Process Turn **phase 28**.
|
||
**Prediction:** phases 29-33 — including phase 32's `0x006af790 → 0x00769640`, the **colonize** order — are
|
||
dead: they run, they call the order method, and it refuses. The AI's colonise orders therefore come only
|
||
from phase 20's task loop (AI3 §5's `AITColonize` path), never from phase 32.
|
||
*Falsified if:* an AI-turn capture shows a list-7 element that can only have come from phase 32, or a hook on
|
||
`0x00769640` shows it returning true after `cl_EndTurn`.
|
||
*Why it matters:* a reimplementation that ports the phase list literally will emit an extra colonize command
|
||
per AI per turn and be one `ModCount` over on every turn with a colony ship.
|
||
|
||
### P5 — `cl_SetResearchTarget` is AI-only, and it is phase 18
|
||
|
||
`0x00578f60` has **exactly one caller in the image**: `0x006caf70`, which is Process Turn phase 18. The UI
|
||
reaches the same order methods (`0x00763110` / `0x00763180`) directly from `0x005d20a0` / `0x005d2140`.
|
||
**Prediction:** extends AI1's P4 — the *façade entry* `0x00578f60` is AI-only surface, and the number of
|
||
research-target bumps in a turn equals the number of AI players whose phase-18 gate opens. On the reference
|
||
game that is 3 on turn 1 and 0 on turns 2 and 3.
|
||
*Falsified if:* the trap count for `0x0088fe0a` on turn 1 is not 3 (this is falsifier (b) of P2, seen from
|
||
the other end).
|
||
|
||
---
|
||
|
||
## 4. What this lane did **not** do
|
||
|
||
1. **Nothing ran under an instrument.** Every new claim is read from the instruction stream or from the
|
||
corpus saves; the live inputs are lane W2's log, not a run of mine. §3 is the measurement plan and it
|
||
reuses W2's module **unchanged** — one save swap and one End Turn.
|
||
2. **The `+0x6c` `CivilianRatios` gate has no located applier** (§1.1). Its `ModCount` cost is unknown, and
|
||
the engine's cost function reports "not exact" when that gate is set rather than guessing zero.
|
||
3. **The eleven "free" lists are free by direct reading only.** No save populates any of them (rule 6), and
|
||
a bump inside a deeper callee would be invisible. The claim is a lower bound on cost, stated as one.
|
||
4. **Which task emits which command is still not modelled** — this lane closes the *cost* of a command and
|
||
the *shape* of a block, not the decision that produces it. Every task's `Execute` body past its first few
|
||
instructions remains unread (AI2 §10.1, AI3 §8), so the engine module emits nothing on its own: it
|
||
provides the order API, the two-pass gate and the cost function, and a caller supplies the decisions.
|
||
5. **List 10 is named only by position.** Its applier `0x0088bed0` sits next to `DestroyFleet` and its
|
||
element is `{i32, i32, counted i32}`; AI3 §0 has `AssignFleetsAndIssueOrders` reaching lists 14, 8 **and
|
||
10** together, and the turn that produced it also produced a brand-new fleet. "Assign these ships to this
|
||
fleet" fits every one of those, and I am **not** naming it on that basis.
|
||
6. **`turn3-state`'s successor was not measured.** W2's second window (values 25→36) is turn 3→4 and no save
|
||
of turn 4 exists in the corpus, so that window's composition is assumed to match turn 2→3 on W2's own
|
||
statement that the two windows agree.
|
||
|
||
> **NAMED 2026-09-09 (rule 11).** It is **`StrategyServer::OnCommand_TransferShips`**,
|
||
> `{systemId, fleetId, ships[]}` — lane W3 named the applier some time ago and these AI-side
|
||
> notes were never updated, so two documents disagreed with a third. The consequence is worth
|
||
> more than the name: **list 10 records the order in which the fleet-assignment pass visited
|
||
> each ship group**, so every captured command block carries its own visit order for free.
|
||
> That is what makes C-exact recoverable as `(save, seeds, visit order) -> byte match` rather
|
||
> than merely canonicalisable — see `findings/resolutions/2026-09-09-fleet-id-order-residue.md`.
|
||
|
||
---
|
||
|
||
## 5. Corrections to earlier findings
|
||
|
||
- **`alliance-mask-and-modcount.md` §2.2 — "one per command applied out of every player's `TurnCommands`
|
||
block".** Right in shape, wrong in detail: **eleven of the twenty-seven lists apply their command without
|
||
bumping** (§1), and four of the six prologue gates bump while a fifth does not and the sixth has no
|
||
located applier.
|
||
- **`alliance-mask-and-modcount.md` §2.5 — "the 10 non-driver bumps are the AI's commands on a two-colony
|
||
board".** Four of the ten are the **research-rate gate**, one per submitted block, and one of those four
|
||
is the *human*'s. Six are AI commands, from one AI player of three.
|
||
- **`ai-turn-logic.md` §8.1 / `watchpoints-modcount-status.md`** — the reference game has **three** AI
|
||
players (32, 496, 512), not one; `ref-turn2.sav` is `turn2-state.sav`; and the four `Species == 4`
|
||
factions submit no `TurnCommands` block at all (§2).
|
||
- **`watchpoints-modcount-status.md` §5 — "four of the twelve `ModCount` writers are unnamed".** All four are
|
||
named (§0.2), as are the two inlined ones.
|
||
- **`ai-turn-logic.md` §3.1 — the Process Turn phase table.** Phases 29-33 cannot emit; `cl_EndTurn` at
|
||
phase 28 latches `client+0x15c` before `BuildTurnCommands` runs (§3, P4).
|