Auto-assignment (Board.add_target/add_ally with no explicit id_) now scopes its 1/2/3... sequence per TYPE within each group, not one sequence shared across every type in the group -- Tank#1/Infantry#1 rather than Tank#A/Infantry#B, matching the game's own numbering. Reverses the type-scoping half of an earlier fix in this file (see TODO.md's "Allies and enemies seem to share indices" entry) per explicit user direction; the targets-vs-allies namespace split that fix also made is untouched, still correct. _next_free_id (letters, rolling over to "AA" past 26) is replaced by _next_free_numeric_id -- a plain counter can't run out the way a fixed alphabet could, so there's no equivalent rollover concern. test_models.py updated to match (one test asserts the opposite of before, renamed accordingly). detected_id (map_vision.read_marker_id) was being logged but never shown anywhere a human could actually check it against the screenshot before now: added to the proposal popover's heading and the pending-proposal's own on-map label. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
71 lines
2.7 KiB
Python
71 lines
2.7 KiB
Python
"""_accept_proposal: an accepted proposal's entity id should prefer the
|
|
marker's own detected "#<N>" id (map_vision.read_marker_id, via
|
|
Proposal.detected_id) over an auto-assigned number, so ids on the board
|
|
match what's actually on screen -- falling back to auto-assign only when
|
|
there's no detection, or it collides with an id already used for that
|
|
type in that group (see _accept_proposal's own docstring, and
|
|
models.py's _next_free_numeric_id for the per-type auto-assignment
|
|
these fall back to).
|
|
|
|
Needs a real Adw/Gtk init (MainWindow.__new__ skips __init__, so no
|
|
window/widgets are actually built, but Adw.init() is still required for
|
|
the module import chain), same pattern proven in this repo already for
|
|
headlessly exercising GTK-adjacent code.
|
|
"""
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "4.0")
|
|
gi.require_version("Gdk", "4.0")
|
|
gi.require_version("Adw", "1")
|
|
from gi.repository import Adw # noqa: E402
|
|
|
|
Adw.init()
|
|
|
|
from fenigma.app import MainWindow # noqa: E402
|
|
from fenigma.map_import import Proposal # noqa: E402
|
|
from fenigma.models import Board # noqa: E402
|
|
|
|
|
|
def _window() -> MainWindow:
|
|
win = MainWindow.__new__(MainWindow) # skip __init__: no widgets needed for this
|
|
win.board = Board()
|
|
return win
|
|
|
|
|
|
def _proposal(detected_id=None, side="hostile", sub_x=0, sub_y=0) -> Proposal:
|
|
return Proposal(side=side, label="G8", sub_x=sub_x, sub_y=sub_y, unit=None,
|
|
centre=(0, 0), box=(0, 0, 0, 0), detected_id=detected_id)
|
|
|
|
|
|
def test_accept_uses_the_detected_id_when_present():
|
|
win = _window()
|
|
win._accept_proposal(_proposal(detected_id="8"))
|
|
assert win.board.targets[0].id == "8"
|
|
|
|
|
|
def test_accept_falls_back_to_auto_id_with_no_detection():
|
|
win = _window()
|
|
win._accept_proposal(_proposal(detected_id=None))
|
|
assert win.board.targets[0].id == "1"
|
|
|
|
|
|
def test_accept_falls_back_to_auto_id_on_a_detected_id_collision():
|
|
win = _window()
|
|
win._accept_proposal(_proposal(detected_id="8", sub_x=1))
|
|
win._accept_proposal(_proposal(detected_id="8", sub_x=2)) # same detected id, must not collide
|
|
ids = [t.id for t in win.board.targets]
|
|
assert ids[0] == "8"
|
|
assert ids[1] != "8"
|
|
|
|
|
|
def test_accept_keeps_target_and_ally_id_detection_in_separate_namespaces():
|
|
"""A detected id colliding with an existing ALLY id shouldn't force a
|
|
TARGET accept to fall back -- targets/allies are already a separate
|
|
id namespace everywhere else (Board.add_target/add_ally), detected-id
|
|
preference shouldn't quietly merge them."""
|
|
win = _window()
|
|
win._accept_proposal(_proposal(detected_id="8", side="friendly"))
|
|
win._accept_proposal(_proposal(detected_id="8", side="hostile"))
|
|
assert win.board.allies[0].id == "8"
|
|
assert win.board.targets[0].id == "8"
|