fix: re-source Chapter Approved layout geometry from Battlemaster - #107
Open
wn-mitch wants to merge 2 commits into
Open
fix: re-source Chapter Approved layout geometry from Battlemaster#107wn-mitch wants to merge 2 commits into
wn-mitch wants to merge 2 commits into
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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: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-1was missing.Two judgement calls worth a reviewer's eye
EFhas no template that models it. Constraint propagation returned an empty domain forCOandEF, which proves the committed data labelled those two ruins inconsistently — so their corpus majority is noise, not evidence.COwas resolved by dimensional impossibility (it is 6″ on its long axis;corner-ruin-balanced-rightis 5×4.5″, so the piece cannot be that footprint), deliberately overruling the majority. That leavesEF(4.5×6″) assigned tocorner-ruin-balanced-rightby elimination, where its 6″ axis does not fit in either orientation. Authoring a properEFfootprint 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-pairingtest, 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'schapter-approved-layout-litepayload, 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
ruff+mypy, Go — all greennpm run validateclean (AJV + referential integrity, 3576 items)rustfmt/gofmtclean; version lockstep in sync at 1.2.0 (not bumped — release decision)SPEC_VERSIONstays at 86. Theterrain-resolverandterrain-keystonesgoldens are built from synthetic cases ingen-conformance.ts, not fromterrain-layouts.jsonjustwas unavailable in this environment, so the preflight recipe was run step by step rather than viajust preflight.Not done
EFfootprint (see above)integrity.tsdoes not check terrain, so a danglingtemplateorparent_area_idis caught only at resolve time. Pre-existing, not introduced heretools/extract-terrain-layouts.pyis left in placeGenerated by Claude Code