The tail's last phase archives a per-player record whose 13 modelled fields were 7. The six ship counts join them: every player's designs (normal and legacy, one id space) are classified against the section catalog, and the global fleet list is walked keyed by Flt.PID against each player's OBJECT id, not its vector position. The census cannot come from a save. A design's hull size and its defence-platform flag are recomputed from the section catalog whenever the design changes and are never written down, so the standalone grows a data root -- `--data DIR`, or $SOTS_DATA_DIR. No game data is embedded, and without a root the six counters report themselves unmodelled instead of being written as six zeros that a wrong model would also produce. `--commit-blocked=IDS` and `--commit-blocked-except=IDS` narrow the commit switch to named phase ids. All-or-nothing across every blocked phase reports one closed count and one regressed count for all of them at once, which is the netting the campaign does not do. MEASURED, closed and regressed never netted, state_checksum leaves: turn1 -> turn2 default 209 -> 204 closed 5 regressed 0 (unchanged) turn1 -> turn2 --commit-blocked=T36 no data closed 29 regressed 9 turn1 -> turn2 --commit-blocked=T36 with data closed 29 regressed 7 turn2 -> turn3 default 108 -> 103 closed 5 regressed 0 (unchanged) turn2 -> turn3 --commit-blocked=T36 with data closed 13 regressed 7 The prediction written before the code said the regressed list would fall to 6 and 8 under a full --commit-blocked. It fell to 7 and 9. The prediction's first falsification case is what happened: two census leaves closed and the third did not, because the census is of the fleet list as it stands and no phase the standalone runs creates a ship. The archived count is higher than ours by exactly one destroyer on BOTH pairs for the one player whose build queue completes that turn, while the self-check on the input turn is exact. That leaf is short by the turn's construction, not wrong about classification. app_test_turn_record now compares the six counters against the record the game archived: 11 saves, 80 player-records, 1040 fields, 480 of them census leaves, 0 mismatches. That is lane D2's 480/480 reproduced through this code path, which visits a design's slots in the original's in-memory order (mission, command, engine) rather than the wire's. T36 stays Blocked, and on two named things, neither of them in this phase: sav and inc come from P01/P02, blocked on the per-system money output; and shpt[0] is short by the ships the turn builds. The archived record is one struct on the wire, so those words cannot be left out while the rest is written -- committing is all-or-nothing at the record, and there is no field-granular knob that could change that. Seven confidently-wrong leaves are not worth 29 that later lanes close for free. COVERAGE, as loudly as the verdict: only 32 of the 480 archived census leaves are nonzero anywhere in the corpus -- per leaf (cls0 shpt/satt, cls1 shpt/satt, cls2 shpt/satt) = 18/3, 0/0, 11/0. cls1 entirely and satt for cls2 have never been observed nonzero: three of the six counters are unexercised hypotheses. The four loss/kill words of each group are zero throughout and are written as zeros with no model behind them. Hull size is an assignment in slot order, and design rule A6 means no save can tell the memory order from the wire order. verified stays 0: nothing here was compared against a running game. Gates, separately: clean_room_check OK; host ctest 45/45 without SOTS_SAVES_DIR and 45/45 with it. No src/shim file touched; the shim cross-build was NOT run (no i686 mingw here). |
||
|---|---|---|
| cmake | ||
| docs | ||
| include/generated | ||
| src | ||
| tests | ||
| third_party | ||
| tools | ||
| .gitignore | ||
| CMakeLists.txt | ||
| CMakePresets.json | ||
| CONTRIBUTING.md | ||
| LICENSE | ||
| README.md | ||
sots-engine
A from-scratch, functional reimplementation of the engine behind Sword of the Stars (2006). Not a byte-for-byte decompilation: behavior-equivalent code, built up one verified piece at a time (OpenRCT2-style) until the tree can build the whole application on its own.
Bring your own game. This repository contains engine code only. Game data, assets, saves, and
the original binaries are never included; tests and tools read an owner-supplied copy via
$SOTS_DATA_DIR. See CONTRIBUTING.md for the clean-room rules.
Status
Phase 2. M0 done — the shim frontend (src/shim/, a proxy binkw32.dll the original game
loads) builds, deploys, hooks, and logs from a real game launch. Engine code accrues under
src/mars/ and src/game/; each module is oracle-verified against the owner's game data before merge:
mars/parse— brace-block +.effectreaders (1,531/1,531 files agree with the reference)mars/text— flat key/value tables, id manifests, CSV (64/64 files agree)game/sim— strategic formulas (economy, research, colonies, movement) as pure functions; 356 hand-computed checksmars/vfs—.gob(ZIP) archive reader + loose-file override; entry counts and bytes verified againstunzipmars/stream— the game's self-describing save/serialization format (reader, writer, typed shapes) + gzip; three real saves round-trip byte-identicalmars/rng— MT19937 with save-state load/store; layout confirmed against real saves, draw mappings read off the binary (docs/mars-rng.md)game/data— typed catalogs (weapons, ship sections, turrets, id registries, tech tree, strings) with cross-reference checks; 229k values agree with the referencegame/design— ship-design assembly/fit/tech-gating rules and derived stats; validates all 127 stock designs from real savesapp— the standalone:sots_turnloads a save, runs one strategic turn over the published phase order of all three turn drivers, and writes a save. 14 of the 44 turn-driver phases are modelled; every phase that is not appears in the run log as a named no-op. How far it is from the byte-match, and what stands in the way, is indocs/S-standalone.md
Build: cmake --preset host && cmake --build --preset host && ctest --preset host (Linux);
tools/sync-build.sh cross-builds the shim on the lab box and stages it for deployment.
Layout (grows with the work)
src/shim/— binkw32 proxy + hooks + old-vs-new compare harness (frontend #1)src/mars/,src/game/— the engine and game reimplementation (accruing)src/app/— the standalone turn driver (frontend #2): load a save, run a turn, write a saveinclude/generated/sots_addresses.h— binary facts (RVAs/prototypes), generated from the RE repotests/— host tests; real-data tests skip unless$SOTS_DATA_DIRis settools/— build (MinGW i686 cross) and deploy scripts
Planning, findings, and verification evidence are tracked in the private RE repo (sots-re).