Skip to content

fix: re-source Chapter Approved layout geometry from Battlemaster - #107

Open
wn-mitch wants to merge 2 commits into
mainfrom
claude/battle-master-40kdc-format-hz1e4r
Open

fix: re-source Chapter Approved layout geometry from Battlemaster#107
wn-mitch wants to merge 2 commits into
mainfrom
claude/battle-master-40kdc-format-hz1e4r

Conversation

@wn-mitch

Copy link
Copy Markdown
Owner

Re-sources the geometry of all 45 11e Chapter Approved terrain layouts from Battlemaster's public API. Data-only change: no schema, no API, no conformance movement.

Why

The layouts came from tools/extract-terrain-layouts.py, which recovers placements by calibrating per-xref image-stamp CTMs out of the GW Event Companion PDF. That got positions roughly right but rotations wrong, and it predates nubbed footprints being in the template catalog, so nub-aware centroids were never accounted for. Measured against Battlemaster:

p50 p90 max affected
Position 0.255″ 2.156″ 6.605″ 520 of 718 areas moved >0.15″
Rotation 94.04° 180.00° 180.00° 498 of 718 changed >1°

Enough of the corpus was out that correcting it piecemeal was not worth it.

What is taken, and what is not

Geometry only. Card identity (id, name, mission_matchup_id, variant, deployment_pattern_id, game_version) and authoring intent (keystones, link_group, objective_role) are preserved from the committed records and asserted to agree rather than overwritten — a disagreement stops the run instead of silently relabelling a card. All 45 ids mapped 1:1, and the deployment-key → pattern mapping was unanimous across all 45.

Also fills the two short-line areas Battlemaster has that disruption-vs-reconnaissance-1 was missing.

Two judgement calls worth a reviewer's eye

EF has no template that models it. Constraint propagation returned an empty domain for CO and EF, which proves the committed data labelled those two ruins inconsistently — so their corpus majority is noise, not evidence. CO was resolved by dimensional impossibility (it is 6″ on its long axis; corner-ruin-balanced-right is 5×4.5″, so the piece cannot be that footprint), deliberately overruling the majority. That leaves EF (4.5×6″) assigned to corner-ruin-balanced-right by elimination, where its 6″ axis does not fit in either orientation. Authoring a proper EF footprint is open follow-up work.

One upstream slip was corrected rather than propagated. Battlemaster places two cards' paired centre trapezoids 0.707″ off 180° symmetry. 43 of its 45 layouts are exactly symmetric, the two exceptions carry byte-identical coordinates, their rotations differ by exactly 180°, and the error is exactly (−0.5, −0.5) — an editor nudge. Propagating it broke the existing keystone-pairing test, which requires a keystone's reflected vertex to land within 0.25″ of its twin so both halves of a printed card measure alike. The pair is snapped to its symmetric mean.

Both are recorded in data/core/_reports/battlemaster-layout-intake.md, along with the per-layout deltas and the part mapping.

Note on the source

