sots-engine/tests/game_design/dump_designs.cpp

122 lines
5.7 KiB
C++

// dump_designs <data-root> <stock_designs.json> <out.json>
//
// Runs every save design through the rules and writes, per design, the
// findings as (rule, level, slot, bank), applied_techs per slot and the
// derived stats -- the input of oracle/compare.py.
#include <cstdio>
#include <fstream>
#include <string>
#include <vector>
#include "game/data/catalog.h"
#include "game/design/rules.h"
#include "game/design/stats.h"
#include "json_min.h"
#include "stock_designs.h"
using namespace game::data;
using namespace game::design;
using json_min::quote;
namespace {
std::string num(double v) {
char buf[64];
std::snprintf(buf, sizeof buf, "%.17g", v);
return buf;
}
std::string str_list(const std::vector<std::string>& v) {
std::string s = "[";
for (std::size_t i = 0; i < v.size(); ++i) s += (i ? "," : "") + quote(v[i]);
return s + "]";
}
std::string opt_num(const std::optional<double>& v) { return v ? num(*v) : "null"; }
std::string opt_int(const std::optional<std::int64_t>& v) { return v ? std::to_string(*v) : "null"; }
} // namespace
int main(int argc, char** argv) {
if (argc != 4) {
std::fprintf(stderr, "usage: dump_designs <data-root> <stock_designs.json> <out.json>\n");
return 2;
}
Catalog cat = load_catalog(argv[1]);
std::vector<StockDesign> designs;
std::string err;
if (!load_stock_designs(argv[2], designs, err)) {
std::fprintf(stderr, "%s\n", err.c_str());
return 1;
}
Ruleset rules(cat);
std::ofstream out(argv[3], std::ios::binary);
if (!out) {
std::fprintf(stderr, "cannot write %s\n", argv[3]);
return 1;
}
out << "[\n";
bool first = true;
for (StockDesign& sd : designs) {
Design& d = sd.design;
rules.strip_applied_techs(d);
std::vector<Violation> v = rules.validate(d);
DesignStats st = derive_stats(rules, d);
out << (first ? "" : ",\n") << "{\"player_index\":" << sd.player_index << ",\"design_index\":" << sd.design_index
<< ",\"save\":" << quote(sd.save) << ",\"player\":" << quote(sd.player) << ",\"name\":" << quote(d.name)
<< ",\"race\":" << quote(std::string(species_name(d.effective_race()))) << ",\"hidden\":" << (d.hidden ? "true" : "false");
out << ",\"violations\":[";
for (std::size_t i = 0; i < v.size(); ++i) {
const Violation& x = v[i];
out << (i ? "," : "") << "{\"rule\":" << quote(x.rule) << ",\"level\":" << quote(std::string(level_name(x.level)))
<< ",\"slot\":" << (x.slot ? quote(std::string(slot_name(*x.slot))) : "null")
<< ",\"bank\":" << (x.bank ? std::to_string(*x.bank) : "null") << ",\"message\":" << quote(x.message) << "}";
}
out << "],\"applied_techs\":{";
bool fa = true;
for (const SectionStats& s : st.sections) {
out << (fa ? "" : ",") << quote(std::string(slot_name(s.slot))) << ":" << str_list(s.applied_techs);
fa = false;
}
out << "},\"options\":{";
fa = true;
for (Slot s : kUsedSlots) {
if (d.slot(s).empty()) continue;
out << (fa ? "" : ",") << quote(std::string(slot_name(s))) << ":" << str_list(d.slot(s).options);
fa = false;
}
out << "},\"stats\":{"
<< "\"hull_class\":" << quote(st.hull_class) << ",\"mass\":" << num(st.mass) << ",\"section_cost\":" << num(st.section_cost)
<< ",\"section_cost_with_options\":" << num(st.section_cost_with_options)
<< ",\"weapon_cost_per_bank\":" << num(st.weapon_cost_per_bank) << ",\"weapon_cost_per_mount\":" << num(st.weapon_cost_per_mount)
<< ",\"total_cost_estimate\":" << num(st.total_cost_estimate) << ",\"health_total\":" << num(st.health_total)
<< ",\"crew\":" << st.crew << ",\"cpoints\":" << st.cpoints << ",\"banks\":" << st.banks << ",\"turrets\":" << st.turrets
<< ",\"command_cost\":" << st.command_cost << ",\"maintenance_cost\":" << st.maintenance_cost
<< ",\"command_quota\":" << opt_int(st.command_quota) << ",\"ftlspeed\":" << opt_num(st.ftlspeed)
<< ",\"nodespeed\":" << opt_num(st.nodespeed) << ",\"engine_techera\":" << quote(st.engine_techera)
<< ",\"capacities\":{";
for (std::size_t i = 0; i < st.capacities.size(); ++i)
out << (i ? "," : "") << quote(st.capacities[i].first) << ":" << quote(st.capacities[i].second);
out << "},\"sections\":{";
fa = true;
for (const SectionStats& s : st.sections) {
out << (fa ? "" : ",") << quote(std::string(slot_name(s.slot))) << ":{\"section\":" << quote(s.section->stem)
<< ",\"mass\":" << num(s.mass) << ",\"cost\":" << num(s.cost) << ",\"option_cost_multiplier\":" << num(s.option_cost_multiplier)
<< ",\"health\":" << num(s.health) << ",\"crew\":" << s.crew << ",\"cpoints\":" << s.cpoints << ",\"banks\":[";
for (std::size_t i = 0; i < s.banks.size(); ++i) {
const BankStats& b = s.banks[i];
out << (i ? "," : "") << "{\"size\":" << quote(fold(b.size)) << ",\"class\":" << quote(fold(b.turret_class))
<< ",\"mounts\":" << b.mounts << ",\"weapon\":" << (b.weapon ? quote(b.weapon->stem) : "null")
<< ",\"weapon_cost\":" << num(b.weapon_cost) << ",\"turret_model\":" << (b.turret ? quote(b.turret->model) : "null")
<< ",\"turret_health\":" << (b.turret ? opt_num(b.turret->health) : "null") << "}";
}
out << "]}";
fa = false;
}
out << "}}}";
first = false;
}
out << "\n]\n";
std::printf("wrote %zu designs to %s\n", designs.size(), argv[3]);
return 0;
}