Files
galaxy-game/tools/local-dev/legacy-report
Ilia Denisov 99962b295f tools/local-dev: legacy-report-to-json CLI for synthetic UI testing
A Go module under tools/local-dev/legacy-report that converts the
"dg" / "gplus" engine .REP files in tools/local-dev/reports/ into the
JSON shape of pkg/model/report.Report. The output drives a DEV-only
synthetic-mode loader on the UI lobby so the map, inspectors, and
order-overlay can be exercised against rich game states without
playing many turns end-to-end.

Scope is intentionally narrow: only the fields the UI client decodes
today (planets, players, own ship classes, header). Importing
pkg/model/report keeps the parser and the typed contract in lockstep
— any backwards-incompatible schema change breaks the tool's
compilation before it ships. The README spells out the parity rule
for extending the parser alongside future UI decoders.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 11:07:50 +02:00
..

legacy-report-to-json

Converts legacy text-format Galaxy turn reports (the dg and gplus engines that lived under tools/local-dev/reports/) into the JSON shape of pkg/model/report.Report.

The output is consumed by the DEV-only synthetic-report loader on the UI client's lobby (import.meta.env.DEV). With it, the map view, inspectors, and order-overlay can be exercised against rich game states without playing many turns end-to-end against a real backend.

The tool is part of the synthetic-report parity rule documented in ui/PLAN.md.

Build / run

# from the repo root, with the Go workspace active
go run ./tools/local-dev/legacy-report/cmd/legacy-report-to-json \
    --in  tools/local-dev/reports/dg/KNNTS039.REP \
    --out tools/local-dev/reports/dg/KNNTS039.json

--in reads - as stdin; --out defaults to stdout when empty or -. The tool exits non-zero on any I/O or parse failure.

Supported input variants

Variant Sample dir Status
dg tools/local-dev/reports/dg/*.REP First-class
gplus tools/local-dev/reports/gplus/*.REP First-class
ng tools/local-dev/reports/ng/*.rep Not supported
lucky tools/local-dev/reports/lucky/*.rep Not supported

dg uses CRLF line endings, gplus uses LF and tabs in section indentation; both are space-aligned tabular inside data blocks. The parser splits on runs of whitespace (strings.Fields) so the same code handles both.

Pseudo-Cyrillic glyphs (MbI, KAMA3, 9IMA) appear in some races and ship class names but are stored as plain ASCII letter substitutions — no encoding conversion is needed.

In-scope fields (current)

The parser only fills the subset of report.Report that the UI client already decodes from server responses (ui/frontend/src/api/game-state.tsdecodeReport):

report.Report field Source section in legacy file
Race <Race> Report for Galaxy ... line
Turn same
Width, Height Size: N (square galaxies)
PlanetCount Planets: N
VoteFor, Votes Your vote: block
Player[] Status of Players (total ...)
LocalPlanet[] Your Planets
OtherPlanet[] <Race> Planets (one per race)
UninhabitedPlanet[] Uninhabited Planets
UnidentifiedPlanet[] Unidentified Planets
LocalShipClass[] Your Ship Types

Players whose name in the legacy file ends with _RIP are emitted with the suffix stripped and Extinct: true.

Skipped sections (today)

These exist in legacy reports but have no UI decoder yet, so the parser ignores them. Each becomes in-scope as soon as its UI phase lands (see "Adding a new field" below).

  • Foreign / other ship types (<Race> Ship Types)
  • Sciences, both local (Your Sciences) and foreign (<Race> Sciences)
  • Battles (Battle at (#N) Name, Battle Protocol)
  • Bombings (Bombings)
  • Approaching / foreign groups (Approaching Groups, <Race> Groups)
  • Ships in production (Ships In Production)
  • Cargo routes — no dedicated section in the legacy text format; the synthetic JSON emits route: []. The UI's overlay path (applyOrderOverlay) supports running on top of an empty routes.

Adding a new field

ui/PLAN.md carries a global rule: every UI phase that extends decodeReport to read a new report.Report field also extends this parser, in the same PR, to populate it from legacy text — or, if the field cannot be derived, adds an entry to the Skipped sections list above with a one-line explanation.

The Go side of the rule is enforced mechanically: this tool imports galaxy/model/report, so any backwards-incompatible change to the schema breaks the tool's compilation before the change ships.

When extending:

  1. Identify the legacy section in tools/local-dev/reports/dg/*.REP (and gplus/*.REP) that carries the field, using game/rules.txt section "Отчет о результатах хода" as the column-layout reference.
  2. Add a section to the state machine in parser.go (classifySection, the section constants, the parse* methods).
  3. Cover the new section with a unit test in parser_test.go (inline minimal fixture) and update the smoke counts in TestParseDgKNNTS039 / TestParseGplus40 so a future regression that drops the section is caught.
  4. Run go test ./tools/local-dev/legacy-report/..., then re-run the CLI on dg/KNNTS039.REP and gplus/40.REP and visually skim the JSON — the field should appear with sensible values.

Tests

go test ./tools/local-dev/legacy-report/...

Inline fixtures exercise the per-section row parsers; smoke tests parse the real dg/KNNTS039.REP and gplus/40.REP and assert top-level counts (number of planets, players, extinct races, ship classes). Field-level fidelity is the inline tests' responsibility; the smoke tests catch regressions where a refactor of the section classifier silently drops a whole table.