Compare commits

...

2 commits

Author SHA1 Message Date
5e9c7e7f94 Merge branch 'main' of https://git.zhaozuohong.vip/mower-ng/mower-ng
All checks were successful
ci/woodpecker/push/check_format Pipeline was successful
2024-11-01 19:50:50 +08:00
1a10f0954c 替换基建选人 2024-11-01 19:50:38 +08:00
9 changed files with 202 additions and 308 deletions

BIN
mower/resources/choose_agent/riic/work_state.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/riic/work_state_blue.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

View file

@ -1,3 +1,4 @@
from mower.utils import config
from mower.utils.graph.utils import SceneGraphSolver
from mower.utils.log import logger
from mower.utils.scene import Scene
@ -47,14 +48,13 @@ class BattleTagChoose(SceneGraphSolver):
arrow = self.find("choose_agent/profession/choose_arrow")
if arrow is None:
self.ctap(pos, interval=0.1, max_seconds=0.5)
# self.tap(pos, interval=0.1)
self.ctap(pos, interval=config.screenshot_avg / 1000)
return
else:
if pos[0][1] > arrow[0][1] and pos[1][1] < arrow[1][1]:
return True
else:
self.ctap(pos, interval=0.1, max_seconds=0.5)
self.ctap(pos, interval=config.screenshot_avg / 1000)
return
def transition(self) -> bool:

View file

