99962b295f
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>
122 lines
5.3 KiB
Markdown
122 lines
5.3 KiB
Markdown
# 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`](../../../pkg/model/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`](../../../ui/PLAN.md).
|
|
|
|
## Build / run
|
|
|
|
```sh
|
|
# 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.ts` → `decodeReport`):
|
|
|
|
| `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
|
|
|
|
```sh
|
|
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.
|