fix(event_sweep): resolve overlapping button regions to prevent result page closure during sweeps

This commit is contained in:
Nik Afiq 2026-07-14 20:49:10 +09:00
parent 84790ecb27
commit a86003cf66
3 changed files with 109 additions and 2 deletions

View File

@ -337,7 +337,41 @@ EVENT_STAGE_MODAL_CLOSE_BUTTON = (1704, 271)
# x=1150 sits well inside it) while excluding the character-art panel,
# confirmed against the same saved false-positive screenshot returning
# no match afterward.
EVENT_SWEEP_RESULT_BUTTON_REGION = (1000, 700, 1300, 1050)
#
# A SECOND contamination source found live (2026-07-13, reported by the
# user: "the sweep went well, but I think it overclick and closed the
# result page"): this region's y1=700 overlaps EVENT_SWEEP_START_BUTTON's
# (1400,668) own real cyan-pixel footprint (y 622-715, x 1152-1655 --
# measured directly during bounty.py's own identical-bug investigation,
# since that modal is confirmed pixel-identical to this one, sharing this
# exact button position). Once the real "掃討完了" result dialog is
# dismissed and the flow lands back on the bare stage-info modal (matching
# this function's own second `ends` condition), 掃討開始 becomes visible
# and cyan again -- _find_result_button re-matched its corner, and
# click_result_button clicked it, re-opening a fresh AP-usage-confirm
# dialog that this loop then had no way to recognize as anything other
# than "still a result button showing," matching the user's own "overclick
# closed the result page" diagnosis exactly. y1 shifted to 730 (15px clear
# of the button's measured 715 bottom edge) to exclude it -- kept wide
# enough (through y2=1050) to still cover both a possible SKIP button and
# the final OK button of a bulk/MAX sweep's reveal sequence (~120px apart
# per story_sweep's own established pattern), since this event's own SKIP
# button was never individually pixel-measured. Not yet re-confirmed live
# with a fresh real sweep -- see EVENT_SWEEP_RESULT_BUTTON_MIN_PIXELS'
# comment for the second half of this fix and its own live-test status.
EVENT_SWEEP_RESULT_BUTTON_REGION = (1000, 730, 1300, 1050)
# A THIRD contamination source, same class bounty.py's own investigation
# found in its pixel-identical modal: the stage-info modal's own "獲得期待
# 報酬" reward-icon-preview artwork has a handful of pixels that
# incidentally fall inside SWEEP_CONFIRM_CYAN's broad range even with no
# dialog open. detector.find_color_centroid's min_pixels parameter filters
# this out. Value carried over from BOUNTY_RESULT_BUTTON_MIN_PIXELS by
# analogy (same shared UI component) rather than freshly measured against
# this event's own region -- its search area here is somewhat larger, so
# this may need tuning once a fresh live sweep confirms whether it's
# actually well-calibrated for this specific region.
EVENT_SWEEP_RESULT_BUTTON_MIN_PIXELS = 3000
# Rotation target: which of stages 9-12 to sweep today, per explicit user
# request (2026-07-10) -- "the current event has up to 12 stages, randomly

View File

@ -215,6 +215,41 @@ re-confirmed against a fresh real bulk sweep (that day's AP was reduced to
14/240 by the sweep that surfaced this, too low to force another MAX
sweep) -- the fix is validated against the actual recorded false-positive
screenshot and the same live code path, just not a full new live sweep.
The x-narrowing above turned out to not be the whole story either. A real
run (2026-07-13, user report: "the sweep went well, but I think it
overclick and closed the result page") again logged "unrecognized_state"
despite a real successful MAX sweep (rows 08-12 all read correctly, sweep
confirmed). Root cause this time: EVENT_SWEEP_RESULT_BUTTON_REGION's
y1=700 still overlapped EVENT_SWEEP_START_BUTTON's (1400,668) own real
cyan-pixel footprint (y 622-715, x 1152-1655) -- known precisely without
needing a fresh live measurement, since bounty.py's own investigation into
the identical bug class (its stage-info modal is confirmed pixel-identical
to this one, sharing this exact button position) already measured it
directly from a real screenshot. Once the real "掃討完了" result dialog is
dismissed and the flow lands back on the bare stage-info modal (this
function's own second `ends` condition), 掃討開始 becomes visible and
cyan again -- _find_result_button re-matched its corner and clicked it,
re-opening a fresh AP-usage-confirm dialog this loop had no way to
recognize as anything but "still a result button showing," exactly
matching the user's own diagnosis. Fixed two ways, both ported directly
from bounty.py's own resolution of the identical bug: (1)
EVENT_SWEEP_RESULT_BUTTON_REGION's y1 shifted from 700 to 730 (15px clear
of the button's measured 715 bottom edge), kept wide through y2=1050 to
still cover a possible SKIP-then-OK sequence's ~120px vertical spread,
since this event's own SKIP button was never individually pixel-measured;
(2) detector.find_color_centroid's min_pixels parameter (added for
bounty.py's own second contamination source -- sparse stray pixels in the
modal's own reward-icon artwork) applied here too via
EVENT_SWEEP_RESULT_BUTTON_MIN_PIXELS, carried over from
BOUNTY_RESULT_BUTTON_MIN_PIXELS by analogy rather than freshly measured
against this larger region. _watch_sweep_result also gained a third
`ends` condition on _is_ap_purchase_prompt (mirroring bounty.py's
_is_ticket_purchase_prompt fix) as a second, independent safety layer: if
a purchase prompt is ever reached here anyway, it's recognized and
cancelled explicitly by _sweep_target rather than left to the blind color
search. Not yet re-confirmed live -- deployed but no fresh real sweep run
since.
"""
import datetime
import os
@ -254,7 +289,10 @@ def _is_ap_purchase_prompt(driver, config):
def _find_result_button(driver, config):
return detector.find_color_centroid(config.EVENT_SWEEP_RESULT_BUTTON_REGION, *config.SWEEP_CONFIRM_CYAN)
return detector.find_color_centroid(
config.EVENT_SWEEP_RESULT_BUTTON_REGION, *config.SWEEP_CONFIRM_CYAN,
min_pixels=config.EVENT_SWEEP_RESULT_BUTTON_MIN_PIXELS,
)
def _count_raised_above_one(driver, config):
@ -432,6 +470,22 @@ def _watch_sweep_result(driver, config):
# SKIP/OK sequence has even started) still can't be mistaken for
# "swept" -- only "modal gone after we've actually clicked through
# something" counts.
#
# A THIRD ends condition, added 2026-07-13 after a real user report
# ("the sweep went well, but I think it overclick and closed the
# result page"): EVENT_SWEEP_RESULT_BUTTON_REGION used to overlap
# EVENT_SWEEP_START_BUTTON's own real footprint (see that config
# constant's comment), so once the real result dialog was dismissed and
# the bare stage-info modal reappeared, _find_result_button could
# re-match 掃討開始 itself and click it -- re-opening a fresh AP-usage-
# confirm dialog this loop had no way to recognize as anything but
# "still a result button." The region is now fixed to exclude that
# button's footprint, but this explicit check (mirroring bounty.py's
# own identical `_is_ticket_purchase_prompt` fix for the same bug
# class) is a second, independent layer: if a purchase/insufficient-AP
# prompt is ever reached here anyway, for any reason, it's recognized
# and handled explicitly by the caller rather than left to a blind
# color search that could otherwise re-click into it.
clicked_any = {"value": False}
def click_result_button(d):
@ -442,6 +496,7 @@ def _watch_sweep_result(driver, config):
d.wait(1.5)
ends = {
(lambda d, c: _is_ap_purchase_prompt(d, c)): "prompted_to_purchase",
(lambda d, c: _is_stage_modal_open(d, c) and _find_result_button(d, c) is None): "swept",
(lambda d, c: clicked_any["value"] and not _is_stage_modal_open(d, c) and _find_result_button(d, c) is None): "swept",
}
@ -525,6 +580,14 @@ def _sweep_target(driver, config, stage, count):
outcome = _watch_sweep_result(driver, config)
print(f"[event_sweep] result: {outcome}")
if outcome == "prompted_to_purchase":
# See _watch_sweep_result's own comment -- cancel this dialog
# explicitly rather than let _close_stage_modal's plain Escape loop
# be the only thing standing between it and a real AP purchase.
print("[event_sweep] AP-purchase prompt detected after the sweep -- cancelling without purchasing")
driver.click(*config.SWEEP_CONFIRM_CANCEL_BUTTON)
driver.wait(1)
if not _close_stage_modal(driver, config):
print("[event_sweep] warning: could not confirm stage info modal closed -- leaving it open rather than pressing further keys blindly")
return outcome

10
plan.md
View File

@ -584,6 +584,16 @@ This is the same failure class `CLAUDE.md` already documents for the mailbox/caf
**Status**: both bugs fixed. The originally reported bug (stage 9 unreadable) is fully confirmed via a real successful live sweep. The `_watch_sweep_result` mislabeling's real root cause is now understood and fixed (previous fix attempt addressed a real but secondary issue); confirmed against the actual live false-positive condition, awaiting one more full live sweep to confirm the outcome log itself reads `"swept"` next time AP allows.
#### Phase 14 follow-up #7: `_find_result_button` re-clicking 掃討開始 itself (2026-07-13)
**Reported**: a real MAX sweep of stage 12 completed successfully (all 5 stage rows OCR'd correctly, sweep confirmed) but still logged `unrecognized_state`. The user's own diagnosis: "the sweep went well, but I think it overclick and closed the result page."
Follow-up #6's x-narrowing (1000-1300) turned out to not be the whole fix. Root cause, identified without needing a fresh live sweep: `EVENT_SWEEP_RESULT_BUTTON_REGION`'s y1=700 still overlapped `EVENT_SWEEP_START_BUTTON`'s (1400,668) own real cyan-pixel footprint (y 622-715, x 1152-1655) — this is the **exact same bug class** just fixed in `bounty.py` (see Phase 15's own writeup below), and since bounty's stage-info modal is confirmed pixel-identical to this one (sharing this exact button position), its already-measured footprint could be reused directly rather than re-deriving it. Once the real "掃討完了" result dialog is dismissed and the flow lands back on the bare stage-info modal, 掃討開始 becomes visible and cyan again — `_find_result_button` re-matched its corner and clicked it, re-opening a fresh AP-usage-confirm dialog the loop had no way to recognize as anything but "still a result button," exactly matching the user's own diagnosis.
**Fixed by porting bounty.py's own resolution directly**: (1) `EVENT_SWEEP_RESULT_BUTTON_REGION`'s y1 shifted 700→730 (15px clear of the button's measured 715 edge), kept wide through y2=1050 to still cover a possible SKIP-then-OK sequence's ~120px spread since this event's own SKIP button was never individually measured; (2) `detector.find_color_centroid`'s `min_pixels` parameter applied here too via a new `EVENT_SWEEP_RESULT_BUTTON_MIN_PIXELS` (3000, carried over from `BOUNTY_RESULT_BUTTON_MIN_PIXELS` by analogy — same shared UI component, though this region is somewhat larger so it may need its own tuning); (3) `_watch_sweep_result` gained a third `ends` condition on `_is_ap_purchase_prompt` (mirroring bounty's `_is_ticket_purchase_prompt` fix) as an independent safety layer.
**Confirmed live at zero AP cost**: opened the real stage-12 modal (view only, no count raised, no sweep confirmed) and directly compared `_find_result_button` against the OLD region (falsely matched `(1226, 707)`, sitting right on 掃討開始's own corner) vs. the NEW region+min_pixels (correctly returned `None` on the same resting screen). **Not yet re-confirmed with a fresh full real sweep** reaching an actual `"swept"` log — that requires spending real AP again, deferred pending the user's go-ahead.
### Phase 15: Bounty
Reference: `module/rewarded_task.py`. Local: `ba_auto/tasks/bounty.py`.