FEnigma/tests/fixtures
Dominik Roth 136492b197 Fix ally marker detection; expand TargetType icons and pickers
map_vision.py: marker shape test only matched diamonds, so friendly
(rectangle) markers could never be detected regardless of color match.
diamonds() now takes a per-side ideal shape (diamond for hostile, full
rectangle for friendly) with fill-ratio bands measured off real markers.

Merged tests/fixtures/map_shots/more/ into the main fixture set: 2
screenshots that solve fine (now 15.png/16.png, with hand-transcribed
ground truth) and 8 that are too low native resolution for the label
reader (same class as the existing 12.png) into too_hard/ as
17.png-24.png, with an explanatory README entry. Updated map_vision.py's
docstring numbers (9/12 solve, 104/131 points correct) to match.

TargetType: expanded from 11 to 44 members to cover every icon in
assets/icons/targets/{enemy,friendly}/, including 7 friendly-only types
(King, Police, General, Hospital, Fort, Civil-Military, Mechanized
Anti-Tank) with no enemy equivalent. UNKNOWN/ENEMY stay icon-less by
design (both are literal words the game's OCR'd text uses, confirmed via
ocr.py's _TYPE_BY_SHORT, so neither can be dropped without breaking real
parsing) and draw the same plain-dot fallback the map itself uses.

icons.py: collapsed the icon lookup into one canonical table
(_TARGET_ICON: TargetType -> (enemy_basename, friendly_basename), one
explicit row per type) instead of a basename table plus two exception
dicts layered on top -- with a startup assertion that every TargetType
has a row. Added build_target_type_grid(), an icon-grid picker (icon +
name, same idea as the existing Shell picker) that replaces the old
plain-text dropdown/list everywhere a type is chosen, and only offers
types the given side actually has real art for.

coord_dialog.py: Add/Edit Target and Add Ally now use the icon grid
instead of Adw.ComboRow. Fixed a resulting horizontal-scroll bug (an
unbreakable long word was blowing out cell width) and locked the
coordinate pickers back to 5 columns.

app.py: right-click quick-add now offers Spotter/RP alongside
Target/Ally/Strike, opens a real modal (not a Popover, which turned out
unreliable for a wide multi-row grid) to ask for a type instead of
silently defaulting to UNKNOWN, and doesn't repeat the coordinate on
every row. The type grid listens for "clicked" rather than "toggled" --
a grouped ToggleButton doesn't emit "toggled" when you click the one
that's already active, which meant confirming the pre-selected default
type silently did nothing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 23:49:46 +02:00
..
map_shots Fix ally marker detection; expand TargetType icons and pickers 2026-08-10 23:49:46 +02:00
writer_shots Add the map-table vision pipeline 2026-08-10 21:41:33 +02:00
map_shots_gt.json Fix ally marker detection; expand TargetType icons and pickers 2026-08-10 23:49:46 +02:00
README.md Add the map-table vision pipeline 2026-08-10 21:41:33 +02:00

Test fixtures

Real screenshots of the game, used as regression data. They are the game author's work, not covered by this repo's MIT license (see /LICENSE).

map_shots/ + map_shots_gt.json

The evaluation set for the map-grid solver (src/fenigma/map_vision.py), scored by tools/eval_map_vision.py. The JSON holds hand-transcribed cell labels at native pixel positions; its own header comment explains the format and the 9%/6% label-padding constant.

Shots wider than 2400px were downscaled to 2400px, and their ground-truth coordinates rescaled with them. 2400 is map_vision.RETRY_WORK_W, the widest the pipeline ever works at, so nothing the code can actually read was lost. Measured after the downscale: the same 7 of 10 solve, 100% of their points land in the correct cell, residual spread unchanged.

too_hard/ holds shots that are permanent rejections; see its own README.

writer_shots/

Typewriter/field-log screenshots. Two uses:

  • Measuring false positives in the map-vs-text routing gate (map_vision.looks_like_map / solve). Over the full 122-shot set the cheap gate false-positived on 6% and solve() accepted none.
  • OCR regression material for ocr.py.

Kept at NATIVE resolution deliberately: the routing gate only ever sees 1500px, but OCR needs the text legible, so these must not be downscaled.

Ten shots are committed, chosen to span the capture-scale range (262px to 5366px wide) since scale is what both the gate and OCR are sensitive to. The false-positive numbers above were measured on all 122; this subset is a regression guard, not the measurement.