sots-engine/docs/AC-predictions.md

227 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Lane AC — predictions, written before the workload was built
Rule 2. Committed before the game was started, before the first End Turn, and before any probe
counter was read. Guest VM141 (`sots-re-win10-b`, `192.168.10.143`), starting from lane L3's
`human-turn15-spyprogram.sav`.
The question: **does the strategic-turn tail draw RNG when the trade and spy containers hold
*active* contents rather than merely present ones?** Lane L3 retired lane W3's risk for populated
containers; the residual condition it named is freighters in service (`tsnumflt > 0`) and a
deployed spy (`spyon != 0`).
---
## 0. One fact from the save that changes the prior before anything is run
Lane L3's §3.5 says *"every trade sector has `tsnumflt = 0` — no freighters in service"*. Read out
of the save L3 shipped, that is **not true**. `human-turn15-spyprogram.sav`, `trdmgr`, sector
`TradeID 816` (`tsgridID 5`):
```
tssec 2 tsct 2 tsnumflt 1 tsflt 2736
```
and the fleet with id `2736` is `Freighters`, `PID 32` — the **AI**'s. Its six member systems
(`tssys` 192, 288, 304, 336, 352, 464 → indices 9, 15, 16, 18, 19, 26) are all AI-owned. The
single serialized route record `trdmgr.rt` (`tro 32, trfow 1, trto 16, trtow 1, trfr 18, trtc 10`)
is likewise the AI's.
So across L3's seven instrumented turns the game **already had a trade sector with a freighter in
service and an active route**, and `TradeManager::Slot13RngCalleeA/B` were entered **0** times
anyway. The five sectors with `tsnumflt = 0` are the ones the human player owns or shares.
This does not make the experiment pointless — it sharpens it. It rules out "any freighter anywhere"
as the gate before I spend a turn, and it means the surviving trade-side hypotheses are narrower:
a per-player gate, a count threshold, a route-*state* condition (`TROUTESTATUS_*` is
Inactive/New/Developing/Established/Shutting Down), or the raid-candidate list being the thing that
is empty rather than the roll being un-taken.
---
## 1. Predictions
### P1 — the trade half (primary)
**Freighters in service on the human player's own trade sectors will NOT make
`TradeManager::Slot13RngCalleeA` (0x00820ca0) or `Slot13RngCalleeB` (0x0088b440) fire.** Both stay
at `calls = 0` on every instrumented End Turn, while `ServerTradeManagerImpl::vslot13` (0x0088ef80)
and `vslot15` (0x0082cca0) stay at exactly 1.
Confidence: **moderate-to-high**, and it rests on §0 rather than on L3's stated reason. An AI
freighter has already failed to trip it for seven turns; the only way a human freighter trips it is
if the gate is per-player, which is possible (`vslot13` may loop players) but is not the simplest
reading of "entered once per turn".
*Falsification and its symptom.* If A or B reports `calls >= 1` on the first turn after
`tsnumflt` goes non-zero on a human sector, P1 is dead and the answer to the lane's question is
"yes, the tail draws under active contents". Symptom: a non-zero `calls` in the `probe_entries`
table on the post-turn `Autosave` record, with `installed = true` on that row.
### P2 — the trade half, second order
If P1 holds, then either (a) the gate is not "a freighter exists" at all, or (b) it is a threshold
above 1. **I predict (a).** Falsifier: if the callees fire only once the human side reaches
`TRADE_ROUTE_MAX_FREIGHTERS = 5` on a sector, (b) is right and P2 is wrong; symptom is a step
change in `calls` exactly at the turn `tsnumflt` reaches 5.
### P3 — the spy half
**A deployed spy (`spyon != 0`, `tdep >= 0`, `spyat` set) WILL make
`SpyManager::Slot13RngCallee` (0x008408e0) fire, at 1 entry per turn per deployed spy.**
Confidence: **low-to-moderate**, and deliberately opposite in sign to P1. The reason to split them
is that the spy path has a per-turn stochastic element the trade path does not obviously have —
`sdet` ("spy detection", −1 in the save) and `deat` are per-spy fields that a deployed spy must
have rolled for each turn, and a detection roll is exactly the shape of thing
`Slot13RngCallee` would be. `cbh`, `cm`, `cmo`, `ncp` are all still zero on the undeployed spy.
*Falsification.* If a spy reaches `spyon != 0` and `Slot13RngCallee` still reports `calls = 0` on
the following End Turn, P3 is dead and both containers are confirmed inert at every reachable
state — which closes lane W3's risk outright. Symptom: `calls = 0` with `installed = true` on that
row while the save shows `spyon != 0`.
### P4 — `spies2`
**`spies2` will become non-empty in exactly the system a spy is deployed at, and nowhere else.**
i.e. `spies2` *is* a per-system spy list and it fills only for a **deployed** spy. That is the
second of the two readings L3 left open, and it is the one I expect.
Confidence: moderate. `spies2` sits on `ServerSystem`, and a built-but-docked spy has no system to
belong to (`tdep = -1`, `spyat = 0`), so a container keyed by system cannot hold it.
*Falsification.* If a spy is deployed and `spies2` is still `0` in all 28 systems, then `spies2` is
**not** the spy list and the first reading is right; the rule-6 flags on `spies2`, `SysMem` and
`mts` stay up and the field needs a different explanation. Symptom: post-deploy save parses with
`spies2` count 0 everywhere while `spymgr.spy.spyon != 0`.
### P5 — reachability of the active spy state at all
**Deploying a spy in this save may be unreachable, and if so I will say so rather than infer.**
The engine's own refusals require a system that is *not mine*, has *no enemy fleet present*, has
*no spy already*, and **has an asteroid belt**. `ARes2` on `ServerSystem` is the asteroid-resource
field (its non-zero values 1703–2531 sit inside `SYSTEM_MIN/MAX_ASTEROID_RESOURCES` 1500–3000).
In the turn-15 save only five of 28 systems have `ARes2 > 0`:
| idx | id | name | `ARes2` | owner |
|---|---|---|---|---|
| 1 | 64 | Ka'Palum | 2061 | unowned |
| 4 | 112 | Regulus | 1703 | unowned |
| 10 | 208 | Kam'Tara | 1948 | unowned, **AI fleet present** (Omikron Fleet II, 4 ships) |
| 12 | 240 | Downbelow | 2172 | **mine** |
| 27 | 480 | Hitomi | 2531 | player 576, `AFlags 128`, position (0,0,0) |
**No AI-owned system has an asteroid belt.** So the textbook target — an enemy colony with a belt —
does not exist here. I predict the reachable routes are, in order: deploy at an **unowned** belt
system (Regulus is 9.01 ly from the tender's berth at Epsilon Eridani against a hull range of 9.0,
so this is marginal and node travel may or may not save it); or wait for the AI to colonize
Kam'Tara, where it already has a fleet, and deploy once its fleet leaves.
*Falsification.* If the Special menu offers `Deploy Spy` at an unowned belt system, P5's pessimism
is wrong and the path is short. Symptom: the menu entry is enabled and the action is accepted.
---
## 2. What would make me wrong about the whole framing
- **The outer callees may not iterate the containers that were measured.** `vslot13`/`vslot15` are
entered once per turn regardless of container contents, which is equally consistent with their
loop being over *players* or over *encounters* and the trade/spy containers being read further in.
If so, "active contents" is the wrong axis entirely and no workload on these two vectors will ever
trip the inner callees. Symptom: a static read of the gate shows a predicate on neither container.
- **The instrument may not be armed.** `probes=11` is L3's config and is line-for-line comparable
with lane H, but lane H bisected `probes=11` as **not** determinism-neutral (the detour on probe
index 8, `GenerateTradeRaidEncounters`, suppresses a draw elsewhere). `probes=8` is the largest
proven byte-neutral configuration and it still installs indices 0–7, i.e. **all four outer callees
and all three inner ones**. I will take the primary reading at `probes=8` for that reason and
cross-check at `probes=11` for comparability, and I will report `installed=` for every row rather
than a bare zero (rule 20; and lane L3's own `hooks=off watch=on` defect is the worked example of
a config that reports a confident nothing).
- **A control that does not agree with itself is not a control** (rule 26). The `watch=on`/`watch=off`
pair goes on `ref-turn2.sav` in two fresh processes before any workload number is trusted.
---
## 3. Addendum — written after the static decode of the gates, before any measurement of an active state
The gates on all three inner callees were decoded out of the instruction stream (`sots.exe`,
image base 0x00400000) before the workload was played. That changes what is worth measuring, so the
new predictions are written here rather than fitted afterwards. **Nothing below had been measured
when it was written**; the only live readings so far are the VM141 determinism-oracle control
(matched the published hashes exactly) and the shim banner showing the watchpoint module armed.
### The decoded gates
`Slot13RngCalleeA` (Q, 0x00820ca0) and `Slot13RngCalleeB` (R, 0x0088b440) are called **directly**
from `ServerTradeManagerImpl::vslot13` (0x0088ef80) at 0x0088f036 and 0x0088f042. That function
does **not** iterate trade routes or trade sectors — it iterates
`StrategyServer+0x64..+0x68`, the **fleet** vector, and for each fleet requires:
- `StarFleet+0xa0` (`LocID`) to resolve to a node whose kind word (`node+0x14`) is **2**. Kind is
set by the `StarMapNode(int)` ctor: `StarSystem` passes 0, `StarFleet` 1, **`TradeSector` 2**. So
the fleet must be located *at a trade-sector node*, not at a star.
- the fleet's `Pos` (`+0x18`) to be **bit-equal** to the sector's `Pos` — arrived, not in transit.
- `sector+0x90` (`tscr`) to have the **fleet owner's player-index bit set**;
- and if `sector+0x8c` (`tsct`) also has that bit, the fleet to carry `FtFlg & 0x800`.
`SpyManager::Slot13RngCallee` (P, 0x008408e0) is called from `ServerSpyManager::vslot13`
(0x008877b0) at 0x00887af2, behind: `deat != 0` (deployed at a real system), `sdet != -1`
(**already detected on an earlier turn**), `turn - sdet >= 3`, the system still owned, and owned by
someone other than the spy's owner.
### P6 — the trade half is structurally closed to both real players in this save
`tscr` is **252 = 0b11111100** on all six sectors of `human-turn15-spyprogram.sav`, and the save has
eight player slots: index 0 `re` (human), index 1 `The Eternal Empire` (AI), and indices 2–7 the
six `NPC = true` slots (`Spengler` ×2 rebel-AI, `Alien Menace`, `Peacekeeper Enforcer`,
`Von Neumann`, `Independent Colony`). **252 is exactly the NPC set and excludes both real players.**
So I predict: **parking a player-owned fleet exactly on a trade-sector node — even flagged as a
raider — will NOT make Q or R fire.** Trade raiding is an NPC mechanic; `TRADE_RAID_ODDS_NPC` and
`TRADE_RAID_ODDS_REFUGEE` exist for that reason and `TRADE_RAID_ODDS_PLAYER` is what a *raid against*
a player pays.
Falsifier: Q reports `calls >= 1` on the turn after a player fleet is parked on a sector node.
Symptom: non-zero `calls` on the `TradeManager::Slot13RngCalleeA` row with `installed = true`.
That result would kill the `tscr`-is-a-player-mask reading and reopen the ledger.
This is the experiment worth running because it separates two explanations that seven turns of
L3's workload could not: "no lane ever parked a fleet on a sector node" versus "players are excluded
by `tscr`". The AI's own `Freighters` fleet (`LocID 816`, the id of trade sector `TradeID 816`,
`Pos` bit-equal to that sector's) **already passes the node-kind and position gates** and is still
rejected — which is the first evidence for the mask reading and is why P6 is stated with confidence
rather than as a coin flip.
### P7 — the spy half WILL make the tail draw, but not through an entry probe
This is the prediction that matters most, and it is the one L3's instrument could not have seen.
The `sdet == -1` arm of `vslot13` (taken at 0x00887856 → 0x00887c5b) is the **detection roll**:
it calls `FUN_0081f570(spy, system, owner)`, tests `FUN_00743f80(system) & 1`, and on that bit rolls
`FUN_008e6dd0(StrategyServer+0x16c /* RNG */, spy->sdo)`, then stamps `sdet = turn` and posts
`EVENT_SPY_DETECTED`. That roll is **inline in the outer callee**, not inside P.
But it is unreachable today because the gate *before* it is `deat != 0` at 0x0088781d, and the
corpus spy has `deat = 0` (`tdep = -1`, `spyon = 0`, docked to its tender).
So: **deploying the spy is predicted to make `OnAllCombatDone_Tail` consume more RNG words per turn
than it does with the spy docked, while all three inner-callee entry probes still read 0.**
Roughly one extra draw per deployed spy per turn, conditional on `FUN_00743f80(system) & 1`.
If that holds, the honest headline is **yes, the tail draws under active contents** — the RNG ledger
and the standalone's generator model do need extending — and the reason seven instrumented turns
said otherwise is that the probe table measures *entry into P/Q/R* while the draw lives in the
outer function. That is rule 20 pointing the other way: instrumenting the entry is necessary and
was not sufficient. **The primary measurement is therefore the `side.rng` word delta on
`OnAllCombatDone_Tail`, not the probe counts.**
Falsifier: the tail's word delta is unchanged after deployment. Symptom: identical
`rng.after.words - rng.before.words` on the tail hook across the docked and deployed turns of the
same save lineage.
### P8 — where the spy can go
`ARes2` is the asteroid-belt field, and the only non-own system with a belt in this save is
**Hitomi** (idx 27, id 480, `ARes2 2531`), owned by player index 7 `Independent Colony`
(`AFlags 128`). Not the AI. So the deploy target, if one exists at all, is Hitomi — two fleet moves
from the tender's berth at Epsilon Eridani via Aldebaran. If Hitomi is undiscovered or unreachable,
the active spy state is out of reach in this save and I will report that rather than infer around it.