sots-engine/tests/game_sim/smoke_real_save.cpp

113 lines
5 KiB
C++

// Real-data smoke test: feed a player's numbers from a save dump through the budget
// roll-up and print the result for eyeballing.
//
// Input: $SOTS_SAVES_JSON = path to the JSON emitted by the RE repo's save reader
// (`save_reader.py SAVE --json`). Skips cleanly when unset. No assertions on exact
// values yet: the per-system money output is not fully resolved, so the system income
// line is a placeholder. Turning this into a compare-mode test (asserting the next
// turn's savings from a save pair) is future work.
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <string>
#include "game/sim/economy.h"
#include "game/sim/numeric.h"
#include "mini_json.h"
using namespace sots::sim;
int main() {
const char* path = std::getenv("SOTS_SAVES_JSON");
if (!path || !*path) {
std::printf("smoke_real_save: SOTS_SAVES_JSON unset, skipped\n");
return 0;
}
std::ifstream f(path);
if (!f) {
std::fprintf(stderr, "smoke_real_save: cannot open %s\n", path);
return 1;
}
std::stringstream ss;
ss << f.rdbuf();
const std::string text = ss.str();
minijson::Value root;
minijson::Parser parser(text);
if (!parser.parse(root)) {
std::fprintf(stderr, "smoke_real_save: JSON parse failed\n");
return 1;
}
const minijson::Value* data = root.get("data");
const minijson::Value* sim = data ? data->get("sim") : nullptr;
const minijson::Value* players = sim ? sim->get("players") : nullptr;
const minijson::Value* systems = sim ? sim->get("systems") : nullptr;
if (!players || !players->isArray()) {
std::fprintf(stderr, "smoke_real_save: no data.sim.players array\n");
return 1;
}
// Index systems by id so a player's owned-system list can be resolved.
std::map<int, const minijson::Value*> systemById;
if (systems && systems->isArray()) {
for (const minijson::Value& entry : systems->arr) {
const minijson::Value* id = entry.get("SysID");
const minijson::Value* sys = entry.get("Sys");
if (id && sys) systemById[static_cast<int>(id->number())] = sys;
}
}
std::printf("smoke_real_save: %s\n", path);
std::printf(" (system income is a placeholder: population / 1e6 per owned system)\n");
for (const minijson::Value& entry : players->arr) {
const minijson::Value* p = entry.get("Player");
if (!p) continue;
const minijson::Value* name = p->get("PlryName");
const minijson::Value* npc = p->get("NPC");
if (npc && npc->boolean()) continue;
BudgetInputs in;
in.savings = static_cast<int>(p->get("Sav") ? p->get("Sav")->number() : 0);
in.maintenance = static_cast<int>(p->get("Maint") ? p->get("Maint")->number() : 0);
in.researchRate = p->get("ResRate") ? p->get("ResRate")->number() : 0;
in.resMod = p->get("ResMod") ? p->get("ResMod")->number(1) : 1;
in.resScl = p->get("ResScl") ? p->get("ResScl")->number(1) : 1;
in.trm = p->get("TRM") ? p->get("TRM")->number() : 0;
in.tra = static_cast<int>(p->get("TRA") ? p->get("TRA")->number() : 0);
in.trp = static_cast<int>(p->get("TRP") ? p->get("TRP")->number() : 0);
in.shrm = p->get("shrm") ? p->get("shrm")->number() : 0;
in.hasResearchTarget = p->get("ResTNm") && !p->get("ResTNm")->string().empty();
const minijson::Value* owners = p->get("owners");
int ownedSystems = 0;
if (owners && owners->isArray()) {
for (const minijson::Value& o : owners->arr) {
const int sysId = static_cast<int>(o.number(-1));
auto it = systemById.find(sysId);
if (it == systemById.end()) continue;
const minijson::Value* abdn = it->second->get("Abdn");
if (abdn && abdn->boolean()) continue;
const double pop = it->second->get("Pop") ? it->second->get("Pop")->number() : 0;
in.systemIncome.push_back(static_cast<int>(pop / 1e6));
++ownedSystems;
}
}
in.ownsSystems = ownedSystems > 0;
const Budget b = ComputeBudget(in, false);
std::printf(" player %-16s species=%d sav=%d maint=%d systems=%d resRate=%.2f\n",
name ? name->string().c_str() : "?",
static_cast<int>(p->get("Species") ? p->get("Species")->number(-1) : -1),
in.savings, in.maintenance, ownedSystems, in.researchRate);
std::printf(" interest=%d debt=%d sysIncome=%d/-%d avail=%d construction=%d\n",
b.savingsInterest, b.debtInterest, b.systemIncomePositive, b.systemIncomeNegative,
b.available, b.construction);
std::printf(" researchMoney=%d RP=%d totalRP=%d net=%d -> savings next turn %d\n",
b.researchMoney, b.researchPoints, b.totalResearchPoints, b.net,
SaturatingAdd(in.savings, b.net));
}
std::printf("smoke_real_save: done (no assertions; compare-mode is future work)\n");
return 0;
}