Commit graph

8 commits

Author SHA1 Message Date
alex
de18d2c91d K: the research-event roll costs one or two RNG words, not one
Every Coverage note in this repo said RollResearchEvent draws "exactly one
NextFloat". That is the cost of REACHING its branch. When the roll beats the
odds, the plague path draws a SECOND word (NextInt) to pick an owned system and
posts EVENT_PLAGUE_OUTBREAK, and the rebellion path allocates an AIRebellion at
ServerPlayer+0x3b8 and cancels the current research. Nothing has caught this
because the branch has not fired in three sessions.

Corrected in research.h (two Coverage entries plus the scope comment, and the
branch entry raised to Risk::High), research.cpp, tech_effects.h and
tech_effects.cpp. Ours still models the first word only; the branch stays
declared unmodelled, now accurately.

Header regenerated from sots-re bb0f990 (750 entries) - lane K's map of the
combat-done tail: the autosave and save-file writer, the bankruptcy limits, the
turn-results accumulator and outbox, and the encounter-block callees.
2026-09-08 08:51:15 -04:00
Alex
405ba41a1e U: the SetResearched unlock cascade, and the prediction for the run that checks it
Pure module game/sim/techgraph: PrereqsMet (AND of ORs, empty group fails),
SetResearched (stamps, child-cost sweep, sticky turnAvailable, zero-cost
recursion) and the newly-available collector, all read out of 0x00581e10,
0x0057d8e0 and 0x00587cc3.

Wired into the B3 hook in compare mode only, over the scratch node copies:
four more node write-backs, the EVENT_TECHS_UNLOCKED list (still an input,
still nullptr when it could not be computed), the de-duplicating observed-tech
append and the one RNG word RollResearchEvent draws.

docs/U-unlock.md section 4 is the prediction, written before the build was staged.
2026-09-08 06:26:00 -04:00
lane S
32d3e36d8b sync generated header (414 entries): std::string is 0x1c, ObservedTech fully mapped
The header's ObservedTech notes carried a std::string size of 0x18. That was a
mis-attribution: the element's +0x24 word is the name string's trailing (empty)
allocator, not a data field. sizeof(std::string) is 0x1c binary-wide, confirmed
against every serializer in the original.

ObservedTech is now fully mapped, so the two research-hook comments that said
sizeof and the append site were unpinned are corrected. The region stays
declared-not-modelled -- appending is a behavioural change this hook does not
make -- but its byte delta now has an exact expected value (44 per completion),
and the note records that the original's append de-duplicates by tech name, so a
naive push_back would diverge on re-observation.

clean_room_check: OK.  host ctest: 33/33.
2026-09-08 05:08:53 -04:00
lane P
f3c6d551de lane P: post the research events from ours (count-only), declare ObservedTech
TechTree::ProcessResearch's events region now compares a modelled value instead of
reporting a known defect. ours posts the pass's events into its own
sots::events::EventStorage, seeded from a scan of the owner's list taken BEFORE the
original runs, and writes only the counts into the region's scratch copy. The game's
PostEvent is never called and no live byte moves; replace mode still posts nothing,
because a bumped EvNxID with no record behind it would corrupt the oracle's save.

- game/events: PostResearchPassEvents (the decision half, pure) + KeylessEventText
- shim/hooks/event_inputs (new lib shim_events, host-tested): the live<->model adapter,
  carrying game pointers as explicit uint32 so a 64-bit host build cannot alias them
- research hook: the wiring, a new observed_techs region for ServerPlayer+0x274, and
  turn / events_next_id_in / events_dedup_risk in the args so the count model's own
  assumption is measured rather than assumed
- EVENT_TECHS_UNLOCKED is NOT posted: its trigger is pinned but needs SetResearched's
  unlock cascade, which ours does not run. The driver takes the unlock list as an input
  and is handed 'no list', so a missing input cannot look like a modelled negative.
  Predicted residual: next_id short by exactly 1 on a completion call.

ctest 33/33 (shim_events_unit is new), clean_room_check OK. The shim TU is
syntax-checked only: no MinGW cross toolchain on this box.

See docs/P-events-wiring.md for the exact prediction for the next VM run.
2026-09-08 04:16:22 -04:00
alex
a7348be72c game/events: the player event log and the five research events
Recovers the game's event-posting API so the engine can post events and the
compare harness can see them. Until now the owner's event list was invisible to
every layer: B3's replace-mode oracle failed by exactly one item across 40,300
(an unposted EVENT_RESEARCH_OVERBUDGET) while its compare read clean, and B2's
clean compare bounds the economy fields only.

New pure module src/game/events:
  * EventStorage / TurnEvents / PlayerEvent -- the list is bucketed by TURN, not
    flat, which the save-editor struct notes had wrong.
  * EventStorage::Post reproducing the original's rules, including the four that
    change save bytes: the FLT_MAX (not infinity) default position; action 0 with
    no subject and no position storing as 2; per-bucket dedup that compares
    message/image/location/position/action but NOT summary; and EvNxID starting
    at 0 and being promoted to 1 on the first post.
  * PruneOldTurns reproduced with its off-by-one: of a leading run of buckets
    older than turn-50 it erases n-1, so one stale bucket always survives. The
    survivor is serialized, so correcting it would diverge.
  * research_events: the five events the research path raises, their EvImg
    identifiers and string-table keys, and the 0.8 completion split evaluated
    against (double)0.8f rather than the decimal 0.8.

Localized text is deliberately absent: only the EVENTSUM_/EVENTMSG_ keys are
here and the text resolves through a caller-supplied lookup, as the game does.

The four event offsets the B3 hook carried as local literals now come from the
generated header; they are read off instructions rather than inferred from the
save schema.

tests/game_events: 112 checks including a replay of the event list
turn3-state.sav actually holds. ctest 31/31 -> 32/32.

docs/E-events.md carries the proposed region and Coverage wording for the next
B3 recapture.
2026-09-08 02:45:57 -04:00
alex
c110fb245b harness: compile-time Coverage on every descriptor, guard regions, replace-mode records; audit of 23 undeclared side effects 2026-09-08 01:28:35 -04:00
alex
4abf654983 b4: colony + movement hooks; 22 formula corrections (growth curve has no capacity term, range margin +0.05f, ties-to-even rounding); 3 verified signatures 2026-09-08 00:57:55 -04:00
alex
dd97137fb0 b3: research hook + RNG-snapshot compare design; fix draw mapping to y/(2^32-1), NextInt inclusive, float32 odds, spend floor, decay constants 2026-09-07 23:14:08 -04:00