209 -> 158 (closed 51, regressed 0) and 108 -> 87 (closed 21, regressed 0), identical to the pre-lane baseline, measured on CT111. Falsification hypotheses 1 and 2 refuted by measurement (three empty BQ frames per reference save, every hbq false, the one command block empty); hypothesis 3 refuted by the corpus oracle; hypothesis 4 stands as a labelled hypothesis. construction.h also carries the ship/fleet birth shape as read but NOT implemented, with the reason stated: the newborn hull copies four cached stat words the engine does not yet compute.
146 lines
7.9 KiB
C++
146 lines
7.9 KiB
C++
// game::sim -- ship construction: what a completed build order writes.
|
|
//
|
|
// The point-consuming half of the pass lives in colony.h as `ProcessBuildQueue`, because
|
|
// that is where the colony turn's other point channels live. This file holds the half that
|
|
// runs *per completed order*: the player's `ShipRecords`, which is the only thing a
|
|
// completion writes that the save can see without also creating the ship.
|
|
//
|
|
// WHERE THIS RUNS
|
|
// ---------------
|
|
// `StrategyServer::ProcessTurn` phase 11 -> `ServerSystem::ProcessTurn` ->
|
|
// `ServerSystem::ProcessBuildQueue` -> `BuildQueue::ProcessTurn`. A second caller exists
|
|
// (the ship-borne queue reached from the ship-action dispatcher, i.e. a construction ship
|
|
// building a station), and it reuses the same function.
|
|
//
|
|
// WHAT A COMPLETION WRITES, in the order the original writes it
|
|
// -------------------------------------------------------------
|
|
// 1. the ship is created and attached, and the new ships of the pass are collected into a
|
|
// vector that is handed to the fleet-forming step after the loop;
|
|
// 2. `ShipRecords.built[hullClass]` is incremented -- indexed by the design's cached hull
|
|
// class ordinal, stride 4;
|
|
// 3. the per-design record whose key equals the design's object id is found, appended if
|
|
// absent, and its own `built` field is incremented;
|
|
// 4. a build-completed event is pushed onto the system's event list;
|
|
// 5. `points -= conleft`, `conleft = 0`.
|
|
//
|
|
// Step 2 has EXACTLY ONE writer in the whole executable -- an image-wide scan for the
|
|
// indexed increment at that displacement returns one site, inside this function. So a ship
|
|
// that reaches the wire with the per-class counter bumped came through this pass and no
|
|
// other. (Losses, kills and in-service are three further parallel arrays with the same
|
|
// stride; nothing increments them here, and nothing in the save corpus is ever non-zero for
|
|
// losses or kills, so they carry no model.)
|
|
//
|
|
// The record layout is settled by ENUMERATION, not by what this function touches: the wire
|
|
// writes `srnc` groups of {srb, srl, srk, sri} followed by `srbd` records of
|
|
// {srd, src, srb, srl, sri}, and the class array's base plus four arrays of three ints lands
|
|
// exactly on the per-design vector's first word. Three classes, four arrays, then the
|
|
// vector.
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include "game/sim/colony.h"
|
|
|
|
namespace sots::sim {
|
|
|
|
// Destroyer / cruiser / dreadnought. The wire's `srnc` is 3 in every save in the corpus.
|
|
constexpr int kHullClassCount = 3;
|
|
|
|
// One element of the second counted section (`srbd`). `src` is the design's hull class and
|
|
// is written once, when the record is appended; a later completion of the same design only
|
|
// touches `built`.
|
|
struct ShipDesignRecord {
|
|
int designKey = 0; // srd -- the design's OBJECT id, not its index
|
|
int hullClass = 0; // src
|
|
int built = 0; // srb
|
|
int lost = 0; // srl
|
|
int inService = 0; // sri
|
|
};
|
|
|
|
// Game::ShipRecords, held inline in the player.
|
|
struct ShipRecords {
|
|
int built[kHullClassCount] = {}; // srb
|
|
int lost[kHullClassCount] = {}; // srl
|
|
int killed[kHullClassCount] = {}; // srk
|
|
int inService[kHullClassCount] = {}; // sri
|
|
std::vector<ShipDesignRecord> designs;
|
|
};
|
|
|
|
// Linear search for `designKey` over the per-design vector, appending a fresh record when
|
|
// there is no hit. The search is a plain forward scan and the append is a push_back, so the
|
|
// vector's order is first-seen and is load-bearing for the wire.
|
|
// CONFIDENCE: high -- read instruction by instruction, including the append's field order.
|
|
ShipDesignRecord& FindOrAppendDesignRecord(ShipRecords& r, int designKey, int hullClass);
|
|
|
|
// One completed hull: bump the class counter and the design record's own counter.
|
|
// A hull class outside [0, kHullClassCount) leaves the class array alone -- the original
|
|
// indexes it unchecked, so an out-of-range class is a corrupt design, not a policy.
|
|
// CONFIDENCE: high on both increments; the guard is ours.
|
|
void RecordShipBuilt(ShipRecords& r, int designKey, int hullClass);
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// One system's construction pass
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
struct Completion {
|
|
int orderId = 0;
|
|
int designId = 0;
|
|
};
|
|
|
|
struct SystemConstructionResult {
|
|
std::vector<Completion> completed;
|
|
int pointsIn = 0;
|
|
int pointsLeft = 0; // the original's return value
|
|
int pointsSpent = 0; // pointsIn - pointsLeft
|
|
int moneyCharged = 0;
|
|
int ordersBefore = 0;
|
|
int ordersAfter = 0;
|
|
int ordersRemoved = 0; // completed here, plus any that were already at or below zero
|
|
bool advancedPartially = false; // an order absorbed everything and stopped the pass
|
|
bool sweepRan = false; // false when points <= 0: the whole body is skipped
|
|
};
|
|
|
|
// FIFO consumption with the design id of every completion kept, which the queue pass alone
|
|
// does not report. `queue` is modified in place exactly as the original modifies the list.
|
|
// CONFIDENCE: high -- see colony.h's ProcessBuildQueue for the rules and their evidence.
|
|
SystemConstructionResult RunSystemConstruction(std::vector<BuildOrder>& queue, int points);
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// The hull and the fleet at birth -- READ, NOT YET IMPLEMENTED
|
|
// ---------------------------------------------------------------------------------------
|
|
//
|
|
// This is written down here rather than coded because the newborn hull copies four cached
|
|
// stat words out of its design, and those words are recomputed by the design's own stats
|
|
// pass, which this engine models only far enough to get a hull class. Coding it now would be
|
|
// writing fields we cannot compute. The whole chain is read instruction by instruction in
|
|
// the RE repo; the shape, for the lane that gets the design stats:
|
|
//
|
|
// THE SHIP. 176 bytes. The object id comes from the network-node id allocator --
|
|
// `(counter << 4) | (node & 0xF)`, per-node counters, PRE-incremented, never issuing 0 --
|
|
// and is written into the object by the id-map insert, not by the constructor. At birth:
|
|
// the owner is the queue's owner and the design is the order's design; range, health,
|
|
// construction capacity, refuel capacity and repair capacity are copied from the design's
|
|
// cached stat words; the plague and one other word are -1 and everything else is zero.
|
|
// Two fields matter to a reimplementation:
|
|
// * the FLEET LINK IS NULL at birth and is set by the fleet-join step, not here;
|
|
// * the TURN-BUILT stamp is the sim's frame counter, i.e. the number of the turn being
|
|
// produced -- MEASURED: the six hulls the zuul frame-16 -> frame-17 pair adds all
|
|
// carry the NEW turn number, which is independent evidence that the frame counter is
|
|
// incremented before the spine runs.
|
|
//
|
|
// THE FLEET. 288 bytes. A system caches ONE home fleet; every hull built there joins it,
|
|
// and a fleet is created only when that cache is empty. So a second turn of building at
|
|
// the same system creates no fleet. A created fleet takes an id from the same allocator,
|
|
// is born at the system's position, and always carries flag 0x400 -- which is NOT a
|
|
// retreat marker (that reading is corrected) and is NOT the design flag of the same
|
|
// numeral that game/design/hull.h warns about. The build path adds 0x20 on top. There is
|
|
// NO scalar ship count: the wire's count is the length of the fleet's ship vector.
|
|
// A fleet created with no name override asks the player's name generator, which is what
|
|
// bumps the generator's counter -- one bump per generated name.
|
|
//
|
|
// Ships are also created OUTSIDE this pass, by the trade manager's encounter spawner. That
|
|
// path touches no ShipRecords at all, which is what makes the per-class built counter a
|
|
// clean discriminator between player-built hulls and spawned ones.
|
|
|
|
} // namespace sots::sim
|