113 lines
5 KiB
C++
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;
|
|
}
|