@ -1,43 +1,40 @@
from copy import deepcopy
from datetime import datetime
import cv2
import numpy as np
from mower.models import Digtal
from mower.solvers.infra.base_mixin import BaseMixin
from mower.data import agent_list
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.solvers.infra.filter import RIIC_Filter
from mower.solvers.infra.riic_tag_choose import RIICTagChoose
from mower.utils import config
from mower.utils.character_recognize import (
match_portrait,
operator_room_select,
)
from mower.utils.graph import SceneGraphSolver
from mower.utils.character_recognize import operator_room_select
from mower.utils.graph.utils import SceneGraphSolver
from mower.utils.image import cropimg
from mower.utils.log import logger
from mower.utils.scene import Scene
from mower.utils.vector import sa, va
class RIIC_ChooseSolver(SceneGraphSolver, BaseMixin):
def run(self, room: str, agent_list: list, wait_time: float = 0) -> bool:
self.room = room
self.agent_list = agent_list
self.agent_list_copy = deepcopy(self.agent_list)
self.wait_time = wait_time
self.wait_choose = []
self.choosed = []
class RIIC_ChooseSolver(SceneGraphSolver):
def run(self, room: str, agents: list, wait_time: float = 0) -> bool:
if agents == []:
raise ValueError("干员列表为空")
self.agents = agents
self.agents_copy = deepcopy(agents)
self.cleard = False
self.filter = RIIC_Filter()
# self.first_filter = True
self.in_adjust = False
self.filter = False
self.agent = None
self.choosed = []
self.final_check = False
self.check = []
self.lack = []
self.tmp_data = None
self.tmp_checked = None
self.tag = "ALL"
self.wait_time = wait_time
self.room = room
self.start_time = datetime.now()
self.success = False
self.check_room()
try:
return super().run()
except ValueError as e:
@ -45,194 +42,36 @@ class RIIC_ChooseSolver(SceneGraphSolver, BaseMixin):
return False
def riic_agent_choose(self):
if self.cleard is False:
if self.find("choose_agent/riic_empty"):
self.cleard = True
else:
self.filter.run(未进驻=None, 技能=False)
self.filter.run(未进驻=False)
self.tap_element("choose_agent/clear", interval=0.1)
return
if pos := self.find("choose_agent/open_profession"):
self.tap(pos, interval=0.1)
if self.agent is None:
if len(self.agents_copy) > 0:
self.agent = self.agents_copy.pop(0)
tag = agent_list[self.agent]["profession"]
RIICTagChoose().run(tag)
else:
return True
agents = dict(operator_room_select(config.recog.img))
if self.in_adjust:
if self.tmp_checked is None:
for name in agents.keys():
if self.is_choosed(agents[name]):
self.tap_element("choose_agent/clear", interval=0.1)
return
self.tmp_checked = self.agent_list[0]
else:
if self.is_choosed(agents[self.tmp_checked]):
if len(self.agent_list) > 1:
del self.agent_list[0]
self.tmp_checked = self.agent_list[0]
else:
return True
else:
self.ctap(agents[self.tmp_checked], 1, config.screenshot_avg / 1000)
agent_name = self.agent
if agent_name in agents.keys():
if agents[agent_name][0][0] > 1650:
self.swipe_noinertia((1000, 540), (-500, 0))
return
if self.is_choosed(agents[agent_name]):
logger.debug(f"干员选好:{self.agent}")
self.choosed.append(agent_name)
self.agent = None
return
logger.info(f"干员选择:{self.agent}")
self.ctap(agents[agent_name], 1, config.screenshot_avg / 1000)
return
if list(agents.keys())[-1] == self.tmp_data or list(agents.keys())[-1] is None:
raise ValueError(f"选择干员: {self.agent}失败 滑动次数过多")
if len(self.wait_choose) == 0:
if len(self.agent_list_copy) > 0:
intersection = list(set(agents.keys()) & set(self.agent_list_copy))
if len(intersection) > 0:
self.wait_choose = intersection
self.agent_list_copy = [
item
for item in self.agent_list_copy
if item not in self.wait_choose
]
else:
if (
list(agents.keys())[-1] == self.tmp_data
or list(agents.keys())[-1] is None
):
raise ValueError(
f"干员选择失败:{set(self.agent_list) - set(self.choosed)}"
)
self.swipe_noinertia((1000, 540), (-1900, 0))
self.tmp_data = list(agents.keys())[-1]
return
else:
try:
if set(self.choosed) == set(self.agent_list):
for i in self.choosed:
if agents[i][0][0] > 1700:
self.filter.run(未进驻=None)
return
self.in_adjust = True
else:
raise ValueError(
f"干员选择失败:{set(self.agent_list) - set(self.choosed)}"
)
except KeyError:
self.filter.run(未进驻=None)
return
else:
name = self.wait_choose[0]
if self.is_choosed(agents[name]):
self.choosed.append(name)
del self.wait_choose[0]
return
logger.info(f"干员选择:{name}")
self.ctap(agents[name], 1, config.screenshot_avg / 1000)
return
def check_agent_order(self):
choosed_agents = self.get_agent_pos(no_number=False)
logger.info(f"{choosed_agents}{self.agent_list}")
if len(choosed_agents) < len(self.agent_list):
if self.in_adjust is False:
self.filter.run(未进驻=None)
return False
agents = dict(operator_room_select(config.recog.img))
for i in self.agent_list:
self.tap(va(agents[i][0], (100, 0)), interval=0)
# 盲点结束等一下
self.sleep()
return False
elif len(choosed_agents) == len(self.agent_list):
for i in self.agent_list:
if choosed_agents[i][0] != (self.agent_list.index(i)) + 1:
self.tap_element("choose_agent/clear", interval=0.5)
self.in_adjust = True
return False
return True
def check_with_number(self, img, height=60):
_, im_w, _ = img.shape
img = cropimg(img, ((0, 0), (im_w, 100)))
kernel = np.ones((9, 9), np.uint8)
img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
img = cv2.inRange(img, (0, 0, 200), (225, 15, 255))
img = cv2.erode(img, kernel)
img = cv2.dilate(img, kernel)
contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rect = [cv2.boundingRect(c) for c in contours]
rect.sort(key=lambda c: c[0])
value = 0
for x, y, w, h in rect:
digit = cropimg(img, ((x, y), (x + w, y + h)))
digit = cv2.copyMakeBorder(
digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
)
if digit.shape[0] < 65 or digit.shape[1] < 40:
continue
score = []
kernel = np.ones((2, 2), np.uint8)
for i in range(10):
im = Digtal().secret_front[i]
default_height = 25
if height and height != default_height:
scale = default_height / height
digit_1 = cv2.resize(digit, None, None, scale, scale)
im = cv2.dilate(im, kernel=kernel)
result = cv2.matchTemplate(digit_1, im, cv2.TM_SQDIFF_NORMED)
min_val, _, _, _ = cv2.minMaxLoc(result)
score.append(min_val)
value = value * 10 + score.index(min(score))
return value
def get_agent_pos(self, no_number=True):
res = {}
img = cv2.cvtColor(config.recog.img, cv2.COLOR_RGB2HSV)
img = cv2.inRange(img, (90, 200, 120), (200, 255, 255))
contours, _ = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
segments = []
for contour in contours:
if cv2.contourArea(contour) < 40000 or cv2.contourArea(contour) > 100000:
continue
x, y, w, h = cv2.boundingRect(contour)
segments.append(((x, y + 100), (x + w, y + h - 200)))
logger.debug(f"segments:{segments}")
opear = match_portrait(config.recog.gray, segment=segments)
if no_number:
return list(dict(opear).keys())
if len(self.agent_list) > 1:
for i in opear:
res[i[0]] = [
self.check_with_number(
cropimg(config.recog.img, sa([i[1][0], i[1][1]], (0, -100)))
),
i[1],
]
else:
for i in opear:
res[i[0]] = i[1]
return res
def check_wait_time(self) -> float:
return max(
self.wait_time - (datetime.now() - self.start_time).total_seconds(), 0
)
def check_room(self):
if (
(scene := self.scene()) == Scene.INFRA_DETAILS
) and not EnterRoomSolver().detect_room() == self.room:
EnterRoomSolver().run(self.room)
elif (
scene in [Scene.ORDER_LIST, Scene.FACTORY_ROOMS]
and not self.detect_room_inside() == self.room
):
EnterRoomSolver().run(self.room)
self.swipe_noinertia((1000, 540), (-1900, 0))
self.tmp_data = list(agents.keys())[-1]
def is_choosed(self, scope) -> bool:
if (
@ -248,8 +87,34 @@ class RIIC_ChooseSolver(SceneGraphSolver, BaseMixin):
return True
return False
def check_wait_time(self) -> float:
return max(
self.wait_time - (datetime.now() - self.start_time).total_seconds(), 0
)
def check_room(self):
if (
(scene := self.scene()) == Scene.INFRA_DETAILS
) and not EnterRoomSolver().detect_room() == self.room:
logger.info(self.room)
EnterRoomSolver().run(self.room)
elif (
scene in [Scene.ORDER_LIST, Scene.FACTORY_ROOMS]
and not self.detect_room_inside() == self.room
):
EnterRoomSolver().run(self.room)
def transition(self) -> bool:
if (scene := self.scene()) == Scene.RIIC_OPERATOR_SELECT:
if self.filter is False:
RIIC_Filter().run("技能", ascending=False)
self.filter = True
if self.cleard is False:
if self.find("choose_agent/riic_empty") or len(self.agents) == 1:
self.cleard = True
else:
self.tap_element("choose_agent/clear", interval=0.1)
return
if self.riic_agent_choose():
self.sleep(self.check_wait_time())
self.tap_element("confirm_blue")

View file

@ -8,111 +8,28 @@ from mower.utils.scene import Scene
class RIIC_Filter(SceneGraphSolver):
def __init__(self) -> None:
self.labels = [
"未进驻",
"产出设施",
"功能设施",
"自定义设施",
"控制中枢",
"生产类后勤",
"功能类后勤",
"恢复类后勤",
]
def run(self, tag: str, ascending: bool = True) -> None:
self.orders = [
"工作状态",
"技能",
"心情",
"信赖值",
]
label_x = (560, 815, 1070, 1330)
label_y = (540, 645)
if tag not in self.orders:
raise KeyError
label_pos = []
for y in label_y:
for x in label_x:
label_pos.append((x, y))
self.label_pos_map = dict(zip(self.labels, label_pos))
self.tag = tag
self.ascending = ascending
self.tag_choose_done = False
def run(self, **kwargs):
# 筛选标签 + True/False (开关标签) /None(切换状态)
# 排序 + True/False 箭头朝(上/下)
self.target_state = None
self.order_label = None
self.labels_chooed_done = False
if kwargs:
text = ""
for self.label, value in kwargs.items():
if self.label in self.orders and self.order_label is None:
self.order_label = self.label
elif self.order_label is not None:
raise ValueError("排序参数仅有一个")
text.join(f"{'打开' if value else '关闭'}{self.label}筛选")
super().run()
text += ",关闭其余筛选"
logger.info(text)
else:
logger.info("关闭所有筛选")
if self.target_state is None:
self.target_state = dict(zip(self.labels, [False] * len(self.labels)))
self.target_state.update(kwargs)
logger.info(f"target_state :{self.target_state}")
try:
super().run()
except ValueError as e:
logger.error(e.__traceback__)
def transition(self) -> bool:
if (scene := self.scene()) == Scene.RIIC_OPERATOR_SELECT:
if self.labels_chooed_done:
if self.order_label in self.orders:
n, s = self.detect_arrange_order()
logger.info(f"detect_arrange_order {n}{s}")
logger.info(
f"order_label {self.order_label}{self.target_state[self.order_label]}"
)
if (
n != self.order_label
or s != self.target_state[self.order_label]
):
self.switch_arrange_order(
self.order_label, self.target_state[self.order_label]
)
return
return True
else:
self.tap_element("arrange_order_options")
elif scene == Scene.INFRA_ARRANGE_ORDER:
logger.debug(self.target_state)
not_taped = True
for label, pos in self.label_pos_map.items():
current_state = self.get_color(pos)[2] > 100
if self.target_state[label] is None:
self.target_state[label] = not current_state
if self.target_state[label] != current_state:
not_taped = False
self.tap(pos, interval=0.1)
if not_taped:
confirm_pos = (config.recog.w * 0.8, config.recog.h * 0.8)
self.tap(confirm_pos)
self.labels_chooed_done = True
else:
return
elif scene in self.waiting_scene:
self.waiting_solver()
else:
return False
return True
def detect_arrange_order(self):
x_list = (1196, 1320, 1445, 1572)
x_list = (1310, 1435, 1560, 1687)
y = 70
hsv = cv2.cvtColor(config.recog.img, cv2.COLOR_RGB2HSV)
mask = cv2.inRange(hsv, (99, 200, 0), (100, 255, 255))
mask = cv2.inRange(config.recog.hsv, (99, 200, 0), (100, 255, 255))
for idx, x in enumerate(x_list):
if np.count_nonzero(mask[y : y + 3, x : x + 5]):
return (self.orders[idx], True)
@ -120,10 +37,21 @@ class RIIC_Filter(SceneGraphSolver):
return (self.orders[idx], False)
def switch_arrange_order(self, name, ascending=False):
name_x = {"工作状态": 1197, "技能": 1322, "心情": 1447, "信赖值": 1575}
name_x = {"工作状态": 1310, "技能": 1435, "心情": 1560, "信赖值": 1687}
if isinstance(name, int):
name = list(name_x.keys())[name - 1]
if isinstance(ascending, str):
ascending = ascending == "true"
name_y = 60
self.tap((name_x[name], name_y), interval=0.5)
def transition(self) -> bool:
if self.scene() == Scene.RIIC_OPERATOR_SELECT:
n, s = self.detect_arrange_order()
if n != self.tag or s != self.ascending:
logger.debug(f"{n}:{s}->{self.tag}:{self.ascending}")
self.switch_arrange_order(self.tag, self.ascending)
return
return True
else:
return False

View file

@ -0,0 +1,91 @@
from mower.utils import config
from mower.utils.graph.utils import SceneGraphSolver
from mower.utils.log import logger
from mower.utils.scene import Scene
class RIICTagChoose(SceneGraphSolver):
def run(self, tag) -> None:
self.tag = tag
self.first_tag = None
self.turned = True
self.professions = [
"PIONEER",
"WARRIOR",
"TANK",
"SNIPER",
"CASTER",
"MEDIC",
"SUPPORT",
"SPECIAL",
]
return super().run()
def get_now_tag(self):
if self.find("choose_agent/foldup"):
return "ALL"
if arrow := self.find("choose_agent/profession/choose_arrow"):
for i in self.professions:
str = "choose_agent/profession/" + i
pos = self.find(str)
if pos[0][1] > arrow[0][1] and pos[1][1] < arrow[1][1]:
return i
return False
def tag_choosed(self, profession: str = "ALL"):
str = "choose_agent/profession/" + profession
if profession == "ALL":
if self.find("choose_agent/foldup"):
return True
else:
self.tap_element(str, interval=0.1)
return
elif pos := self.find(str):
arrow = self.find("choose_agent/profession/choose_arrow")
if arrow is None:
self.ctap(pos, interval=config.screenshot_avg / 1000)
return
else:
if pos[0][1] > arrow[0][1] and pos[1][1] < arrow[1][1]:
return True
else:
self.ctap(pos, interval=config.screenshot_avg / 1000)
return
def transition(self) -> bool:
if self.scene() == Scene.RIIC_OPERATOR_SELECT:
if pos := self.find("choose_agent/open_profession"):
self.ctap(pos)
if self.first_tag is None:
self.first_tag = self.get_now_tag()
if self.first_tag == self.tag:
self.turned = False
if self.first_tag == self.tag:
if self.turned is False:
if self.first_tag == "ALL" and self.tag == "ALL":
if self.tag_choosed("PIONEER"):
logger.info(f"切换职业:{self.first_tag}-->PIONEER")
self.turned = True
else:
if self.tag_choosed():
logger.info(f"切换职业:{self.first_tag}-->全部")
self.turned = True
else:
self.first_tag = self.get_now_tag()
if self.turned:
now_tag = self.get_now_tag()
if self.tag != now_tag:
logger.info(f"切换职业:{now_tag}-->{self.tag}")
self.tag_choosed(self.tag)
else:
return True
else:
return False

View file

@ -226,7 +226,9 @@ class Recognizer:
self.scene = Scene.SKIP
elif self.find("login_connecting"):
self.scene = Scene.LOGIN_LOADING
elif self.find("arrange_order_options"):
elif self.find("choose_agent/riic/work_state") or self.find(
"choose_agent/riic/work_state_blue"
):
self.scene = Scene.RIIC_OPERATOR_SELECT
elif self.find("arrange_order_options_scene"):
self.scene = Scene.INFRA_ARRANGE_ORDER

View file

@ -7,6 +7,8 @@ color = {
"choose_agent/battle_confirm": ((1476, 991), (1591, 991)),
"choose_agent/clear": (685, 996),
"choose_agent/fast_select": ((1296, 25), (1592, 98)),
"choose_agent/riic/work_state": (1270, 50),
"choose_agent/riic/work_state_blue": (1269, 46),
"choose_agent/support_agent": (619, 44),
"choose_agent/support_status": (1038, 41),
"clue": (1740, 855),
@ -17,7 +19,7 @@ color = {
"clue/summary": (59, 153),
"clue/title_party": (1621, 200),
"confirm": (0, 683),
"confirm_blue": (1724, 990),
"confirm_blue": ((1609, 990), (1724, 990)),
"contract": (1652, 699),
"control_central_assistants": (39, 560),
"depot": (0, 955),