线索改进

This commit is contained in:
zhbaor 2025-06-29 20:28:54 +08:00
parent fe6a59b0f6
commit 6d3640aea0
13 changed files with 118 additions and 132 deletions

BIN
mower/resources/clue/filter_all_on.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -1,15 +1,13 @@
from datetime import timedelta from datetime import timedelta
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils.log import logger from mower.utils.log import logger
from mower.utils.recognize import Scene from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver, TransitionOn from mower.utils.solver import BaseSolver, TransitionOn
from .utils import clue_cls from .utils import ClueMainSolver, clue_cls
class DailySolver(BaseSolver, BaseMixin): class DailySolver(BaseSolver):
solver_name = "每日线索领取" solver_name = "每日线索领取"
solver_max_duration = timedelta(minutes=2) solver_max_duration = timedelta(minutes=2)
solver_default_scene = None solver_default_scene = None
@ -18,19 +16,12 @@ class DailySolver(BaseSolver, BaseMixin):
self.success = False self.success = False
super().run() super().run()
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
if self.detect_room() != "meeting":
EnterRoomSolver().run("meeting", detail=False)
return
self.tap((500, 1000))
@TransitionOn(Scene.INFRA_CONFIDENTIAL) @TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self): def _(self):
if self.animation():
return
if self.success: if self.success:
return True return True
if self.animation():
return
# 检查是否领过线索 # 检查是否领过线索
daily_scope = ((1822, 208), (1886, 243)) daily_scope = ((1822, 208), (1886, 243))
if self.find("clue/badge_new", scope=daily_scope): if self.find("clue/badge_new", scope=daily_scope):
@ -53,17 +44,10 @@ class DailySolver(BaseSolver, BaseMixin):
self.tap(exit_pos) self.tap(exit_pos)
self.success = True self.success = True
@TransitionOn(Scene.CLUE_MESSAGE_BOARD)
def _(self):
self.cback(3, id="infra_back")
@TransitionOn(BaseSolver.waiting_scene) @TransitionOn(BaseSolver.waiting_scene)
def _(self): def _(self):
self.waiting_solver() self.waiting_solver()
@TransitionOn() @TransitionOn()
def _(self): def _(self):
if self.detect_room() != "meeting": ClueMainSolver().run()
EnterRoomSolver().run("meeting", detail=False)
return
self.tap((500, 1000))

View file

@ -1,12 +1,13 @@
from datetime import timedelta from datetime import timedelta
from mower.solvers.infra.base_mixin import BaseMixin from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config from mower.utils import config
from mower.utils.log import logger from mower.utils.log import logger
from mower.utils.recognize import Scene from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver, TransitionOn from mower.utils.solver import BaseSolver, TransitionOn
from .utils import MeetingSolver
class GetClueCountSolver(BaseSolver, BaseMixin): class GetClueCountSolver(BaseSolver, BaseMixin):
solver_name = "线索数量" solver_name = "线索数量"
@ -22,13 +23,12 @@ class GetClueCountSolver(BaseSolver, BaseMixin):
@TransitionOn(Scene.INFRA_DETAILS) @TransitionOn(Scene.INFRA_DETAILS)
def _(self): def _(self):
if self.find("meeting_arrange_check_in"): MeetingSolver().run()
self.res = self.read_screen( self.res = self.read_screen(
config.recog.img, limit=10, cord=((476, 985), (519, 1020)) config.recog.img, limit=10, cord=((476, 985), (519, 1020))
) )
logger.info(f"当前拥有线索数量为{self.res}") logger.info(f"当前拥有线索数量为{self.res}")
return True return True
self.cback(3, id="infra_back")
@TransitionOn(BaseSolver.waiting_scene) @TransitionOn(BaseSolver.waiting_scene)
def _(self): def _(self):
@ -36,4 +36,4 @@ class GetClueCountSolver(BaseSolver, BaseMixin):
@TransitionOn() @TransitionOn()
def _(self): def _(self):
EnterRoomSolver().run("meeting", detail=False) MeetingSolver().run()

View file

@ -1,7 +1,5 @@
from datetime import timedelta from datetime import timedelta
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config from mower.utils import config
from mower.utils.image import cmatch, crop2content, cropimg, loadres, thres2 from mower.utils.image import cmatch, crop2content, cropimg, loadres, thres2
from mower.utils.log import logger from mower.utils.log import logger
@ -10,10 +8,10 @@ from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver, TransitionOn from mower.utils.solver import BaseSolver, TransitionOn
from mower.utils.vector import va from mower.utils.vector import va
from .utils import clue_cls, clue_scope from .utils import ClueMainSolver, clue_cls, clue_scope
class GiveAwaySolver(BaseSolver, BaseMixin): class GiveAwaySolver(BaseSolver):
solver_name = "传递线索" solver_name = "传递线索"
solver_max_duration = timedelta(minutes=2) solver_max_duration = timedelta(minutes=2)
solver_default_scene = None solver_default_scene = None
@ -24,13 +22,6 @@ class GiveAwaySolver(BaseSolver, BaseMixin):
return return
super().run() super().run()
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
if self.detect_room() != "meeting":
EnterRoomSolver().run("meeting", detail=False)
return
self.tap((500, 1000))
@TransitionOn(Scene.INFRA_CONFIDENTIAL) @TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self): def _(self):
self.ctap((1799, 578)) self.ctap((1799, 578))
@ -38,19 +29,15 @@ class GiveAwaySolver(BaseSolver, BaseMixin):
@TransitionOn(Scene.CLUE_GIVE_AWAY) @TransitionOn(Scene.CLUE_GIVE_AWAY)
def _(self): def _(self):
if not (config.conf.leifeng_mode or self.clue_count > 9): if not (config.conf.leifeng_mode or self.clue_count > 9):
self.tap((1868, 54))
return True return True
if self.animation(): if self.animation():
return return
if (c := clue_cls("give_away")) is None: if (c := clue_cls("give_away")) is None:
self.tap((1868, 54))
return True return True
if config.recog.gray[230][600] < 90: if config.recog.gray[230][600] < 90:
self.ctap(clue_scope["give_away"], 5) self.ctap(clue_scope["give_away"], 5)
return return
if self.find("clue/icon_notification"): if self.find("product_complete"):
return
if self.detect_product_complete():
return return
orange = False orange = False
for i in range(4): for i in range(4):
@ -79,7 +66,4 @@ class GiveAwaySolver(BaseSolver, BaseMixin):
@TransitionOn() @TransitionOn()
def _(self): def _(self):
if self.detect_room() != "meeting": ClueMainSolver().run()
EnterRoomSolver().run("meeting", detail=False)
return
self.tap((500, 1000))

View file

@ -1,22 +1,20 @@
from datetime import timedelta from datetime import timedelta
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config from mower.utils import config
from mower.utils.scene import Scene from mower.utils.scene import Scene
from mower.utils.solver import BaseSolver, TransitionOn from mower.utils.solver import BaseSolver, TransitionOn
from .utils import MeetingSolver
class MessageBoard(BaseSolver, BaseMixin):
class MessageBoard(BaseSolver):
solver_name = "留言板" solver_name = "留言板"
solver_max_duration = timedelta(minutes=2) solver_max_duration = timedelta(minutes=2)
solver_default_scene = None solver_default_scene = None
@TransitionOn(Scene.INFRA_DETAILS) @TransitionOn(Scene.INFRA_DETAILS)
def _(self): def _(self):
if self.detect_room() != "meeting": MeetingSolver().run()
EnterRoomSolver().run("meeting", detail=False)
return
if pos := self.find("clue/title_party"): if pos := self.find("clue/title_party"):
self.tap(pos) self.tap(pos)
return return
@ -35,23 +33,10 @@ class MessageBoard(BaseSolver, BaseMixin):
return return
return True return True
@TransitionOn(
[
Scene.INFRA_CONFIDENTIAL,
Scene.CLUE_MESSAGE_BOARD_FRIEND,
Scene.CLUE_ACCESS_RECORD,
]
)
def _(self):
self.cback(3, id="infra_back")
@TransitionOn(BaseSolver.waiting_scene) @TransitionOn(BaseSolver.waiting_scene)
def _(self): def _(self):
self.waiting_solver() self.waiting_solver()
@TransitionOn() @TransitionOn()
def _(self): def _(self):
if self.detect_room() != "meeting": MeetingSolver().run()
EnterRoomSolver().run("meeting", detail=False)
return
self.tap((500, 1000))

View file

@ -1,12 +1,13 @@
from datetime import timedelta from datetime import timedelta
from mower.solvers.infra.base_mixin import BaseMixin from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config from mower.utils import config
from mower.utils.log import logger from mower.utils.log import logger
from mower.utils.recognize import Scene from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver, TransitionOn from mower.utils.solver import BaseSolver, TransitionOn
from .utils import MeetingSolver
class PartyTimeSolver(BaseSolver, BaseMixin): class PartyTimeSolver(BaseSolver, BaseMixin):
solver_name = "线索交流结束时间" solver_name = "线索交流结束时间"
@ -15,9 +16,7 @@ class PartyTimeSolver(BaseSolver, BaseMixin):
@TransitionOn(Scene.INFRA_DETAILS) @TransitionOn(Scene.INFRA_DETAILS)
def _(self): def _(self):
if self.detect_room() != "meeting": MeetingSolver().run()
EnterRoomSolver().run("meeting", detail=False)
return
if not self.find("clue/party_on"): if not self.find("clue/party_on"):
config.party_time = None config.party_time = None
logger.info("线索交流未开启") logger.info("线索交流未开启")
@ -28,16 +27,11 @@ class PartyTimeSolver(BaseSolver, BaseMixin):
return True return True
if pos := self.find("clue/show_party_details"): if pos := self.find("clue/show_party_details"):
self.tap(pos) self.tap(pos)
return
@TransitionOn(BaseSolver.waiting_scene) @TransitionOn(BaseSolver.waiting_scene)
def _(self): def _(self):
self.waiting_solver() self.waiting_solver()
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self):
self.cback(3, id="infra_back")
@TransitionOn() @TransitionOn()
def _(self): def _(self):
EnterRoomSolver().run("meeting", detail=False) MeetingSolver().run()

View file

@ -2,8 +2,6 @@ from datetime import timedelta
import cv2 import cv2
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config from mower.utils import config
from mower.utils.image import crop2content, cropimg, loadres, thres2 from mower.utils.image import crop2content, cropimg, loadres, thres2
from mower.utils.log import logger from mower.utils.log import logger
@ -13,9 +11,9 @@ from mower.utils.solver import BaseSolver, TransitionOn
from mower.utils.vector import va from mower.utils.vector import va
from .utils import ( from .utils import (
ClueMainSolver,
clue_cls, clue_cls,
clue_scope, clue_scope,
exit_pos,
is_orange, is_orange,
main_dots, main_dots,
main_scope, main_scope,
@ -29,7 +27,7 @@ filter_receive = (1900, 45)
filter_self = (1610, 70) filter_self = (1610, 70)
class PlaceSolver(BaseSolver, BaseMixin): class PlaceSolver(BaseSolver):
solver_name = "放置线索" solver_name = "放置线索"
solver_max_duration = timedelta(minutes=2) solver_max_duration = timedelta(minutes=2)
solver_default_scene = None solver_default_scene = None
@ -53,13 +51,6 @@ class PlaceSolver(BaseSolver, BaseMixin):
return cl, st return cl, st
return None, None return None, None
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
if self.detect_room() != "meeting":
EnterRoomSolver().run("meeting", detail=False)
return
self.tap((500, 1000))
@TransitionOn(Scene.INFRA_CONFIDENTIAL) @TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self): def _(self):
if self.animation(): if self.animation():
@ -82,7 +73,6 @@ class PlaceSolver(BaseSolver, BaseMixin):
if st in ["available", "self", "available_self_only"]: if st in ["available", "self", "available_self_only"]:
self.ctap(main_scope[cl], 1) self.ctap(main_scope[cl], 1)
return return
else:
return True return True
@TransitionOn(Scene.CLUE_PLACE) @TransitionOn(Scene.CLUE_PLACE)
@ -93,8 +83,6 @@ class PlaceSolver(BaseSolver, BaseMixin):
if unlock_pos := self.detect_unlock(): if unlock_pos := self.detect_unlock():
self.tap(unlock_pos) self.tap(unlock_pos)
return return
else:
self.tap(exit_pos)
return True return True
if self.get_color((1328 + 77 * cl, 114))[0] < 150: if self.get_color((1328 + 77 * cl, 114))[0] < 150:
# 右上角 1-7 # 右上角 1-7
@ -160,7 +148,4 @@ class PlaceSolver(BaseSolver, BaseMixin):
@TransitionOn() @TransitionOn()
def _(self): def _(self):
if self.detect_room() != "meeting": ClueMainSolver().run()
EnterRoomSolver().run("meeting", detail=False)
return
self.tap((500, 1000))

View file

@ -1,7 +1,5 @@
from datetime import timedelta from datetime import timedelta
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config from mower.utils import config
from mower.utils.image import cropimg from mower.utils.image import cropimg
from mower.utils.log import logger from mower.utils.log import logger
@ -9,42 +7,33 @@ from mower.utils.rapidocr import ocr_rec
from mower.utils.recognize import Scene from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver, TransitionOn from mower.utils.solver import BaseSolver, TransitionOn
from .utils import clue_cls, exit_pos from .utils import ClueMainSolver, clue_cls
class ReceiveSolver(BaseSolver, BaseMixin): class ReceiveSolver(BaseSolver):
solver_name = "接收好友线索" solver_name = "接收好友线索"
solver_max_duration = timedelta(minutes=2) solver_max_duration = timedelta(minutes=2)
solver_default_scene = None solver_default_scene = None
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
if self.detect_room() != "meeting":
EnterRoomSolver().run("meeting", detail=False)
return
self.tap((500, 1000))
@TransitionOn(Scene.INFRA_CONFIDENTIAL) @TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self): def _(self):
receive_scope = ((1822, 365), (1886, 400)) receive_scope = ((1822, 365), (1886, 400))
if self.find("clue/badge_new", scope=receive_scope): if self.find("clue/badge_new", scope=receive_scope):
self.ctap((1800, 430)) self.ctap((1800, 430))
else: return
return True return True
@TransitionOn(Scene.CLUE_RECEIVE) @TransitionOn(Scene.CLUE_RECEIVE)
def _(self): def _(self):
if self.find("infra_complete/信用", scope=((1440, 130), (1920, 193))): if self.find("product_complete"):
self.sleep()
return return
if clue := clue_cls("receive"): if (clue := clue_cls("receive")) is None:
return True
name_scope = ((1580, 220), (1880, 255)) name_scope = ((1580, 220), (1880, 255))
name_img = cropimg(config.recog.gray, name_scope) name_img = cropimg(config.recog.gray, name_scope)
name = ocr_rec(name_img) or "好友" name = ocr_rec(name_img) or "好友"
logger.info(f"接收{name}{clue}号线索") logger.info(f"接收{name}{clue}号线索")
self.tap(name_scope) self.tap(name_scope)
else:
self.tap(exit_pos)
@TransitionOn(BaseSolver.waiting_scene) @TransitionOn(BaseSolver.waiting_scene)
def _(self): def _(self):
@ -52,7 +41,4 @@ class ReceiveSolver(BaseSolver, BaseMixin):
@TransitionOn() @TransitionOn()
def _(self): def _(self):
if self.detect_room() != "meeting": ClueMainSolver().run()
EnterRoomSolver().run("meeting", detail=False)
return
self.tap((500, 1000))

View file

@ -1,7 +1,15 @@
import cv2 from datetime import timedelta
import cv2
import networkx as nx
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config from mower.utils import config
from mower.utils.graph.utils import DG
from mower.utils.image import cropimg, loadres from mower.utils.image import cropimg, loadres
from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver, TransitionOn
from mower.utils.vector import va from mower.utils.vector import va
clue_size = (162, 216) clue_size = (162, 216)
@ -45,7 +53,6 @@ for i in range(1, 8):
main_time[i] = va(clue_top_left[i], main_time_offset) main_time[i] = va(clue_top_left[i], main_time_offset)
main_scope[i] = tl2p(va(clue_top_left[i], main_offset)) main_scope[i] = tl2p(va(clue_top_left[i], main_offset))
tm_thres = 0.6 tm_thres = 0.6
exit_pos = (1239, 144)
def clue_cls(scope): def clue_cls(scope):
@ -58,3 +65,53 @@ def clue_cls(scope):
if max_val > tm_thres: if max_val > tm_thres:
return i return i
return None return None
class MeetingSolver(BaseSolver, BaseMixin):
solver_name = "会客室"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
@TransitionOn(Scene.INFRA_MAIN)
def _(self):
EnterRoomSolver().run("meeting", detail=False)
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
if self.detect_room() != "meeting":
self.scene_graph_step(Scene.INFRA_MAIN)
return
if self.find("meeting_arrange_check_in"):
return True
self.tap((960, 540))
@TransitionOn()
def _(self):
try:
nx.shortest_path(DG, self.scene(), Scene.INFRA_DETAILS)
self.scene_graph_step(Scene.INFRA_DETAILS)
except nx.NetworkXNoPath:
self.scene_graph_step(Scene.INFRA_MAIN)
class ClueMainSolver(BaseSolver):
solver_name = "主界面"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
MeetingSolver().run()
self.tap((500, 1000))
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self):
return True
@TransitionOn()
def _(self):
try:
nx.shortest_path(DG, self.scene(), Scene.INFRA_CONFIDENTIAL)
self.scene_graph_step(Scene.INFRA_CONFIDENTIAL)
except nx.NetworkXNoPath:
self.scene_graph_step(Scene.INFRA_MAIN)

