Compare commits
4 commits
d71e7bf0d0
...
4c3eaef108
Author | SHA1 | Date | |
---|---|---|---|
4c3eaef108 | |||
6d3640aea0 | |||
fe6a59b0f6 | |||
706fa6a4c0 |
13 changed files with 356 additions and 286 deletions
BIN
mower/resources/clue/filter_all_on.png
(Stored with Git LFS)
Normal file
BIN
mower/resources/clue/filter_all_on.png
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -1,51 +1,53 @@
|
||||||
from mower.solvers.infra.base_mixin import BaseMixin
|
from datetime import timedelta
|
||||||
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
|
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_default_scene = None
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
self.success = False
|
self.success = False
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
def transition(self) -> bool:
|
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
|
||||||
if (
|
def _(self):
|
||||||
scene := self.scene()
|
if self.success:
|
||||||
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
|
return True
|
||||||
self.tap((500, 1000))
|
if self.animation():
|
||||||
elif scene == Scene.INFRA_CONFIDENTIAL:
|
return
|
||||||
if self.animation():
|
# 检查是否领过线索
|
||||||
return
|
daily_scope = ((1822, 208), (1886, 243))
|
||||||
if self.success:
|
if self.find("clue/badge_new", scope=daily_scope):
|
||||||
return True
|
self.tap((1800, 270))
|
||||||
# 检查是否领过线索
|
|
||||||
daily_scope = ((1822, 208), (1886, 243))
|
|
||||||
if self.find("clue/badge_new", scope=daily_scope):
|
|
||||||
self.tap((1800, 270))
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
elif scene == Scene.CLUE_DAILY:
|
|
||||||
exit_pos = (1484, 152)
|
|
||||||
if self.find("clue/icon_notification"):
|
|
||||||
logger.info("当前获得线索数已经达到最大值")
|
|
||||||
self.tap(exit_pos)
|
|
||||||
self.success = True
|
|
||||||
elif clue := clue_cls("daily"):
|
|
||||||
logger.info(f"领取今日线索({clue}号)")
|
|
||||||
self.tap("clue/button_get")
|
|
||||||
else:
|
|
||||||
# 今日线索已领取,点X退出
|
|
||||||
self.tap(exit_pos)
|
|
||||||
self.success = True
|
|
||||||
elif scene == Scene.CLUE_MESSAGE_BOARD:
|
|
||||||
self.cback(3, id="infra_back")
|
|
||||||
elif scene in self.waiting_scene:
|
|
||||||
self.waiting_solver()
|
|
||||||
else:
|
else:
|
||||||
EnterRoomSolver().run("meeting", detail=False)
|
return True
|
||||||
|
|
||||||
|
@TransitionOn(Scene.CLUE_DAILY)
|
||||||
|
def _(self):
|
||||||
|
exit_pos = (1484, 152)
|
||||||
|
if self.find("clue/icon_notification"):
|
||||||
|
logger.info("当前获得线索数已经达到最大值")
|
||||||
|
self.tap(exit_pos)
|
||||||
|
self.success = True
|
||||||
|
elif clue := clue_cls("daily"):
|
||||||
|
logger.info(f"领取今日线索({clue}号)")
|
||||||
|
self.tap("clue/button_get")
|
||||||
|
else:
|
||||||
|
# 今日线索已领取,点X退出
|
||||||
|
self.tap(exit_pos)
|
||||||
|
self.success = True
|
||||||
|
|
||||||
|
@TransitionOn(BaseSolver.waiting_scene)
|
||||||
|
def _(self):
|
||||||
|
self.waiting_solver()
|
||||||
|
|
||||||
|
@TransitionOn()
|
||||||
|
def _(self):
|
||||||
|
ClueMainSolver().run()
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
|
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
|
from mower.utils.solver import BaseSolver, TransitionOn
|
||||||
|
|
||||||
|
from .utils import MeetingSolver
|
||||||
|
|
||||||
|
|
||||||
class GetClueCountSolver(BaseSolver, BaseMixin):
|
class GetClueCountSolver(BaseSolver, BaseMixin):
|
||||||
solver_name = "线索数量"
|
solver_name = "线索数量"
|
||||||
|
solver_max_duration = timedelta(minutes=2)
|
||||||
|
solver_default_scene = None
|
||||||
|
|
||||||
def run(self) -> int:
|
def run(self) -> int:
|
||||||
self.res = -1
|
self.res = -1
|
||||||
|
@ -16,16 +21,19 @@ class GetClueCountSolver(BaseSolver, BaseMixin):
|
||||||
return self.res
|
return self.res
|
||||||
raise ValueError("未找到线索数量")
|
raise ValueError("未找到线索数量")
|
||||||
|
|
||||||
def transition(self) -> bool:
|
@TransitionOn(Scene.INFRA_DETAILS)
|
||||||
if self.find("meeting_arrange_check_in"):
|
def _(self):
|
||||||
self.res = self.read_screen(
|
MeetingSolver().run()
|
||||||
config.recog.img, limit=10, cord=((476, 985), (519, 1020))
|
self.res = self.read_screen(
|
||||||
)
|
config.recog.img, limit=10, cord=((476, 985), (519, 1020))
|
||||||
logger.info(f"当前拥有线索数量为{self.res}")
|
)
|
||||||
return True
|
logger.info(f"当前拥有线索数量为{self.res}")
|
||||||
elif (scene := self.scene()) == Scene.INFRA_CONFIDENTIAL:
|
return True
|
||||||
self.cback(3, id="infra_back")
|
|
||||||
elif scene in self.waiting_scene:
|
@TransitionOn(BaseSolver.waiting_scene)
|
||||||
self.waiting_solver()
|
def _(self):
|
||||||
else:
|
self.waiting_solver()
|
||||||
EnterRoomSolver().run("meeting", detail=False)
|
|
||||||
|
@TransitionOn()
|
||||||
|
def _(self):
|
||||||
|
MeetingSolver().run()
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
from mower.solvers.infra.base_mixin import BaseMixin
|
from datetime import timedelta
|
||||||
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
|
||||||
from mower.utils.rapidocr import ocr_rec
|
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
|
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_default_scene = None
|
||||||
|
|
||||||
def run(self, clue_count) -> None:
|
def run(self, clue_count) -> None:
|
||||||
self.clue_count = clue_count
|
self.clue_count = clue_count
|
||||||
|
@ -20,50 +22,48 @@ class GiveAwaySolver(BaseSolver, BaseMixin):
|
||||||
return
|
return
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
def transition(self) -> bool:
|
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
|
||||||
if (
|
def _(self):
|
||||||
scene := self.scene()
|
self.ctap((1799, 578))
|
||||||
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
|
|
||||||
self.tap((500, 1000))
|
@TransitionOn(Scene.CLUE_GIVE_AWAY)
|
||||||
elif scene == Scene.INFRA_CONFIDENTIAL:
|
def _(self):
|
||||||
self.ctap((1799, 578))
|
if not (config.conf.leifeng_mode or self.clue_count > 9):
|
||||||
elif scene == Scene.CLUE_GIVE_AWAY:
|
return True
|
||||||
if not (config.conf.leifeng_mode or self.clue_count > 9):
|
if self.animation():
|
||||||
self.tap((1868, 54))
|
return
|
||||||
return True
|
if (c := clue_cls("give_away")) is None:
|
||||||
if self.animation():
|
return True
|
||||||
return
|
if config.recog.gray[230][600] < 90:
|
||||||
if (c := clue_cls("give_away")) is None:
|
self.ctap(clue_scope["give_away"], 5)
|
||||||
self.tap((1868, 54))
|
return
|
||||||
return True
|
if self.find("product_complete"):
|
||||||
if config.recog.gray[230][600] < 90:
|
return
|
||||||
self.ctap(clue_scope["give_away"], 5)
|
orange = False
|
||||||
return
|
for i in range(4):
|
||||||
if self.find("clue/icon_notification"):
|
scope = (1285 + (c - 1) * 65, 137 + i * 225)
|
||||||
return
|
bg = loadres("clue/give_away_digit_bg")
|
||||||
if self.detect_product_complete():
|
h, w, _ = bg.shape
|
||||||
return
|
scope = scope, va(scope, (w, h))
|
||||||
orange = False
|
img = cropimg(config.recog.img, scope)
|
||||||
for i in range(4):
|
if cmatch(img, bg):
|
||||||
scope = (1285 + (c - 1) * 65, 137 + i * 225)
|
orange = True
|
||||||
bg = loadres("clue/give_away_digit_bg")
|
break
|
||||||
h, w, _ = bg.shape
|
if not orange:
|
||||||
scope = scope, va(scope, (w, h))
|
i = 0
|
||||||
img = cropimg(config.recog.img, scope)
|
name_top_left = (880, 125 + 225 * i)
|
||||||
if cmatch(img, bg):
|
name_scope = (name_top_left, va(name_top_left, (390, 34)))
|
||||||
orange = True
|
name = cropimg(config.recog.gray, name_scope)
|
||||||
break
|
name = thres2(name, 250)
|
||||||
if not orange:
|
name = crop2content(name)
|
||||||
i = 0
|
name = ocr_rec(name)
|
||||||
name_top_left = (880, 125 + 225 * i)
|
logger.info(f"给{name}送一张线索{c}")
|
||||||
name_scope = (name_top_left, va(name_top_left, (390, 34)))
|
self.tap((1808, 200 + i * 255))
|
||||||
name = cropimg(config.recog.gray, name_scope)
|
|
||||||
name = thres2(name, 250)
|
@TransitionOn(BaseSolver.waiting_scene)
|
||||||
name = crop2content(name)
|
def _(self):
|
||||||
name = ocr_rec(name)
|
self.waiting_solver()
|
||||||
logger.info(f"给{name}送一张线索{c}")
|
|
||||||
self.tap((1808, 200 + i * 255))
|
@TransitionOn()
|
||||||
elif scene in self.waiting_scene:
|
def _(self):
|
||||||
self.waiting_solver()
|
ClueMainSolver().run()
|
||||||
else:
|
|
||||||
EnterRoomSolver().run("meeting", detail=False)
|
|
||||||
|
|
|
@ -1,42 +1,42 @@
|
||||||
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
|
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
|
||||||
|
|
||||||
def transition(self):
|
@TransitionOn(Scene.INFRA_DETAILS)
|
||||||
if (
|
def _(self):
|
||||||
scene := self.scene()
|
MeetingSolver().run()
|
||||||
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
|
if pos := self.find("clue/title_party"):
|
||||||
if pos := self.find("clue/title_party"):
|
self.tap(pos)
|
||||||
self.tap(pos)
|
return
|
||||||
return
|
score, scope = config.recog.match("clue/message_board_entry")
|
||||||
score, scope = config.recog.match("clue/message_board_entry")
|
if score > 0.8:
|
||||||
if score > 0.8:
|
self.ctap(scope)
|
||||||
self.ctap(scope)
|
return
|
||||||
return
|
self.tap("clue/interact")
|
||||||
self.tap("clue/interact")
|
|
||||||
elif scene == Scene.CLUE_MESSAGE_BOARD:
|
@TransitionOn(Scene.CLUE_MESSAGE_BOARD)
|
||||||
if self.animation():
|
def _(self):
|
||||||
return
|
if self.animation():
|
||||||
if pos := self.find("clue/message_board_collect"):
|
return
|
||||||
self.tap(pos)
|
if pos := self.find("clue/message_board_collect"):
|
||||||
return
|
self.tap(pos)
|
||||||
return True
|
return
|
||||||
elif scene in [
|
return True
|
||||||
Scene.INFRA_CONFIDENTIAL,
|
|
||||||
Scene.CLUE_MESSAGE_BOARD_FRIEND,
|
@TransitionOn(BaseSolver.waiting_scene)
|
||||||
Scene.CLUE_ACCESS_RECORD,
|
def _(self):
|
||||||
]:
|
self.waiting_solver()
|
||||||
self.cback(3, id="infra_back")
|
|
||||||
elif scene in self.waiting_scene:
|
@TransitionOn()
|
||||||
self.waiting_solver()
|
def _(self):
|
||||||
else:
|
MeetingSolver().run()
|
||||||
EnterRoomSolver().run("meeting", detail=False)
|
|
||||||
|
|
|
@ -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,7 +27,6 @@ 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):
|
||||||
|
@ -36,8 +34,4 @@ class PartyTimeSolver(BaseSolver, BaseMixin):
|
||||||
|
|
||||||
@TransitionOn()
|
@TransitionOn()
|
||||||
def _(self):
|
def _(self):
|
||||||
EnterRoomSolver().run("meeting", detail=False)
|
MeetingSolver().run()
|
||||||
|
|
||||||
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
|
|
||||||
def _(self):
|
|
||||||
self.cback(3, id="infra_back")
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
|
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
|
||||||
from mower.utils.rapidocr import ocr_rec
|
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
|
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,
|
||||||
|
@ -27,8 +27,10 @@ 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_default_scene = None
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
self.clue_status = {}
|
self.clue_status = {}
|
||||||
|
@ -49,104 +51,101 @@ class PlaceSolver(BaseSolver, BaseMixin):
|
||||||
return cl, st
|
return cl, st
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
def transition(self) -> bool:
|
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
|
||||||
if (
|
def _(self):
|
||||||
scene := self.scene()
|
if self.animation():
|
||||||
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
|
return
|
||||||
self.tap((500, 1000))
|
if unlock_pos := self.detect_unlock():
|
||||||
elif scene == Scene.INFRA_CONFIDENTIAL:
|
self.tap(unlock_pos)
|
||||||
if self.animation():
|
return
|
||||||
return
|
for i in range(1, 8):
|
||||||
|
if is_orange(self.get_color(main_dots[i]).tolist()):
|
||||||
|
self.clue_status[i] = "available"
|
||||||
|
elif clue_cls(i):
|
||||||
|
hsv = config.recog.hsv
|
||||||
|
if 160 < hsv[main_time[i][1]][main_time[i][0]][0] < 180:
|
||||||
|
self.clue_status[i] = "friend"
|
||||||
|
else:
|
||||||
|
self.clue_status[i] = "self"
|
||||||
|
else:
|
||||||
|
self.clue_status[i] = None
|
||||||
|
cl, st = self.place_index()
|
||||||
|
if st in ["available", "self", "available_self_only"]:
|
||||||
|
self.ctap(main_scope[cl], 1)
|
||||||
|
return
|
||||||
|
return True
|
||||||
|
|
||||||
|
@TransitionOn(Scene.CLUE_PLACE)
|
||||||
|
def _(self):
|
||||||
|
cl, st = self.place_index()
|
||||||
|
|
||||||
|
if cl is None:
|
||||||
if unlock_pos := self.detect_unlock():
|
if unlock_pos := self.detect_unlock():
|
||||||
self.tap(unlock_pos)
|
self.tap(unlock_pos)
|
||||||
return
|
return
|
||||||
for i in range(1, 8):
|
return True
|
||||||
if is_orange(self.get_color(main_dots[i]).tolist()):
|
if self.get_color((1328 + 77 * cl, 114))[0] < 150:
|
||||||
self.clue_status[i] = "available"
|
# 右上角 1-7
|
||||||
elif clue_cls(i):
|
self.tap(clue_scope[cl])
|
||||||
hsv = config.recog.hsv
|
return
|
||||||
if 160 < hsv[main_time[i][1]][main_time[i][0]][0] < 180:
|
receive = st in ["available", "self"]
|
||||||
self.clue_status[i] = "friend"
|
filter_pos = filter_receive if receive else filter_self
|
||||||
else:
|
if not all(self.get_color(filter_pos) > [252] * 3):
|
||||||
self.clue_status[i] = "self"
|
self.tap(filter_pos)
|
||||||
|
return
|
||||||
|
self.clue_list = []
|
||||||
|
for cp in clue_pos:
|
||||||
|
clue_img = cropimg(config.recog.img, tl2p(cp))
|
||||||
|
res = loadres(f"clue/{cl}")
|
||||||
|
result = cv2.matchTemplate(clue_img, res, cv2.TM_CCOEFF_NORMED)
|
||||||
|
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
||||||
|
if max_val > tm_thres:
|
||||||
|
name_scope = (va(cp, (274, 99)), va(cp, (580, 134)))
|
||||||
|
name_img = cropimg(config.recog.gray, name_scope)
|
||||||
|
name = ocr_rec(name_img)
|
||||||
|
time_scope = (va(cp, (45, 222)), va(cp, (168, 255)))
|
||||||
|
time_hsv = cropimg(config.recog.hsv, time_scope)
|
||||||
|
if 165 < time_hsv[0][0][0] < 175:
|
||||||
|
time_img = thres2(cropimg(config.recog.gray, time_scope), 180)
|
||||||
|
time_img = crop2content(time_img)
|
||||||
|
time = ocr_rec(time_img)
|
||||||
else:
|
else:
|
||||||
self.clue_status[i] = None
|
time = None
|
||||||
cl, st = self.place_index()
|
self.clue_list.append({"name": name, "time": time, "scope": tl2p(cp)})
|
||||||
if st in ["available", "self", "available_self_only"]:
|
|
||||||
self.ctap(main_scope[cl], 1)
|
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
return True
|
break
|
||||||
elif scene == Scene.CLUE_PLACE:
|
if self.clue_list:
|
||||||
cl, st = self.place_index()
|
list_name = "接收库" if receive else "自有库"
|
||||||
|
logger.info(f"{cl}号线索{list_name}:{self.clue_list}")
|
||||||
if cl is None:
|
selected = None
|
||||||
if unlock_pos := self.detect_unlock():
|
for c in self.clue_list:
|
||||||
self.tap(unlock_pos)
|
if c["time"]:
|
||||||
return
|
selected = c
|
||||||
else:
|
|
||||||
self.tap(exit_pos)
|
|
||||||
return True
|
|
||||||
if self.get_color((1328 + 77 * cl, 114))[0] < 150:
|
|
||||||
# 右上角 1-7
|
|
||||||
self.tap(clue_scope[cl])
|
|
||||||
return
|
|
||||||
receive = st in ["available", "self"]
|
|
||||||
filter_pos = filter_receive if receive else filter_self
|
|
||||||
if not all(self.get_color(filter_pos) > [252] * 3):
|
|
||||||
self.tap(filter_pos)
|
|
||||||
return
|
|
||||||
self.clue_list = []
|
|
||||||
for cp in clue_pos:
|
|
||||||
clue_img = cropimg(config.recog.img, tl2p(cp))
|
|
||||||
res = loadres(f"clue/{cl}")
|
|
||||||
result = cv2.matchTemplate(clue_img, res, cv2.TM_CCOEFF_NORMED)
|
|
||||||
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
|
||||||
if max_val > tm_thres:
|
|
||||||
name_scope = (va(cp, (274, 99)), va(cp, (580, 134)))
|
|
||||||
name_img = cropimg(config.recog.gray, name_scope)
|
|
||||||
name = ocr_rec(name_img)
|
|
||||||
time_scope = (va(cp, (45, 222)), va(cp, (168, 255)))
|
|
||||||
time_hsv = cropimg(config.recog.hsv, time_scope)
|
|
||||||
if 165 < time_hsv[0][0][0] < 175:
|
|
||||||
time_img = thres2(cropimg(config.recog.gray, time_scope), 180)
|
|
||||||
time_img = crop2content(time_img)
|
|
||||||
time = ocr_rec(time_img)
|
|
||||||
else:
|
|
||||||
time = None
|
|
||||||
self.clue_list.append(
|
|
||||||
{"name": name, "time": time, "scope": tl2p(cp)}
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
break
|
break
|
||||||
if self.clue_list:
|
selected = selected or self.clue_list[0]
|
||||||
list_name = "接收库" if receive else "自有库"
|
self.tap(selected["scope"])
|
||||||
logger.info(f"{cl}号线索{list_name}:{self.clue_list}")
|
if self.clue_status[cl] == "available":
|
||||||
selected = None
|
self.clue_status[cl] = "friend"
|
||||||
for c in self.clue_list:
|
elif self.clue_status[cl] == "available_self_only":
|
||||||
if c["time"]:
|
self.clue_status[cl] = "self_only"
|
||||||
selected = c
|
elif self.clue_status[cl] == "self":
|
||||||
break
|
self.clue_status[cl] = "friend"
|
||||||
selected = selected or self.clue_list[0]
|
|
||||||
self.tap(selected["scope"])
|
|
||||||
if self.clue_status[cl] == "available":
|
|
||||||
self.clue_status[cl] = "friend"
|
|
||||||
elif self.clue_status[cl] == "available_self_only":
|
|
||||||
self.clue_status[cl] = "self_only"
|
|
||||||
elif self.clue_status[cl] == "self":
|
|
||||||
self.clue_status[cl] = "friend"
|
|
||||||
else:
|
|
||||||
self.clue_status[cl] = None
|
|
||||||
else:
|
else:
|
||||||
if self.clue_status[cl] == "available":
|
self.clue_status[cl] = None
|
||||||
self.clue_status[cl] = "available_self_only"
|
|
||||||
elif self.clue_status[cl] == "available_self_only":
|
|
||||||
self.clue_status[cl] = None
|
|
||||||
elif self.clue_status[cl] == "self":
|
|
||||||
self.clue_status[cl] = "self_only"
|
|
||||||
else:
|
|
||||||
self.clue_status[cl] = None
|
|
||||||
elif scene in self.waiting_scene:
|
|
||||||
self.waiting_solver()
|
|
||||||
else:
|
else:
|
||||||
EnterRoomSolver().run("meeting", detail=False)
|
if self.clue_status[cl] == "available":
|
||||||
|
self.clue_status[cl] = "available_self_only"
|
||||||
|
elif self.clue_status[cl] == "available_self_only":
|
||||||
|
self.clue_status[cl] = None
|
||||||
|
elif self.clue_status[cl] == "self":
|
||||||
|
self.clue_status[cl] = "self_only"
|
||||||
|
else:
|
||||||
|
self.clue_status[cl] = None
|
||||||
|
|
||||||
|
@TransitionOn(BaseSolver.waiting_scene)
|
||||||
|
def _(self):
|
||||||
|
self.waiting_solver()
|
||||||
|
|
||||||
|
@TransitionOn()
|
||||||
|
def _(self):
|
||||||
|
ClueMainSolver().run()
|
||||||
|
|
|
@ -1,45 +1,44 @@
|
||||||
from mower.solvers.infra.base_mixin import BaseMixin
|
from datetime import timedelta
|
||||||
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
|
||||||
from mower.utils.rapidocr import ocr_rec
|
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
|
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_default_scene = None
|
||||||
|
|
||||||
def run(self):
|
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
|
||||||
super().run()
|
def _(self):
|
||||||
|
receive_scope = ((1822, 365), (1886, 400))
|
||||||
|
if self.find("clue/badge_new", scope=receive_scope):
|
||||||
|
self.ctap((1800, 430))
|
||||||
|
return
|
||||||
|
return True
|
||||||
|
|
||||||
def transition(self) -> bool:
|
@TransitionOn(Scene.CLUE_RECEIVE)
|
||||||
if (
|
def _(self):
|
||||||
scene := self.scene()
|
if self.find("product_complete"):
|
||||||
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
|
return
|
||||||
self.tap((500, 1000))
|
if (clue := clue_cls("receive")) is None:
|
||||||
elif scene == Scene.INFRA_CONFIDENTIAL:
|
return True
|
||||||
receive_scope = ((1822, 365), (1886, 400))
|
name_scope = ((1580, 220), (1880, 255))
|
||||||
if self.find("clue/badge_new", scope=receive_scope):
|
name_img = cropimg(config.recog.gray, name_scope)
|
||||||
self.ctap((1800, 430))
|
name = ocr_rec(name_img) or "好友"
|
||||||
else:
|
logger.info(f"接收{name}的{clue}号线索")
|
||||||
return True
|
self.tap(name_scope)
|
||||||
elif scene == Scene.CLUE_RECEIVE:
|
|
||||||
if self.find("infra_complete/信用", scope=((1440, 130), (1920, 193))):
|
@TransitionOn(BaseSolver.waiting_scene)
|
||||||
self.sleep()
|
def _(self):
|
||||||
return
|
self.waiting_solver()
|
||||||
if clue := clue_cls("receive"):
|
|
||||||
name_scope = ((1580, 220), (1880, 255))
|
@TransitionOn()
|
||||||
name_img = cropimg(config.recog.gray, name_scope)
|
def _(self):
|
||||||
name = ocr_rec(name_img) or "好友"
|
ClueMainSolver().run()
|
||||||
logger.info(f"接收{name}的{clue}号线索")
|
|
||||||
self.tap(name_scope)
|
|
||||||
else:
|
|
||||||
self.tap(exit_pos)
|
|
||||||
elif scene in self.waiting_scene:
|
|
||||||
self.waiting_solver()
|
|
||||||
else:
|
|
||||||
EnterRoomSolver().run("meeting", detail=False)
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue