107 lines
8.1 KiB
Markdown
107 lines
8.1 KiB
Markdown
# Roadmap
|
||
|
||
Groomed 2026-09-08. Replaces the phase-0/phase-2 backlog, all of which shipped. The board
|
||
(`board.md`) is the ledger of findings; this is the plan. **The product is `sots-engine` — our
|
||
code.** RE findings are inputs to it, and the measure of progress is how much of the game runs in
|
||
our engine, verified against the original. Every number here is measured; the source is named.
|
||
|
||
## 1. The engine today (`sots-engine` main `0592104`, 31,980 LOC, ctest 45/45, clean-room, MIT)
|
||
|
||
| module | LOC | what it is | verification |
|
||
|---|---:|---|---|
|
||
| `mars/parse`, `mars/text` | 1,774 | brace-block + flat-KV + CSV readers (the `Mars::Script` pull tokenizer, first-wins keys) | oracle 1,531/1,531 + 64/64 |
|
||
| `mars/vfs` | 788 | `.gob` ZIP + native-override VFS (the `./Mods` mechanism) | 10,268 files CRC-clean |
|
||
| `mars/stream` | 5,407 | save reader/writer, 386-class wire schema, `SchemaProbe`, `CoverageArchive` | **100% named coverage, byte-identical round-trip, 11 saves** |
|
||
| `mars/rng` | 244 | MT19937 + all seven entry points (`NextFloat/NextInt/Chance/NextUInt/FloatRange/IntRangeBell/GaussianRange`) | live: every draw of a turn attributed |
|
||
| `game/data`, `game/design`, `game/config` | 3,835 | weapons/sections/tech/strings catalogs, design rules, hull class, tuning | 229,042 values 0 diffs; 127/127 designs |
|
||
| `game/effects`, `game/events` | 1,531 | tech effects, `EventStorage` (turn-bucketed, dedup) | live: research slice 35 calls / 0 div |
|
||
| `game/sim` | 3,382 | economy (`ComputeBudget`, output term), research + unlock cascade, colony, movement | live: budget 4,437 / output 24,357 / research 35 / movement 45 calls — all 0 div on declared regions |
|
||
| `game/nav`, `game/combat` | 1,163 | route classifier + failure bits; retreat **planner** | offline vs 58 waypoints / 46 plans, 0 failures; retreat never instrumented |
|
||
| `app` (`sots_turn`) | 1,754 | **the standalone**: load save → walk all three drivers' phase order → write save; divergence + completion metric | 11/11 saves run; ref pair **209 → 204** leaves, closed 5 / regressed 0 |
|
||
| `shim` | 769 + hooks | proxy `binkw32.dll` + MinHook trace/compare/replace harness — **scaffolding, to be displaced** | it is the instrument, not the product |
|
||
|
||
What the engine can do end to end today: read every shipped asset and every save byte-for-byte,
|
||
run the economy/research/movement/output pieces to the original's numbers, and step a turn with
|
||
**14 of 44** driver phases and **2 of 37** tail phases modelled (8 committed). It cannot yet
|
||
reproduce a turn.
|
||
|
||
## 2. The milestone, restated
|
||
|
||
> `sots_turn` loads a save, runs one strategic turn, and writes an autosave that byte-matches
|
||
> the original's.
|
||
|
||
Today showed the milestone as first stated is **underdetermined on the reference pair**:
|
||
`ModCount` = 2 + one per command applied from every player's block, and on `turn1-state.sav` the
|
||
human block is empty yet 10 non-driver bumps occur — **the AI's orders are generated during the
|
||
turn, after the autosave**. `Summary.Checksum` is fed by `ModCount`. So, two rungs:
|
||
|
||
- **Rung A — byte-match modulo the command stream.** Every leaf except `ModCount`,
|
||
`Summary.Checksum`, and what the AI's own orders touch. **No AI needed.** Near target.
|
||
- **Rung B — full byte-match.** Needs AI order generation in our engine (§4.1), or recorded AI
|
||
command blocks fed as input. Rung B is where the engine becomes the game.
|
||
|
||
## 3. Engine work to Rung A (ranked by leaves closed per lane)
|
||
|
||
| # | engine deliverable | module | RE input (done unless noted) | closes |
|
||
|---|---|---|---|---|
|
||
| 1 | **income chain**: `TradePointsToMoney` (`incomeModifier/14000`) + AI difficulty ×1.1 | `game/sim` | lane N/Y; 25-record `BnkEl` oracle, 6/25 match now; **multiplier not on the wire — one Ghidra read** | P01/P02/P03/P05/P06/T31, ~82 of 204 leaves, 2 RNG words |
|
||
| 2 | **wire `ShipCensus` + alliance mask into `BuildTurnRecord`**, commit T36 | `app` | lanes D2/A2, 480+560 fields 0 mismatch | +24 turnstats leaves, clean |
|
||
| 3 | **encounter-detection draws** in the generator model | `game/sim` + `app` | lane I bound; **lane H measuring now** | last 2 RNG words |
|
||
| 4 | **`nve` visibility record** | `game/sim` | one mechanism ×8, unread | 32 leaves |
|
||
| 5 | **post events into the save's turn bucket** (P11 + tail event phases) | `app` + `game/events` | model exists; wiring only | events subtree |
|
||
| 6 | **`Player.Status` restore** (S31) | `app` | needs the writer between tail 31 and autosave — **watchpoint** | S31 regression |
|
||
| 7 | **tail phases** — bankruptcy decision half, turn results outbox swap, per-player sync | `game/sim` + `app` | lane K map; 34/37 stubs | tail subtree |
|
||
| 8 | Rung B: **AI order generation** | new `game/ai` | §4.1 | `ModCount`, Checksum |
|
||
|
||
Rules that hold for every item: formula-held-inputs-missing is **evaluated and reported, not
|
||
written**; `closed` and `regressed` reported separately, never netted; `verified` in the phase
|
||
table means compared against the live game and the build fails if it drifts silently.
|
||
|
||
## 4. Future engine modules (breadth, ranked by what it unblocks)
|
||
|
||
1. **`game/ai`** — `AIPlayer`, fuzzy `IAIRule`, `StrategyAIAgent`. Essentially unread; **proven
|
||
to gate Rung B**. Known: `AISystem::Write` emits nothing; the 3 `AIAgent` save blocks are cache
|
||
state (typed, 10 element layouts unexercised).
|
||
2. **`game/combat`** beyond the planner — resolver (7,641 B, orchestration only) and retreat are
|
||
mapped; the six pipeline callees and the tanker fold are unread; **nothing here has run under
|
||
an instrument**. Combat *simulation* (`GameCombatSim`) stays parked.
|
||
3. **trade + spy managers** — all 11 virtual targets named; four can draw RNG and none has fired
|
||
(no trade routes / spy program in the corpus).
|
||
4. **`net/`** — the lockstep protocol. Direct IP works in the original without GameSpy; our engine
|
||
inherits determinism to the word, so a lockstep peer is mostly serialization we already own.
|
||
5. **UI** and **renderer** — not on any path; DXVK carries rendering for the original.
|
||
|
||
## 5. Workloads to manufacture (an unexercised path is a hypothesis)
|
||
|
||
- **orders save → End Turn → autosave** — the Rung-A reference (human commands in-file).
|
||
- **trade routes + spy program** — the four tail draw sites; `spies2`/`SysMem`/`mts` elements.
|
||
- **a cruiser** (2 census leaves), **a DN platform** (the third).
|
||
- **a Liir fleet** — waypoint type 2 is the Liir drive, not a node line; **a Hiver game** — types 4/5.
|
||
- **a battle with `res->+4` clear and a partial retreat** — resolver + retreat instrumented; tests
|
||
the `NMnx` prediction and the predicted zero-ship-fleet bug in the original.
|
||
- `ResErrRoll` true entering `ProcessResearch` — save exists; needs one turn funding > ½ a tech.
|
||
- expense sliders / debtor / aid — likely dead in 1.8; debtor ≈ 35 turns. Low priority.
|
||
|
||
## 6. Multiplayer revival (lane G2)
|
||
|
||
- **Tier 0 (hours):** `/concurrent /join 127.0.0.1:3369` — two clients on one VM, no server.
|
||
Predictions written; queued behind lane H for VM140.
|
||
- **Tier 1:** availability responder, byte-specified; probably a no-op (fails open on DNS failure).
|
||
- **Tier 3 (config, not code):** self-hosted OpenSpy/UniSpy with the `swordots`/`Z5gR9Z`/1381
|
||
row; fix queryport 3369 (not 6500) and add the 16 custom QR2 keys (ids 50–65).
|
||
- No CD-key code exists; 333networks ruled out (v0 only).
|
||
|
||
## 7. Method and infrastructure
|
||
|
||
- `guides/method-rules.md` (18 rules) is the contract for every lane brief. Rule 18 — *measure
|
||
first, the lab exists* — is why the RNG ledger closed in one VM session after three static lanes.
|
||
- Reachability claims older than lane V2 are **lower bounds**; re-check "no caller" findings with
|
||
`tools/vtable_map.py`. Never trust Ghidra's function end (five lanes bitten).
|
||
- Regenerate the determinism-oracle autosaves on VM140 (lane N overwrote them; lane H doing it).
|
||
- Board rows superseded by later rows are marked `SUPERSEDED` at integration — keep doing it.
|
||
- Displace the shim: each hook that reaches `replace` with 0 divergences is code the original no
|
||
longer runs. Track that count; it is the honest "how much of the game is ours" number.
|
||
|
||
## Parked
|
||
- Battle-load bug (thread contention) — resume only with a reason.
|
||
- Renderer — after the sim byte-matches.
|