#include "app/construction_phase.h" #include #include #include #include "game/sim/construction.h" namespace sots::app { namespace { std::string fmt(const char* f, ...) { char buf[512]; va_list ap; va_start(ap, f); std::vsnprintf(buf, sizeof buf, f, ap); va_end(ap); return std::string(buf); } } // namespace ConstructionPhaseResult RunBuildQueues(mars::stream::shapes::SaveGame& game) { ConstructionPhaseResult r; // The owner of a queue is the system's `PID`, which is the player's OBJECT id, not its // position in the player vector. The two are different numbers and confusing them is a // trap this codebase has already paid for once. std::map playerIndex; for (std::size_t i = 0; i < game.sim.players.size(); ++i) playerIndex[game.sim.players[i].playerID] = i; int shipBorne = 0; int ordersByOwner = 0; int unownedQueues = 0; for (auto& e : game.sim.systems) { if (!e.sys.bq.has_value()) continue; ++r.queuesVisited; auto& q = *e.sys.bq; r.ordersPending += static_cast(q.orders.size()); for (const auto& o : q.orders) r.pointsDemanded += o.conleft; if (!q.orders.empty()) { if (playerIndex.count(e.sys.pid)) ++ordersByOwner; else ++unownedQueues; } } // The ship-borne queues. Every one of them is gated behind the ship's `hbq` flag, and // the count is reported because an order hiding in one would falsify the "no orders on // the reference pair" reading, which is the load-bearing claim of this phase. for (const auto& fe : game.sim.fleets) for (const auto& se : fe.flt.ships) if (se.ship.hbq) shipBorne += static_cast(se.ship.bq2.orders.size()); r.blockedOnPoints = r.ordersPending > 0; if (r.queuesVisited == 0) { r.notes.push_back("no system carries a build queue: a queue is written only for a " "system with an owner"); return r; } r.notes.push_back(fmt("%d system build queue(s), %d pending order(s) demanding %d " "construction point(s); %d order(s) in ship-borne queues", r.queuesVisited, r.ordersPending, r.pointsDemanded, shipBorne)); if (r.ordersPending == 0 && shipBorne == 0) { r.notes.push_back("NOTHING TO BUILD. The pass is faithful and idle: with no order " "anywhere in the game it cannot create a ship, so it closes no " "leaf here. On the reference pairs the archived ship census is " "still one destroyer higher than ours, and the order behind that " "destroyer is created inside the turn by the AI -- that leaf is " "blocked on AI order generation, not on this phase"); return r; } // Points would come from the system's output vector; that term is the roadmap's item 1 // and is not this lane's. The pass is therefore driven with nothing and reports the // demand, so the shape of the gap is visible in the run log. r.notes.push_back(fmt("BLOCKED: construction points come from the per-system output " "term (out[7] scaled by the shipyard bonus, then out[8] = min(out" "[7], demand)), which is unmodelled. %d order(s) would be offered " "points this turn", r.ordersPending)); if (unownedQueues) r.notes.push_back(fmt("%d queue(s) hold orders but their system's owner id resolves " "to no player in the save -- reported, not skipped silently", unownedQueues)); (void)ordersByOwner; return r; } } // namespace sots::app