L4: predictions for the research-selection tie set, before the instrument
This commit is contained in:
parent
c769394b57
commit
ec8d841dba
1 changed files with 92 additions and 0 deletions
|
|
@ -244,3 +244,95 @@ happened to.
|
|||
5. **The dump's own coverage check**: for every list it walks the node chain *and* reads `_Mysize`,
|
||||
and logs a `MISMATCH` line if they disagree. A wrong list layout would otherwise print a
|
||||
confident, wrong zero (rule 1).
|
||||
|
||||
---
|
||||
|
||||
# Addendum — the research-selection tie set
|
||||
|
||||
Added after the first two runs, before the research instrument was written. The coordinator's
|
||||
question: if the one varying AI decision is a **tie** broken by something per-process, the
|
||||
original's possible outcomes form a small enumerable set, and a deterministic `game/ai` can pick
|
||||
canonically and claim *"our choice is one of exactly k the original can produce, and here are all
|
||||
k"*. That is a stronger claim than behavioural equivalence, and it needs the candidate set.
|
||||
|
||||
## What the selection site actually is (read before predicting)
|
||||
|
||||
Process Turn phase 18 is `0x006caf70`. It tries three producers in order and takes the first
|
||||
non-null:
|
||||
|
||||
```
|
||||
eax = 0x006a84f0(agent) ; producer A
|
||||
if (!eax) eax = 0x006c27c0(agent) ; producer B
|
||||
if (!eax) eax = 0x006c8890(agent, &agent+0x13c); producer C -- the candidate walk
|
||||
if (eax && eax != player->+0x294) cl_SetResearchTarget(<eax+4 as a C string>)
|
||||
```
|
||||
|
||||
`player->+0x294` is the current target, and `cl_SetResearchTarget 0x00578f60` takes the tech's
|
||||
**name string**, not an id — the object's `+0x4` is a `std::string` and the caller resolves the
|
||||
short-string union before pushing it.
|
||||
|
||||
Producer C, `0x006c8890`, has two halves:
|
||||
|
||||
**C1, the candidate walk.** `if (player->+0x294 != 0) return 0`. Otherwise build a `std::vector`
|
||||
of 0x0c-stride candidates with `0x006c2490` — which constructs a working object, fills it
|
||||
(`0x006bcca0`), emits the vector (`0x006bc500`, a nested walk over groups, **no `std::sort` at that
|
||||
level**) and destroys it — then walk the vector **front to back** calling
|
||||
`0x006c8580(ecx = the target slot, edx = cand[1], stack: agent, cand[0])` and **return the first
|
||||
one that answers non-zero**. First-acceptable in arrival order. No score, no comparator, no tie
|
||||
resolution: **whatever the vector's order is, is the answer.**
|
||||
|
||||
**C2, the fallback**, reached only when the walk accepts nothing: `0x006b36e0(agent) & 0x80000007`
|
||||
indexes one of two eight-entry `.data` tables, and the value there seeds a three-arm rotation
|
||||
`(i + seed) % 3` over `0x006c8670`. Both tables are in the image and both hold values in {0,1,2}:
|
||||
|
||||
```
|
||||
0x00a1a544 = 2 0 2 0 2 1 2 0
|
||||
0x00a1a564 = 0 2 0 2 1 0 2 0
|
||||
```
|
||||
|
||||
`0x006b36e0` is not an RNG call — it reads `player->+0xf4`, calls `0x0080da80` and computes. So C2's
|
||||
"roll" is a **hash of player state**, and its outcome space is at most **three arms**.
|
||||
|
||||
## Predictions
|
||||
|
||||
### P10 — where the variation lives
|
||||
|
||||
**Prediction: `0x006c8890` (producer C) is entered once per AI player on turn 1 and the variation is
|
||||
in C1's candidate vector, not in C2.** Specifically, across two runs of the same workload:
|
||||
|
||||
* players 32 and 496 (stable across five runs between lanes L4 and L5) produce **identical candidate
|
||||
streams in identical order**, and the accepted candidate sits at the same position;
|
||||
* player 512 produces a candidate stream that is **the same set in a different order**, and the
|
||||
first-acceptable one is therefore a different tech.
|
||||
|
||||
*Falsified if:* the streams are identical for 512 and the accepted position still differs ⇒ the
|
||||
decision is inside `0x006c8580`, i.e. state or a seed, and arrival order is not the mechanism.
|
||||
*Also falsified if:* `0x006b36e0` fires for 512 ⇒ the pick came from **C2**, which is not a tie at
|
||||
all but a three-arm rotation seeded by a state hash, and `k <= 3` by construction.
|
||||
|
||||
### P11 — k is enumerable, and small
|
||||
|
||||
**Prediction: the tie set for player 512 is the set of candidates in its stream that
|
||||
`0x006c8580` would accept, and the stream is short — tens, not thousands.** A player with no
|
||||
colonies and no research history has few reachable techs.
|
||||
|
||||
*Falsified if:* the stream is hundreds of entries ⇒ "name all k" is not a practical verification
|
||||
claim for this decision and the honest move is to mask the leaf, as lane L5 already does.
|
||||
|
||||
### P12 — the two stable players are the control
|
||||
|
||||
**Prediction: 32 and 496 accept a candidate at position 0 or very near it**, because a player whose
|
||||
choice never varies is one whose first candidate is always acceptable. If instead they accept deep
|
||||
in the stream and are still stable, then order is stable for them and unstable for 512 — which
|
||||
would point at 512's *container*, not at the walk.
|
||||
|
||||
### P13 — what our engine should do
|
||||
|
||||
If P10 holds, a deterministic `game/ai` sorts the candidate vector by a canonical key (the tech
|
||||
name, which is what the original ships in the command anyway) before the walk. The verification
|
||||
claim becomes: **our pick is the canonical member of the tie set; every observed original run picks
|
||||
some member of the same set; here is the set.** If P10 is falsified in the C2 direction instead, the
|
||||
claim is different and weaker in kind but stronger in size: three arms, both tables in the image,
|
||||
so k <= 3 and all three are nameable without any capture at all.
|
||||
|
||||
*Either way the capture names k.* That is the point of running it.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue