重构clue_new为Solve
This commit is contained in:
parent
74a9c79786
commit
1dbf508120
11 changed files with 437 additions and 365 deletions
|
@ -7,17 +7,15 @@ from ctypes import CFUNCTYPE, c_char_p, c_int, c_void_p
|
|||
from datetime import datetime, timedelta
|
||||
from typing import Literal
|
||||
|
||||
import cv2
|
||||
|
||||
from arknights_mower.data import agent_list, base_room_list
|
||||
from arknights_mower.solvers.credit import CreditSolver
|
||||
from arknights_mower.solvers.credit_fight import CreditFight
|
||||
from arknights_mower.solvers.cultivate_depot import cultivate as cultivateDepotSolver
|
||||
from arknights_mower.solvers.depotREC import depotREC as DepotSolver
|
||||
from arknights_mower.solvers.infra.base_mixin import BaseMixin
|
||||
from arknights_mower.solvers.infra.clue import ClueSolver
|
||||
from arknights_mower.solvers.infra.enter_room import EnterRoomSolver
|
||||
from arknights_mower.solvers.infra.get_agent_from_room import GetAgentFromRoomSolver
|
||||
from arknights_mower.solvers.infra.get_clue_count import GetClueCountSolver
|
||||
from arknights_mower.solvers.infra.reload import (
|
||||
ReloadSolver,
|
||||
)
|
||||
|
@ -29,18 +27,16 @@ from arknights_mower.solvers.operation import OperationSolver
|
|||
from arknights_mower.solvers.reclamation_algorithm import ReclamationAlgorithm
|
||||
from arknights_mower.solvers.recruit import RecruitSolver
|
||||
from arknights_mower.solvers.secret_front import SecretFront
|
||||
from arknights_mower.solvers.shop import CreditShop
|
||||
from arknights_mower.solvers.sign_in import SignInSolver
|
||||
from arknights_mower.solvers.skland import SKLand
|
||||
from arknights_mower.solvers.sss_navi import SSSNaviSolver
|
||||
from arknights_mower.utils import config, detector, rapidocr
|
||||
from arknights_mower.utils import config, detector
|
||||
from arknights_mower.utils import typealias as tp
|
||||
from arknights_mower.utils.csleep import MowerExit, csleep
|
||||
from arknights_mower.utils.datetime import format_time, get_server_weekday
|
||||
from arknights_mower.utils.digit_reader import DigitReader
|
||||
from arknights_mower.utils.email import send_message
|
||||
from arknights_mower.utils.graph import SceneGraphSolver
|
||||
from arknights_mower.utils.image import cropimg, loadres, thres2
|
||||
from arknights_mower.utils.log import logger
|
||||
from arknights_mower.utils.operators import Operator, Operators
|
||||
from arknights_mower.utils.path import get_path
|
||||
|
@ -1696,357 +1692,18 @@ class BaseSchedulerSolver(SceneGraphSolver, BaseMixin):
|
|||
self.todo_task = True
|
||||
|
||||
def clue_new(self):
|
||||
logger.info("基建:线索")
|
||||
self.scene_graph_navigation(Scene.INFRA_MAIN)
|
||||
self.enter_room("meeting")
|
||||
|
||||
clue_size = (162, 216)
|
||||
clue_top_left = {
|
||||
"daily": (1118, 334),
|
||||
"receive": (1305, 122),
|
||||
"give_away": (30, 208),
|
||||
# 摆放线索界面,线索框的左上角
|
||||
1: (72, 228),
|
||||
2: (374, 334),
|
||||
3: (679, 198),
|
||||
4: (1003, 265),
|
||||
5: (495, 660),
|
||||
6: (805, 573),
|
||||
7: (154, 608),
|
||||
}
|
||||
dot_offset = (168, -8)
|
||||
main_offset = (425, 0)
|
||||
main_time_offset = (443, 257)
|
||||
|
||||
def va(a, b):
|
||||
return a[0] + b[0], a[1] + b[1]
|
||||
|
||||
def tl2p(top_left):
|
||||
return top_left, va(top_left, clue_size)
|
||||
|
||||
def is_orange(dot):
|
||||
orange_dot = (255, 104, 1)
|
||||
return all([abs(dot[i] - orange_dot[i]) < 3 for i in range(3)])
|
||||
|
||||
clue_scope = {}
|
||||
for index, top_left in clue_top_left.items():
|
||||
clue_scope[index] = tl2p(top_left)
|
||||
clue_dots = {}
|
||||
main_dots = {}
|
||||
main_time = {}
|
||||
main_scope = {}
|
||||
for i in range(1, 8):
|
||||
clue_dots[i] = va(clue_top_left[i], dot_offset)
|
||||
main_dots[i] = va(clue_dots[i], main_offset)
|
||||
main_time[i] = va(clue_top_left[i], main_time_offset)
|
||||
main_scope[i] = tl2p(va(clue_top_left[i], main_offset))
|
||||
|
||||
class ClueTaskManager:
|
||||
def __init__(self):
|
||||
# 操作顺序:领取每日线索、接收好友线索、摆线索、送线索、更新线索交流结束时间
|
||||
self.task_list = [
|
||||
"daily",
|
||||
"receive",
|
||||
"place",
|
||||
"give_away",
|
||||
"party_time",
|
||||
]
|
||||
self.task = self.task_list[0]
|
||||
|
||||
def complete(self, task):
|
||||
task = task or self.task
|
||||
if task in self.task_list:
|
||||
self.task_list.remove(task)
|
||||
self.task = self.task_list[0] if self.task_list else None
|
||||
|
||||
tm_thres = 0.6
|
||||
|
||||
def clue_cls(scope):
|
||||
scope_dict = clue_scope if isinstance(scope, str) else main_scope
|
||||
img = cropimg(config.recog.img, scope_dict[scope])
|
||||
for i in range(1, 8):
|
||||
res = loadres(f"clue/{i}")
|
||||
result = cv2.matchTemplate(img, res, cv2.TM_CCOEFF_NORMED)
|
||||
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
||||
if max_val > tm_thres:
|
||||
return i
|
||||
return None
|
||||
|
||||
exit_pos = (1239, 144)
|
||||
|
||||
ctm = ClueTaskManager()
|
||||
|
||||
friend_clue = []
|
||||
|
||||
clue_status = {}
|
||||
|
||||
def place_index():
|
||||
for cl, st in clue_status.items():
|
||||
if st in ["available", "self", "available_self_only"]:
|
||||
return cl, st
|
||||
return None, None
|
||||
|
||||
def detect_unlock():
|
||||
unlock_pos = self.find("clue/button_unlock")
|
||||
if unlock_pos is None:
|
||||
return None
|
||||
color = self.get_color(self.get_pos(unlock_pos))
|
||||
if all(color > [252] * 3):
|
||||
return unlock_pos
|
||||
return None
|
||||
|
||||
while ctm.task:
|
||||
scene = self.scene()
|
||||
|
||||
if scene == Scene.INFRA_DETAILS:
|
||||
if ctm.task == "party_time":
|
||||
if self.find("clue/title_party", scope=((1600, 190), (1880, 260))):
|
||||
self.party_time = self.double_read_time(
|
||||
((1768, 438), (1902, 480))
|
||||
)
|
||||
logger.info(f"线索交流结束时间:{self.party_time}")
|
||||
if not find_next_task(
|
||||
self.tasks,
|
||||
task_type=TaskTypes.CLUE_PARTY,
|
||||
):
|
||||
self.tasks.append(
|
||||
SchedulerTask(
|
||||
time=self.party_time - timedelta(milliseconds=1),
|
||||
task_type=TaskTypes.CLUE_PARTY,
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.party_time = None
|
||||
logger.info("线索交流未开启")
|
||||
ctm.complete("party_time")
|
||||
else:
|
||||
# 点击左下角,关闭进驻信息,进入线索界面
|
||||
self.tap((725, 850))
|
||||
|
||||
elif scene == Scene.INFRA_CONFIDENTIAL:
|
||||
if ctm.task == "daily":
|
||||
# 检查是否领过线索
|
||||
daily_scope = ((1815, 200), (1895, 250))
|
||||
if self.find("clue/badge_new", scope=daily_scope):
|
||||
self.tap((1800, 270))
|
||||
else:
|
||||
ctm.complete("daily")
|
||||
elif ctm.task == "receive":
|
||||
receive_scope = ((1815, 360), (1895, 410))
|
||||
if self.find("clue/badge_new", scope=receive_scope):
|
||||
self.ctap((1800, 430))
|
||||
else:
|
||||
ctm.complete("receive")
|
||||
elif ctm.task == "place":
|
||||
if unlock_pos := detect_unlock():
|
||||
self.tap(unlock_pos)
|
||||
continue
|
||||
for i in range(1, 8):
|
||||
if is_orange(self.get_color(main_dots[i])):
|
||||
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:
|
||||
clue_status[i] = "friend"
|
||||
else:
|
||||
clue_status[i] = "self"
|
||||
else:
|
||||
clue_status[i] = None
|
||||
cl, st = place_index()
|
||||
if st in ["available", "self", "available_self_only"]:
|
||||
self.tap(main_scope[cl])
|
||||
continue
|
||||
else:
|
||||
ctm.complete("place")
|
||||
elif ctm.task == "give_away":
|
||||
self.ctap((1799, 578))
|
||||
elif ctm.task == "party_time":
|
||||
self.back()
|
||||
|
||||
elif scene == Scene.CLUE_DAILY:
|
||||
if not self.find(
|
||||
"clue/icon_notification", scope=((1400, 0), (1920, 400))
|
||||
) and (clue := clue_cls("daily")):
|
||||
logger.info(f"领取今日线索({clue}号)")
|
||||
self.tap_element("clue/button_get")
|
||||
ctm.complete("daily")
|
||||
else:
|
||||
# 今日线索已领取,点X退出
|
||||
self.tap((1484, 152))
|
||||
|
||||
elif scene == Scene.CLUE_RECEIVE:
|
||||
if self.find(
|
||||
"infra_trust_complete", scope=((1230, 0), (1920, 1080)), score=0.1
|
||||
):
|
||||
self.sleep()
|
||||
continue
|
||||
if clue := clue_cls("receive"):
|
||||
name_scope = ((1580, 220), (1880, 255))
|
||||
name_img = cropimg(config.recog.gray, name_scope)
|
||||
name_img = cv2.copyMakeBorder(
|
||||
name_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE
|
||||
self.party_time = ClueSolver().run()
|
||||
if self.party_time:
|
||||
if not find_next_task(
|
||||
self.tasks,
|
||||
task_type=TaskTypes.CLUE_PARTY,
|
||||
):
|
||||
self.tasks.append(
|
||||
SchedulerTask(
|
||||
time=self.party_time - timedelta(milliseconds=1),
|
||||
task_type=TaskTypes.CLUE_PARTY,
|
||||
)
|
||||
name = rapidocr.engine(
|
||||
name_img,
|
||||
use_det=True,
|
||||
use_cls=False,
|
||||
use_rec=True,
|
||||
)[0][0][1]
|
||||
name = name.strip() if name else "好友"
|
||||
logger.info(f"接收{name}的{clue}号线索")
|
||||
self.tap(name_scope)
|
||||
else:
|
||||
ctm.complete("receive")
|
||||
self.tap(exit_pos)
|
||||
|
||||
elif scene == Scene.CLUE_PLACE:
|
||||
cl, st = place_index()
|
||||
if cl is None:
|
||||
if unlock_pos := detect_unlock():
|
||||
self.tap(unlock_pos)
|
||||
else:
|
||||
ctm.complete("place")
|
||||
self.tap(exit_pos)
|
||||
continue
|
||||
if self.get_color((1328 + 77 * cl, 114))[0] < 150:
|
||||
# 右上角 1-7
|
||||
self.tap(clue_scope[cl])
|
||||
continue
|
||||
receive = st in ["available", "self"]
|
||||
filter_receive = (1900, 45)
|
||||
filter_self = (1610, 70)
|
||||
filter_pos = filter_receive if receive else filter_self
|
||||
if not all(self.get_color(filter_pos) > [252] * 3):
|
||||
self.tap(filter_pos)
|
||||
continue
|
||||
clue_pos = ((1305, 208), (1305, 503), (1305, 797))
|
||||
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_img = cv2.copyMakeBorder(
|
||||
name_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE
|
||||
)
|
||||
name = rapidocr.engine(
|
||||
name_img,
|
||||
use_det=True,
|
||||
use_cls=False,
|
||||
use_rec=True,
|
||||
)[0][0][1]
|
||||
if name:
|
||||
name = name.strip()
|
||||
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 = cv2.copyMakeBorder(
|
||||
time_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE
|
||||
)
|
||||
time = rapidocr.engine(
|
||||
time_img,
|
||||
use_det=True,
|
||||
use_cls=False,
|
||||
use_rec=True,
|
||||
)[0][0][1]
|
||||
if time:
|
||||
time = time.strip()
|
||||
else:
|
||||
time = None
|
||||
clue_list.append(
|
||||
{"name": name, "time": time, "scope": tl2p(cp)}
|
||||
)
|
||||
else:
|
||||
break
|
||||
if clue_list:
|
||||
list_name = "接收库" if receive else "自有库"
|
||||
logger.info(f"{cl}号线索{list_name}:{clue_list}")
|
||||
selected = None
|
||||
for c in clue_list:
|
||||
if c["time"]:
|
||||
selected = c
|
||||
break
|
||||
selected = selected or clue_list[0]
|
||||
self.tap(selected["scope"])
|
||||
if clue_status[cl] == "available":
|
||||
clue_status[cl] = "friend"
|
||||
elif clue_status[cl] == "available_self_only":
|
||||
clue_status[cl] = "self_only"
|
||||
elif clue_status[cl] == "self":
|
||||
clue_status[cl] = "friend"
|
||||
else:
|
||||
clue_status[cl] = None
|
||||
else:
|
||||
if clue_status[cl] == "available":
|
||||
clue_status[cl] = "available_self_only"
|
||||
elif clue_status[cl] == "available_self_only":
|
||||
clue_status[cl] = None
|
||||
elif clue_status[cl] == "self":
|
||||
clue_status[cl] = "self_only"
|
||||
else:
|
||||
clue_status[cl] = None
|
||||
|
||||
elif scene == Scene.CLUE_GIVE_AWAY:
|
||||
give_away_true = self.leifeng_mode or (
|
||||
not self.leifeng_mode and self.clue_count > self.clue_count_limit
|
||||
)
|
||||
if (c := clue_cls("give_away")) and give_away_true:
|
||||
if not friend_clue:
|
||||
if self.find(
|
||||
"clue/icon_notification", scope=((1400, 0), (1920, 400))
|
||||
):
|
||||
self.sleep()
|
||||
continue
|
||||
for i in range(4):
|
||||
label_scope = ((1450, 228 + i * 222), (1580, 278 + i * 222))
|
||||
if not self.find("clue/label_give_away", scope=label_scope):
|
||||
break
|
||||
name_top_left = (870, 127 + 222 * i)
|
||||
name_scope = (name_top_left, va(name_top_left, (383, 62)))
|
||||
name = rapidocr.engine(
|
||||
cropimg(config.recog.gray, name_scope),
|
||||
use_det=True,
|
||||
use_cls=False,
|
||||
use_rec=True,
|
||||
)[0][0][1]
|
||||
if name:
|
||||
name = name.strip()
|
||||
data = {"name": name}
|
||||
for j in range(1, 8):
|
||||
pos = (1230 + j * 64, 142 + i * 222)
|
||||
data[j] = self.get_color(pos)[0] < 137
|
||||
friend_clue.append(data)
|
||||
logger.debug(friend_clue)
|
||||
friend = None
|
||||
for idx, fc in enumerate(friend_clue):
|
||||
if not fc[c]:
|
||||
friend = idx
|
||||
fc[c] = True
|
||||
break
|
||||
friend = friend or 0
|
||||
logger.info(f"给{friend_clue[friend]['name']}送一张线索{c}")
|
||||
self.tap(clue_scope["give_away"])
|
||||
self.clue_count -= 1
|
||||
self.tap((1790, 200 + friend * 222))
|
||||
else:
|
||||
ctm.complete("give_away")
|
||||
self.tap((1868, 54))
|
||||
|
||||
elif scene == Scene.CLUE_SUMMARY:
|
||||
self.back()
|
||||
|
||||
else:
|
||||
self.scene_graph_navigation(Scene.INFRA_MAIN)
|
||||
self.enter_room("meeting")
|
||||
|
||||
shop_solver = CreditShop()
|
||||
shop_solver.run()
|
||||
self.scene_graph_navigation(Scene.INFRA_MAIN)
|
||||
|
||||
def adjust_order_time(self, accelerate, room):
|
||||
|
@ -2590,11 +2247,9 @@ class BaseSchedulerSolver(SceneGraphSolver, BaseMixin):
|
|||
def turn_on_room_detail(self, room):
|
||||
EnterRoomSolver().run(room)
|
||||
|
||||
def get_agent_from_room(self, room, read_time_index=None):
|
||||
def get_agent_from_room(self, room: str, read_time_index=None):
|
||||
if read_time_index is None:
|
||||
read_time_index = []
|
||||
if room == "meeting" and not config.conf.leifeng_mode:
|
||||
self.clue_count = GetClueCountSolver().run()
|
||||
if room.startswith("dorm"):
|
||||
read_time_index = [
|
||||
i
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#import lzma
|
||||
#import pickle
|
||||
# import lzma
|
||||
# import pickle
|
||||
|
||||
import cv2
|
||||
|
||||
#from matplotlib import pyplot as plt
|
||||
# from matplotlib import pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from arknights_mower.models import secret_front
|
||||
|
@ -17,7 +17,7 @@ from arknights_mower.utils.graph import SceneGraphSolver
|
|||
from arknights_mower.utils.image import cropimg
|
||||
from arknights_mower.utils.log import logger
|
||||
|
||||
#from arknights_mower import __rootdir__
|
||||
# from arknights_mower import __rootdir__
|
||||
from arknights_mower.utils.scene import Scene
|
||||
from arknights_mower.utils.vector import sa
|
||||
|
||||
|
@ -217,8 +217,8 @@ class BaseChoose(SceneGraphSolver):
|
|||
y21,
|
||||
) = rect2[0]
|
||||
x22, y22 = rect2[1]
|
||||
#a = (x22 + x21) / 2
|
||||
#b = (y22 + y21) / 2
|
||||
# a = (x22 + x21) / 2
|
||||
# b = (y22 + y21) / 2
|
||||
# 如果rect2的左上角在rect1内,并且rect2完全在rect1的边界内,则认为rect2被rect1包含
|
||||
return x11 < (x22 + x21) / 2 < x12 and y11 < (y22 + y21) / 2 < y12
|
||||
|
||||
|
|
22
arknights_mower/solvers/infra/clue/__init__.py
Normal file
22
arknights_mower/solvers/infra/clue/__init__.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
from arknights_mower.solvers.shop import CreditShop
|
||||
from arknights_mower.utils.log import logger
|
||||
|
||||
from .daily import DailySolver
|
||||
from .get_clue_count import GetClueCountSolver
|
||||
from .give_away import GiveAwaySolver
|
||||
from .party_time import PartyTimeSolver
|
||||
from .place import PlaceSolver
|
||||
from .receive import ReceiveSolver
|
||||
|
||||
|
||||
class ClueSolver:
|
||||
def run(self) -> None:
|
||||
logger.info("基建:线索")
|
||||
clue_count = GetClueCountSolver().run()
|
||||
DailySolver().run()
|
||||
ReceiveSolver().run()
|
||||
PlaceSolver().run()
|
||||
GiveAwaySolver().run(clue_count)
|
||||
party_time = PartyTimeSolver().run()
|
||||
CreditShop().run()
|
||||
return party_time
|
34
arknights_mower/solvers/infra/clue/daily.py
Normal file
34
arknights_mower/solvers/infra/clue/daily.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
from arknights_mower.solvers.infra.enter_room import EnterRoomSolver
|
||||
from arknights_mower.utils.graph import SceneGraphSolver
|
||||
from arknights_mower.utils.log import logger
|
||||
from arknights_mower.utils.recognize import Scene
|
||||
|
||||
from .utils import clue_cls
|
||||
|
||||
|
||||
class DailySolver(SceneGraphSolver):
|
||||
def run(self) -> None:
|
||||
super().run()
|
||||
|
||||
def transition(self) -> bool:
|
||||
if (scene := self.scene()) == Scene.INFRA_DETAILS:
|
||||
self.tap((725, 850))
|
||||
elif scene == Scene.INFRA_CONFIDENTIAL:
|
||||
# 检查是否领过线索
|
||||
daily_scope = ((1815, 200), (1895, 250))
|
||||
if self.find("clue/badge_new", scope=daily_scope):
|
||||
self.tap((1800, 270))
|
||||
else:
|
||||
return True
|
||||
elif scene == Scene.CLUE_DAILY:
|
||||
if not self.find(
|
||||
"clue/icon_notification", scope=((1400, 0), (1920, 400))
|
||||
) and (clue := clue_cls("daily")):
|
||||
logger.info(f"领取今日线索({clue}号)")
|
||||
self.tap_element("clue/button_get")
|
||||
else:
|
||||
# 今日线索已领取,点X退出
|
||||
self.tap((1484, 152))
|
||||
return True
|
||||
else:
|
||||
EnterRoomSolver().run("meeting", detail=False)
|
70
arknights_mower/solvers/infra/clue/give_away.py
Normal file
70
arknights_mower/solvers/infra/clue/give_away.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
from arknights_mower.solvers.infra.enter_room import EnterRoomSolver
|
||||
from arknights_mower.utils import config, rapidocr
|
||||
from arknights_mower.utils.graph import SceneGraphSolver
|
||||
from arknights_mower.utils.image import cropimg
|
||||
from arknights_mower.utils.log import logger
|
||||
from arknights_mower.utils.recognize import Scene
|
||||
from arknights_mower.utils.vector import va
|
||||
|
||||
from .utils import clue_cls, clue_scope
|
||||
|
||||
friend_clue = []
|
||||
|
||||
|
||||
class GiveAwaySolver(SceneGraphSolver):
|
||||
def run(self, clue_count) -> None:
|
||||
self.clue_count = clue_count
|
||||
super().run()
|
||||
|
||||
def transition(self) -> bool:
|
||||
if (scene := self.scene()) == Scene.INFRA_DETAILS:
|
||||
self.tap((725, 850))
|
||||
elif scene == Scene.INFRA_CONFIDENTIAL:
|
||||
self.ctap((1799, 578))
|
||||
elif scene == Scene.CLUE_GIVE_AWAY:
|
||||
give_away_true = config.conf.leifeng_mode or (
|
||||
not config.conf.leifeng_mode and self.clue_count > 9
|
||||
)
|
||||
if (c := clue_cls("give_away")) and give_away_true:
|
||||
if not friend_clue:
|
||||
if self.find(
|
||||
"clue/icon_notification", scope=((1400, 0), (1920, 400))
|
||||
):
|
||||
self.sleep()
|
||||
return
|
||||
for i in range(4):
|
||||
label_scope = ((1450, 228 + i * 222), (1580, 278 + i * 222))
|
||||
if not self.find("clue/label_give_away", scope=label_scope):
|
||||
break
|
||||
name_top_left = (870, 127 + 222 * i)
|
||||
name_scope = (name_top_left, va(name_top_left, (383, 62)))
|
||||
name = rapidocr.engine(
|
||||
cropimg(config.recog.gray, name_scope),
|
||||
use_det=True,
|
||||
use_cls=False,
|
||||
use_rec=True,
|
||||
)[0][0][1]
|
||||
if name:
|
||||
name = name.strip()
|
||||
data = {"name": name}
|
||||
for j in range(1, 8):
|
||||
pos = (1230 + j * 64, 142 + i * 222)
|
||||
data[j] = self.get_color(pos)[0] < 137
|
||||
friend_clue.append(data)
|
||||
logger.debug(friend_clue)
|
||||
friend = None
|
||||
for idx, fc in enumerate(friend_clue):
|
||||
if not fc[c]:
|
||||
friend = idx
|
||||
fc[c] = True
|
||||
break
|
||||
friend = friend or 0
|
||||
logger.info(f"给{friend_clue[friend]['name']}送一张线索{c}")
|
||||
self.tap(clue_scope["give_away"])
|
||||
self.clue_count -= 1
|
||||
self.tap((1790, 200 + friend * 222))
|
||||
else:
|
||||
self.tap((1868, 54))
|
||||
return True
|
||||
else:
|
||||
EnterRoomSolver().run("meeting", detail=False)
|
25
arknights_mower/solvers/infra/clue/party_time.py
Normal file
25
arknights_mower/solvers/infra/clue/party_time.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from arknights_mower.solvers.infra.base_mixin import BaseMixin
|
||||
from arknights_mower.solvers.infra.enter_room import EnterRoomSolver
|
||||
from arknights_mower.utils.graph import SceneGraphSolver
|
||||
from arknights_mower.utils.log import logger
|
||||
from arknights_mower.utils.recognize import Scene
|
||||
|
||||
|
||||
class PartyTimeSolver(SceneGraphSolver, BaseMixin):
|
||||
def run(self):
|
||||
super().run()
|
||||
return self.party_time
|
||||
|
||||
def transition(self) -> bool:
|
||||
if self.scene() == Scene.INFRA_DETAILS:
|
||||
if self.find("clue/title_party", scope=((1600, 190), (1880, 260))):
|
||||
self.party_time = self.double_read_time(((1768, 438), (1902, 480)))
|
||||
logger.info(f"线索交流结束时间:{self.party_time}")
|
||||
return True
|
||||
else:
|
||||
self.party_time = None
|
||||
logger.info("线索交流未开启")
|
||||
return True
|
||||
|
||||
else:
|
||||
EnterRoomSolver().run("meeting", detail=False)
|
156
arknights_mower/solvers/infra/clue/place.py
Normal file
156
arknights_mower/solvers/infra/clue/place.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
import cv2
|
||||
|
||||
from arknights_mower.solvers.infra.enter_room import EnterRoomSolver
|
||||
from arknights_mower.utils import config, rapidocr
|
||||
from arknights_mower.utils.graph import SceneGraphSolver
|
||||
from arknights_mower.utils.image import cropimg, loadres, thres2
|
||||
from arknights_mower.utils.log import logger
|
||||
from arknights_mower.utils.recognize import Scene
|
||||
from arknights_mower.utils.vector import va
|
||||
|
||||
from .utils import (
|
||||
clue_cls,
|
||||
clue_scope,
|
||||
exit_pos,
|
||||
is_orange,
|
||||
main_dots,
|
||||
main_scope,
|
||||
main_time,
|
||||
tl2p,
|
||||
tm_thres,
|
||||
)
|
||||
|
||||
clue_status = {}
|
||||
clue_pos = ((1305, 208), (1305, 503), (1305, 797))
|
||||
clue_list = []
|
||||
filter_receive = (1900, 45)
|
||||
filter_self = (1610, 70)
|
||||
|
||||
|
||||
def place_index():
|
||||
for cl, st in clue_status.items():
|
||||
if st in ["available", "self", "available_self_only"]:
|
||||
return cl, st
|
||||
return None, None
|
||||
|
||||
|
||||
class PlaceSolver(SceneGraphSolver):
|
||||
def run(self) -> None:
|
||||
super().run()
|
||||
|
||||
def detect_unlock(self):
|
||||
unlock_pos = self.find("clue/button_unlock")
|
||||
if unlock_pos is None:
|
||||
return None
|
||||
color = self.get_color(self.get_pos(unlock_pos))
|
||||
if all(color > [252] * 3):
|
||||
return unlock_pos
|
||||
return None
|
||||
|
||||
def transition(self) -> bool:
|
||||
if (scene := self.scene()) == Scene.INFRA_DETAILS:
|
||||
self.tap((725, 850))
|
||||
elif scene == Scene.INFRA_CONFIDENTIAL:
|
||||
if unlock_pos := self.detect_unlock():
|
||||
self.tap(unlock_pos)
|
||||
for i in range(1, 8):
|
||||
if is_orange(self.get_color(main_dots[i])):
|
||||
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:
|
||||
clue_status[i] = "friend"
|
||||
else:
|
||||
clue_status[i] = "self"
|
||||
else:
|
||||
clue_status[i] = None
|
||||
cl, st = place_index()
|
||||
if st in ["available", "self", "available_self_only"]:
|
||||
self.tap(main_scope[cl])
|
||||
else:
|
||||
return True
|
||||
elif scene == Scene.CLUE_PLACE:
|
||||
cl, st = place_index()
|
||||
receive = st in ["available", "self"]
|
||||
filter_pos = filter_receive if receive else filter_self
|
||||
if cl is None:
|
||||
if unlock_pos := self.detect_unlock():
|
||||
self.tap(unlock_pos)
|
||||
else:
|
||||
self.tap(exit_pos)
|
||||
return True
|
||||
elif self.get_color((1328 + 77 * cl, 114))[0] < 150:
|
||||
# 右上角 1-7
|
||||
self.tap(clue_scope[cl])
|
||||
elif not all(self.get_color(filter_pos) > [252] * 3):
|
||||
self.tap(filter_pos)
|
||||
return
|
||||
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_img = cv2.copyMakeBorder(
|
||||
name_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE
|
||||
)
|
||||
name = rapidocr.engine(
|
||||
name_img,
|
||||
use_det=True,
|
||||
use_cls=False,
|
||||
use_rec=True,
|
||||
)[0][0][1]
|
||||
if name:
|
||||
name = name.strip()
|
||||
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 = cv2.copyMakeBorder(
|
||||
time_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE
|
||||
)
|
||||
time = rapidocr.engine(
|
||||
time_img,
|
||||
use_det=True,
|
||||
use_cls=False,
|
||||
use_rec=True,
|
||||
)[0][0][1]
|
||||
if time:
|
||||
time = time.strip()
|
||||
else:
|
||||
time = None
|
||||
clue_list.append({"name": name, "time": time, "scope": tl2p(cp)})
|
||||
else:
|
||||
break
|
||||
if clue_list:
|
||||
list_name = "接收库" if receive else "自有库"
|
||||
logger.info(f"{cl}号线索{list_name}:{clue_list}")
|
||||
selected = None
|
||||
for c in clue_list:
|
||||
if c["time"]:
|
||||
selected = c
|
||||
break
|
||||
selected = selected or clue_list[0]
|
||||
self.tap(selected["scope"])
|
||||
if clue_status[cl] == "available":
|
||||
clue_status[cl] = "friend"
|
||||
elif clue_status[cl] == "available_self_only":
|
||||
clue_status[cl] = "self_only"
|
||||
elif clue_status[cl] == "self":
|
||||
clue_status[cl] = "friend"
|
||||
else:
|
||||
clue_status[cl] = None
|
||||
else:
|
||||
if clue_status[cl] == "available":
|
||||
clue_status[cl] = "available_self_only"
|
||||
elif clue_status[cl] == "available_self_only":
|
||||
clue_status[cl] = None
|
||||
elif clue_status[cl] == "self":
|
||||
clue_status[cl] = "self_only"
|
||||
else:
|
||||
clue_status[cl] = None
|
||||
|
||||
else:
|
||||
EnterRoomSolver().run("meeting", detail=False)
|
50
arknights_mower/solvers/infra/clue/receive.py
Normal file
50
arknights_mower/solvers/infra/clue/receive.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
import cv2
|
||||
|
||||
from arknights_mower.solvers.infra.enter_room import EnterRoomSolver
|
||||
from arknights_mower.utils import config, rapidocr
|
||||
from arknights_mower.utils.graph import SceneGraphSolver
|
||||
from arknights_mower.utils.image import cropimg
|
||||
from arknights_mower.utils.log import logger
|
||||
from arknights_mower.utils.recognize import Scene
|
||||
|
||||
from .utils import clue_cls, exit_pos
|
||||
|
||||
|
||||
class ReceiveSolver(SceneGraphSolver):
|
||||
def run(self) -> None:
|
||||
super().run()
|
||||
|
||||
def transition(self) -> bool:
|
||||
if (scene := self.scene()) == Scene.INFRA_DETAILS:
|
||||
self.tap((725, 850))
|
||||
elif scene == Scene.INFRA_CONFIDENTIAL:
|
||||
receive_scope = ((1815, 360), (1895, 410))
|
||||
if self.find("clue/badge_new", scope=receive_scope):
|
||||
self.ctap((1800, 430))
|
||||
else:
|
||||
return True
|
||||
elif scene == Scene.CLUE_RECEIVE:
|
||||
if self.find(
|
||||
"infra_trust_complete", scope=((1230, 0), (1920, 1080)), score=0.1
|
||||
):
|
||||
self.sleep()
|
||||
elif clue := clue_cls("receive"):
|
||||
name_scope = ((1580, 220), (1880, 255))
|
||||
name_img = cropimg(config.recog.gray, name_scope)
|
||||
name_img = cv2.copyMakeBorder(
|
||||
name_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE
|
||||
)
|
||||
name = rapidocr.engine(
|
||||
name_img,
|
||||
use_det=True,
|
||||
use_cls=False,
|
||||
use_rec=True,
|
||||
)[0][0][1]
|
||||
name = name.strip() if name else "好友"
|
||||
logger.info(f"接收{name}的{clue}号线索")
|
||||
self.tap(name_scope)
|
||||
else:
|
||||
self.tap(exit_pos)
|
||||
return True
|
||||
else:
|
||||
EnterRoomSolver().run("meeting", detail=False)
|
60
arknights_mower/solvers/infra/clue/utils.py
Normal file
60
arknights_mower/solvers/infra/clue/utils.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
import cv2
|
||||
|
||||
from arknights_mower.utils import config
|
||||
from arknights_mower.utils.image import cropimg, loadres
|
||||
from arknights_mower.utils.vector import va
|
||||
|
||||
clue_size = (162, 216)
|
||||
clue_top_left = {
|
||||
"daily": (1118, 334),
|
||||
"receive": (1305, 122),
|
||||
"give_away": (30, 208),
|
||||
# 摆放线索界面,线索框的左上角
|
||||
1: (72, 228),
|
||||
2: (374, 334),
|
||||
3: (679, 198),
|
||||
4: (1003, 265),
|
||||
5: (495, 660),
|
||||
6: (805, 573),
|
||||
7: (154, 608),
|
||||
}
|
||||
dot_offset = (168, -8)
|
||||
main_offset = (425, 0)
|
||||
main_time_offset = (443, 257)
|
||||
|
||||
|
||||
def tl2p(top_left):
|
||||
return top_left, va(top_left, clue_size)
|
||||
|
||||
|
||||
def is_orange(dot):
|
||||
orange_dot = (255, 104, 1)
|
||||
return all([abs(dot[i] - orange_dot[i]) < 3 for i in range(3)])
|
||||
|
||||
|
||||
clue_scope = {}
|
||||
for index, top_left in clue_top_left.items():
|
||||
clue_scope[index] = tl2p(top_left)
|
||||
clue_dots = {}
|
||||
main_dots = {}
|
||||
main_time = {}
|
||||
main_scope = {}
|
||||
for i in range(1, 8):
|
||||
clue_dots[i] = va(clue_top_left[i], dot_offset)
|
||||
main_dots[i] = va(clue_dots[i], main_offset)
|
||||
main_time[i] = va(clue_top_left[i], main_time_offset)
|
||||
main_scope[i] = tl2p(va(clue_top_left[i], main_offset))
|
||||
tm_thres = 0.6
|
||||
exit_pos = (1239, 144)
|
||||
|
||||
|
||||
def clue_cls(scope):
|
||||
scope_dict = clue_scope if isinstance(scope, str) else main_scope
|
||||
img = cropimg(config.recog.img, scope_dict[scope])
|
||||
for i in range(1, 8):
|
||||
res = loadres(f"clue/{i}")
|
||||
result = cv2.matchTemplate(img, res, cv2.TM_CCOEFF_NORMED)
|
||||
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
||||
if max_val > tm_thres:
|
||||
return i
|
||||
return None
|
|
@ -1,6 +1,6 @@
|
|||
import cv2
|
||||
|
||||
#from matplotlib import pyplot as plt
|
||||
# from matplotlib import pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from arknights_mower.utils import config
|
||||
|
|
Loading…
Reference in a new issue