sots-engine/src/app/alliance.h
alex 5e409cfa05 lane A2: S04, the alliance mask -- 80 player-records, 560 fields, 0 mismatches
The spine's fourth phase, read byte-for-byte and implemented:

    almem[i] = (1 << i) | (ALid != -1 ? AL : 0)

with i the player's POSITION IN THE PLAYER VECTOR, not its index field. Both
inputs are on the wire and so is the output, through the turn-record archive,
so the phase is checkable against bytes the original wrote:

    app_turn_record: 11 saves, 80 player-records, 560 fields, 0 mismatches
                     (was 480 fields over six fields; almem is the seventh)

The eight zero masks of the corpus's earliest archived turn are PREDICTED, not
excluded: the archiving phase also runs on load, and the load path does not run
the spine. BuildTurnRecord takes spineRan and models it, so all 80 records are
compared.

Three parts of the rule the corpus cannot separate -- the bit index, the OR,
and the ALid guard -- are pinned in app_alliance with the separating inputs no
save provides, and app_turn_record prints that it could not separate them.

Divergence, closed and regressed reported separately:

    turn1->turn2  default          209 -> 204   closed 5, regressed 0
    turn1->turn2  --commit-blocked 209 -> 189   closed 29, regressed 9  (was 17)
    turn2->turn3  default          108 -> 103   closed 5, regressed 0
    turn2->turn3  --commit-blocked 108 -> 106   closed 13, regressed 11 (was 19)

T36 stays blocked: nine leaves would still be wrong (inc x3, sav x3 behind the
budget; three census leaves behind the design catalogue). It now closes all 24
turnstats leaves on the reference pair, so it becomes a clean +24 once those
two land.

Prediction and falsification committed first in 49ae628.
Gates run separately: clean-room OK; host ctest 43/43. No src/shim touched.
2026-09-08 12:44:00 -04:00

47 lines
2.2 KiB
C++

// The alliance / shared-vision mask -- the spine's fourth phase.
//
// Early in every strategic turn, before anything else in the turn can read it, the driver
// walks the server's player vector and rebuilds one word on each player's turn record from
// scratch. The word is a bitmask of "whose vision this player shares this turn": the player's
// own bit, plus every member of the player's alliance when it is in one.
//
// Two things about it are worth stating in the header because both are places a
// reimplementation goes quietly wrong.
//
// 1. The bit is the player's POSITION IN THE PLAYER VECTOR, not the player's own index
// field. Every other per-player index in the tail -- the turn-results array, the battle
// tally, the archive key -- uses the index field, and this one does not. The shift count
// in the original is the loop induction variable; the index field is never loaded in this
// phase.
// 2. The mask is rebuilt, not accumulated. The word is cleared first, so nothing survives
// from the previous turn.
//
// The word is not itself a save leaf. It reaches the wire only because the last phase of the
// post-combat tail copies the whole turn record into the per-turn statistics archive, which
// IS on the wire -- so this phase is checkable against bytes the original wrote.
#pragma once
#include <cstddef>
#include <cstdint>
#include <vector>
#include "mars/stream/shapes.h"
namespace sots::app {
// The value the original uses for "this player is in no alliance".
constexpr std::int32_t kNoAlliance = -1;
// One player's mask. `vectorIndex` is the player's position in the server's player vector.
//
// Above 31 players the original's shift wraps -- x86 masks a variable shift count to five
// bits -- so bit (index % 32) would be set. No save in the corpus has more than eight
// players and that path is untested; it is reproduced rather than "fixed" because the point
// of this file is to be the original, not to be correct.
std::int32_t AllianceMask(const mars::stream::shapes::Player& p, std::size_t vectorIndex);
// The whole phase: every player's mask, in vector order.
std::vector<std::int32_t> RebuildAllianceMasks(
const std::vector<mars::stream::shapes::PlayerEntry>& players);
} // namespace sots::app