View file

@ -18,10 +18,10 @@ def todo_complete(solver: BaseSolver):
@edge(Scene.ACTIVITY_ROOM_DETAILS, Scene.INFRA_MAIN) @edge(Scene.ACTIVITY_ROOM_DETAILS, Scene.INFRA_MAIN)
@edge(Scene.CTRLCENTER_ASSISTANT, Scene.INFRA_MAIN) @edge(Scene.CTRLCENTER_ASSISTANT, Scene.INFRA_MAIN)
@edge(Scene.CLUE_DAILY, Scene.INFRA_CONFIDENTIAL) @edge(Scene.CLUE_DAILY, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_RECEIVE, Scene.INFRA_CONFIDENTIAL) @edge(Scene.CLUE_RECEIVE, Scene.INFRA_DETAILS)
@edge(Scene.CLUE_GIVE_AWAY, Scene.INFRA_CONFIDENTIAL) @edge(Scene.CLUE_GIVE_AWAY, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_SUMMARY, Scene.INFRA_CONFIDENTIAL) @edge(Scene.CLUE_SUMMARY, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_PLACE, Scene.INFRA_CONFIDENTIAL) @edge(Scene.CLUE_PLACE, Scene.INFRA_DETAILS)
@edge(Scene.CLUE_MESSAGE_BOARD, Scene.INFRA_CONFIDENTIAL) @edge(Scene.CLUE_MESSAGE_BOARD, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_MESSAGE_BOARD_FRIEND, Scene.CLUE_MESSAGE_BOARD) @edge(Scene.CLUE_MESSAGE_BOARD_FRIEND, Scene.CLUE_MESSAGE_BOARD)
@edge(Scene.CLUE_ACCESS_RECORD, Scene.CLUE_MESSAGE_BOARD) @edge(Scene.CLUE_ACCESS_RECORD, Scene.CLUE_MESSAGE_BOARD)
@ -33,6 +33,12 @@ def infra_back(solver: BaseSolver):
solver.cback(3, id="infra_back") solver.cback(3, id="infra_back")
@edge(Scene.CLUE_RECEIVE, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_PLACE, Scene.INFRA_CONFIDENTIAL)
def clue_receive_back(solver: BaseSolver):
solver.tap((960, 1000))
@edge(Scene.RIIC_OPERATOR_SELECT, Scene.INFRA_DETAILS) @edge(Scene.RIIC_OPERATOR_SELECT, Scene.INFRA_DETAILS)
def riic_operator_select(solver: BaseSolver): def riic_operator_select(solver: BaseSolver):
solver.ctap("confirm_blue", 3) solver.ctap("confirm_blue", 3)

View file

@ -283,7 +283,7 @@ class Recognizer:
self.scene = Scene.CLUE_GIVE_AWAY self.scene = Scene.CLUE_GIVE_AWAY
elif self.find("clue/summary"): elif self.find("clue/summary"):
self.scene = Scene.CLUE_SUMMARY self.scene = Scene.CLUE_SUMMARY
elif self.find("clue/filter_all"): elif self.find("clue/filter_all") or self.find("clue/filter_all_on"):
self.scene = Scene.CLUE_PLACE self.scene = Scene.CLUE_PLACE
elif self.find("clue/message_board_banner"): elif self.find("clue/message_board_banner"):
self.scene = Scene.CLUE_MESSAGE_BOARD self.scene = Scene.CLUE_MESSAGE_BOARD

View file

@ -18,6 +18,7 @@ color = {
"clue/access_records.jpg": (843, 279), "clue/access_records.jpg": (843, 279),
"clue/daily": (526, 623), "clue/daily": (526, 623),
"clue/filter_all": (1297, 99), "clue/filter_all": (1297, 99),
"clue/filter_all_on": (1299, 100),
"clue/give_away": (25, 18), "clue/give_away": (25, 18),
"clue/message_board_banner": (19, 1006), "clue/message_board_banner": (19, 1006),
"clue/message_board_collect": (1657, 876), "clue/message_board_collect": (1657, 876),

View file

@ -69,6 +69,7 @@ Res = Literal[
"clue/button_unlock", "clue/button_unlock",
"clue/daily", "clue/daily",
"clue/filter_all", "clue/filter_all",
"clue/filter_all_on",
"clue/give_away", "clue/give_away",
"clue/give_away_digit_bg", "clue/give_away_digit_bg",
"clue/icon_notification", "clue/icon_notification",