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>
|
||
|---|---|---|
| .. | ||
| map_shots | ||
| writer_shots | ||
| map_shots_gt.json | ||
| README.md | ||
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% andsolve()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.