Three phases, one input. A star system carries four per-player masks and three of
them agree on nearly every system of every save the corpus holds, so a model built
on the wrong one looks right until it does not. The gate is the DERIVED
active-presence mask -- fleet-here OR gate-here OR owner, recomputed on every
arrival and departure -- not the sticky one and not the explored one.
S29 SystemObservedStamp the system's own last-observed turn (whole function)
T17 RebuildPlayerViewTree the per-(system, player) observation record: who saw
the system, on what turn, and what encounter was there
T21 UpdateSurveyAndStats the explored sweep: seen this turn implies surveyed
game/sim/visibility is pure and knows nothing about save shapes; app/visibility_phase
wires it to them. The mask is READ FROM THE SAVE and never rebuilt: neither reference
pair moves a mask leaf, so the loaded value is the value these phases would see, and
rebuilding it from an unmodelled movement pass would be a change with no evidence.
Measured, closed and regressed reported separately and never netted:
turn1-state -> turn2-state 209 -> 158 closed 51, regressed 0
turn2-state -> turn3-state 108 -> 87 closed 21, regressed 0
of which this lane closed 46 and 16 (the rest were already closed at main). The 46
are the brief's 32-leaf target in full -- 8 record counts, 8 player ids, 8 turn
stamps, 8 encounter ids -- plus 8 system stamps and 6 explored masks.
Three further pairs the model was never fitted to, all zero regressions:
human-turn2 -> human-turn3 353 -> 311 closed 42 (a different game, 21 systems)
zuul15 -> zuul16 276 -> 264 closed 12
zuul16 -> zuul17 341 -> 329 closed 12
The corpus's one discriminating row is a host test rather than a comment: a system
whose last visiting fleet has gone carries the sticky and explored bits set, the
active bit clear, and a stamp frozen a turn behind. The test asserts the freeze AND
asserts what the wrong gate would have produced, so a future edit that swaps the
mask fails loudly instead of quietly agreeing with five saves.
Labelled hypothesis, with the workload named in the header: the encounter id is
recovered from the encounter fleet at the system, because the field the original
reads is set once at map generation and is not on the wire. It agrees on all six
encounter fleets in the corpus and no save can separate it -- none kills an
encounter while leaving its system visible.
Not written, deliberately: the colony-ownership stamp that moves beside these.
Its gate is demonstrably NOT the active mask (one system in the corpus has a zero
mask and moves it anyway), the formula is not held, so it is reported, not written.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARBgSooAfokKUy6wKUKEyZ
55 lines
2.6 KiB
C++
55 lines
2.6 KiB
C++
// The visibility phases, wired to the save shapes.
|
|
//
|
|
// Three phases of the turn touch the per-system visibility cluster, in two different
|
|
// drivers, and they are listed here together because they share one input -- the system's
|
|
// active-presence mask -- and nothing else in the turn reads it.
|
|
//
|
|
// S29 the system's own last-observed stamp
|
|
// T17 the per-(system, player) observation record
|
|
// T21 the explored sweep
|
|
//
|
|
// The mask itself is READ FROM THE SAVE and never rebuilt. The original recomputes it on
|
|
// every fleet arrival and departure, which the standalone does not model; but neither
|
|
// reference pair moves a mask leaf, so the loaded value is the value these phases would
|
|
// see, and rebuilding it from an unmodelled movement pass would be a change with no
|
|
// evidence behind it. When that stops being true it will show up as a regression here
|
|
// first, which is the point of saying it out loud.
|
|
#pragma once
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "mars/stream/shapes.h"
|
|
|
|
namespace sots::app {
|
|
|
|
// What one visibility phase did, in the terms the run log prints.
|
|
struct VisibilityPhaseResult {
|
|
int systemsVisited = 0; // systems the phase looked at
|
|
int systemsTouched = 0; // systems where something moved
|
|
int leafWrites = 0; // save leaves this phase changed
|
|
std::vector<std::string> notes;
|
|
};
|
|
|
|
// S29: `ltis`, the system's own last-observed turn. Written on every system some player can
|
|
// currently see; left alone on the rest.
|
|
VisibilityPhaseResult RunSystemObservedStamp(mars::stream::shapes::SaveGame& game);
|
|
|
|
// T21: the explored sweep -- every player who can see a system has now surveyed it.
|
|
VisibilityPhaseResult RunExploredSweep(mars::stream::shapes::SaveGame& game);
|
|
|
|
// T17: the per-(system, player) observation record.
|
|
VisibilityPhaseResult RunObservationRecords(mars::stream::shapes::SaveGame& game);
|
|
|
|
// The encounter parked at a system, as this model can recover it.
|
|
//
|
|
// The field the original reads is a star-system member that is NOT serialised: it is set
|
|
// once when the map is generated and never moves. The only observable that carries the same
|
|
// id is the encounter fleet sitting at the system, so that is what this reads -- and it is a
|
|
// HYPOTHESIS, not a reading. It agrees with the original on all six encounter fleets in the
|
|
// corpus and no save can separate it from the true rule, because no save kills an encounter
|
|
// while leaving its system visible. Returns -1 when there is none.
|
|
std::int32_t EncounterAtSystem(const mars::stream::shapes::SaveGame& game,
|
|
std::int32_t systemId);
|
|
|
|
} // namespace sots::app
|