The Layout Embed API (/v1/public/embed/*) is documented but not deployed — every route 404s. This used the live TTS Map API's chapter-approved-layout-lite payload, which is better suited anyway: inch geometry and template references rather than rendered images.

Scope

The import tooling was written, used once, and removed in the second commit — the geometry was worth replacing wholesale, and a scraper for a source we do not intend to track again is maintenance without a payer. The net diff is 5 files: the layouts, the three embedded bundles, and the report.

Verification

  • TypeScript 1502 tests / 85 files, Rust 22 suites, Python 1168 + ruff + mypy, Go — all green
  • npm run validate clean (AJV + referential integrity, 3576 items)
  • All four language bundles regenerated; artifact drift is only the three embedded data bundles, the correct signature for a data-only change
  • rustfmt / gofmt clean; version lockstep in sync at 1.2.0 (not bumped — release decision)
  • Conformance corpora unchanged, so SPEC_VERSION stays at 86. The terrain-resolver and terrain-keystones goldens are built from synthetic cases in gen-conformance.ts, not from terrain-layouts.json
  • Geometry invariants gated the write: every layout resolves, all geometry sits on the 60×44 board, and every card holds 180° symmetry

just was unavailable in this environment, so the preflight recipe was run step by step rather than via just preflight.

Not done

  • A proper EF footprint (see above)
  • Terrain still has no referential-integrity coverage — integrity.ts does not check terrain, so a dangling template or parent_area_id is caught only at resolve time. Pre-existing, not introduced here
  • tools/extract-terrain-layouts.py is left in place

Generated by Claude Code

claude added 2 commits July 26, 2026 19:00
The 45 Chapter Approved terrain layouts came from
tools/extract-terrain-layouts.py, which recovers placements by calibrating
per-xref image-stamp CTMs out of the GW Event Companion PDF. That got positions
roughly right but rotations wrong, and it predates nubbed footprints being in
the template catalog, so nub-aware centroids were never accounted for. 520 of
718 area placements were out: p50 0.26", p90 2.16", max 6.61".

Battlemaster authors the same cards against the physical terrain and publishes
them through its public TTS Map API, so the geometry is re-sourced from there.
Card identity (id, name, mission_matchup_id, variant, deployment_pattern_id,
game_version) and authoring intent (keystones, link_group, objective_role) are
preserved from the committed records and asserted to agree rather than
overwritten; only geometry is taken.

The conversion is derived and re-asserted on every run rather than hardcoded:

- Frame: centre-origin y-up to corner-origin y-down, part position is the local
  min corner, rotation is counter-clockwise. Each field pinned against the
  snapshot.
- Anchor: Battlemaster anchors the artwork bbox centre, 40kdc the nubbed
  footprint's area centroid. Compensating for that is the actual fix for most
  placements.
- Per-template orientation offset, decided in stages: bounding box narrows to
  two candidates, then on-board placement (decisive for area-trapezoid), then
  the one hand-authored card, then keystone preservation. Position residual is
  deliberately not used - every card is 180°-symmetric, so it is degenerate.
- Part to feature-template mapping by constraint propagation over the
  composites whose observed child multiset is unambiguous, closed with the
  multiplicity, size-class and nub constraints.

Two judgement calls, both reported every run:

- CO and EF cannot be learned from the corpus - propagation returns an empty
  domain, proving the committed data labels them inconsistently. Resolved by
  the nub constraint instead, overruling the corpus majority for CO because a
  6" piece cannot occupy a 5x4.5" footprint. EF has no template that really
  models it and is assigned by elimination; authoring a proper footprint is
  follow-up work.
- Battlemaster places two cards' paired centre trapezoids 0.707" off 180°
  symmetry. 43 of its 45 layouts are exactly symmetric, the two exceptions
  carry identical coordinates and the error is exactly (-0.5, -0.5), so it is
  an upstream slip; propagating it would break keystone twin pairing. The pair
  is snapped to its symmetric mean.

Also fills the two short-line areas Battlemaster has that
disruption-vs-reconnaissance-1 was missing.

Gated by geometry invariants applied to the projected data before any write, so
a dry run fails on exactly what --write would produce: every layout resolves,
all geometry stays on the board, and every card keeps 180° symmetry. Schemas,
generated types and the conformance corpora are untouched - the terrain
corpora are built from synthetic cases - so SPEC_VERSION does not move.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCxxbDJMFUCp3vJ7tx8fDm
The import was a one-off: the committed geometry was far enough out to be worth
replacing wholesale, and that is done. Keeping a scraper for a source we do not
intend to track again is maintenance we would not pay for.

Removes tools/src/battlemaster/, the CLI, its npm script, its tests and the
calibration fixture. data/core/terrain-layouts.json and the four embedded
bundles are untouched and byte-identical to the intake commit.

The intake report stays as provenance for a 24k-line data change, with the part
mapping folded in so the record is one file, retitled as a point-in-time
document rather than a regenerable artifact. It still carries the one open item:
Battlemaster's EF ruin (4.5x6") has no 40kdc template that models it and was
assigned to corner-ruin-balanced-right (5x4.5") by elimination.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCxxbDJMFUCp3vJ7tx8fDm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants