改写战斗中替换group的逻辑
This commit is contained in:
commit
7f89eb0db8
3890 changed files with 82290 additions and 0 deletions
116
mower/solvers/infra/switch_product/__init__.py
Normal file
116
mower/solvers/infra/switch_product/__init__.py
Normal file
|
@ -0,0 +1,116 @@
|
|||
from datetime import datetime
|
||||
|
||||
import cv2
|
||||
|
||||
from mower.models import riic_base_digits
|
||||
from mower.solvers.infra.base_mixin import BaseMixin
|
||||
from mower.solvers.infra.drone import DroneSolver
|
||||
from mower.solvers.infra.enter_room import EnterRoomSolver
|
||||
from mower.utils import config
|
||||
from mower.utils import typealias as tp
|
||||
from mower.utils.digit_reader import DigitReader
|
||||
from mower.utils.graph import SceneGraphSolver
|
||||
from mower.utils.image import cropimg, thres2
|
||||
from mower.utils.log import logger
|
||||
|
||||
from .check_current_product import CheckCurrentProductSolver
|
||||
from .choose_product import ChooseProductSolver
|
||||
from .get_remain_time import GetRemainTimeSolver
|
||||
from .wait_for_product import WaitForProductSolver
|
||||
|
||||
production_time = {
|
||||
"经验": 180 * 60,
|
||||
"赤金": 72 * 60,
|
||||
"源石碎片": 60 * 60,
|
||||
"先锋双芯片": 60 * 60,
|
||||
"狙击双芯片": 60 * 60,
|
||||
"医疗双芯片": 60 * 60,
|
||||
"术师双芯片": 60 * 60,
|
||||
"近卫双芯片": 60 * 60,
|
||||
"重装双芯片": 60 * 60,
|
||||
"辅助双芯片": 60 * 60,
|
||||
"特种双芯片": 60 * 60,
|
||||
}
|
||||
|
||||
|
||||
class SwitchProductSolver(SceneGraphSolver, BaseMixin):
|
||||
def run(self, room, tar_product, only_get_time=False):
|
||||
logger.info(f"Start:切换产物 房间:{room} 目标产物:{tar_product}")
|
||||
EnterRoomSolver().run(room, detail=False)
|
||||
|
||||
# 检查当前产物
|
||||
cur_product = CheckCurrentProductSolver().run(room)
|
||||
logger.info(f"当前产物:{cur_product}")
|
||||
if cur_product == tar_product:
|
||||
logger.info("当前产物已为目标产物")
|
||||
return True
|
||||
|
||||
# 读取当前无人机数量避免重复开关
|
||||
cur_drone_count = DigitReader().get_drone(config.recog.gray)
|
||||
# 读取生产速度
|
||||
speed = self.read_speed()
|
||||
# 获取无人机加速的剩余时间
|
||||
remain_time = GetRemainTimeSolver().run(room)
|
||||
if only_get_time:
|
||||
return remain_time % production_time[cur_product] / speed
|
||||
# 使用无人机加速
|
||||
start_time = datetime.now()
|
||||
drone_count = remain_time % production_time[cur_product] // 180
|
||||
logger.info(f"应使用无人机数量:{drone_count}")
|
||||
if not DroneSolver().run(room, drone_count, cur_count=cur_drone_count):
|
||||
return False
|
||||
|
||||
# 等待产物完成
|
||||
wait_time = max(
|
||||
remain_time % production_time[cur_product] % 180 / speed
|
||||
- (datetime.now() - start_time).total_seconds(),
|
||||
0,
|
||||
)
|
||||
|
||||
logger.info(f"等待时间:{wait_time}")
|
||||
WaitForProductSolver().run(room, wait_time)
|
||||
|
||||
# 选择目标产物
|
||||
if not ChooseProductSolver().run(room, tar_product):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def number(self, scope: tp.Scope, height: int, thres: int) -> int:
|
||||
"数字识别"
|
||||
img = cropimg(config.recog.gray, scope)
|
||||
default_height = 25
|
||||
if height != default_height:
|
||||
scale = 25 / height
|
||||
img = cv2.resize(img, None, None, scale, scale)
|
||||
img = thres2(img, thres)
|
||||
img = cv2.bitwise_not(img)
|
||||
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 = ""
|
||||
for x, y, w, h in rect:
|
||||
if h < 8 and w < 8:
|
||||
value += "."
|
||||
continue
|
||||
elif h < 20 and w < 20:
|
||||
continue
|
||||
digit = cropimg(img, ((x, y), (x + w, y + h)))
|
||||
digit = cv2.copyMakeBorder(
|
||||
digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
|
||||
)
|
||||
score = []
|
||||
for i in range(10):
|
||||
im = riic_base_digits[i]
|
||||
result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
|
||||
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
||||
score.append(min_val)
|
||||
value += str(score.index(min(score)))
|
||||
|
||||
return value
|
||||
|
||||
def read_speed(self) -> float:
|
||||
speed1 = self.number(((1185, 955), (1255, 977)), 17, 120)
|
||||
speed2 = self.number(((1285, 955), (1355, 977)), 17, 150)
|
||||
return float(speed1) + float(speed2) + 1.0
|
49
mower/solvers/infra/switch_product/check_current_product.py
Normal file
49
mower/solvers/infra/switch_product/check_current_product.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
from datetime import datetime, 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.graph import SceneGraphSolver
|
||||
from mower.utils.recognize import Scene
|
||||
|
||||
from .utils import product
|
||||
|
||||
|
||||
class CheckCurrentProductSolver(SceneGraphSolver, BaseMixin):
|
||||
def run(self, room) -> None:
|
||||
self.room = room
|
||||
if (
|
||||
self.scene() == Scene.FACTORY_ROOMS
|
||||
and not self.detect_room_inside() == self.room
|
||||
):
|
||||
EnterRoomSolver().run(self.room, detail=False)
|
||||
self.wait_start()
|
||||
super().run()
|
||||
return self.res
|
||||
|
||||
def timeout(self) -> bool:
|
||||
return datetime.now() > self.start_time + timedelta(seconds=5)
|
||||
|
||||
def wait_start(self):
|
||||
self.start_time = datetime.now()
|
||||
|
||||
def check_product(self) -> str:
|
||||
for p in product:
|
||||
if self.find(f"product/{p}"):
|
||||
return p
|
||||
|
||||
def transition(self) -> bool:
|
||||
if (scene := self.scene()) == Scene.INFRA_DETAILS:
|
||||
self.wait_start()
|
||||
self.ctap((200, 1000), 1, config.screenshot_avg / 1000)
|
||||
elif scene == Scene.FACTORY_ROOMS:
|
||||
if res := self.check_product():
|
||||
self.res = res
|
||||
return True
|
||||
elif self.timeout():
|
||||
self.res = None
|
||||
return True
|
||||
elif scene in self.waiting_scene:
|
||||
self.waiting_solver()
|
||||
else:
|
||||
EnterRoomSolver().run(self.room, detail=False)
|
119
mower/solvers/infra/switch_product/choose_product.py
Normal file
119
mower/solvers/infra/switch_product/choose_product.py
Normal file
|
@ -0,0 +1,119 @@
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
import cv2
|
||||
|
||||
from mower.models import riic_base_digits
|
||||
from mower.solvers.infra.enter_room import EnterRoomSolver
|
||||
from mower.utils import config
|
||||
from mower.utils import typealias as tp
|
||||
from mower.utils.email import send_message
|
||||
from mower.utils.graph import SceneGraphSolver
|
||||
from mower.utils.image import cmatch, cropimg, loadres, thres2
|
||||
from mower.utils.log import logger
|
||||
from mower.utils.recognize import Scene
|
||||
|
||||
production_index = {
|
||||
"经验": [0, 1],
|
||||
"赤金": [1, 0],
|
||||
"源石碎片": [3, 0],
|
||||
"先锋双芯片": [2, 0],
|
||||
"狙击双芯片": [2, 5],
|
||||
"医疗双芯片": [2, 6],
|
||||
"术师双芯片": [2, 2],
|
||||
"近卫双芯片": [2, 4],
|
||||
"重装双芯片": [2, 1],
|
||||
"辅助双芯片": [2, 3],
|
||||
"特种双芯片": [2, 7],
|
||||
}
|
||||
first_pos = [((180, 215 + 140 * i), (190, 225 + 140 * i)) for i in range(4)]
|
||||
|
||||
second_pos = [(500 + 742 * j, 200 + 270 * i) for j in range(2) for i in range(4)]
|
||||
|
||||
|
||||
class ChooseProductSolver(SceneGraphSolver):
|
||||
def run(self, room, tar_product) -> bool:
|
||||
self.room = room
|
||||
self.tar_product = tar_product
|
||||
self.first_pos = first_pos[production_index[self.tar_product][0]]
|
||||
self.second_pos = second_pos[production_index[self.tar_product][1]]
|
||||
self.success = False
|
||||
self.num_flag = False
|
||||
self.wait_start()
|
||||
super().run()
|
||||
return self.success
|
||||
|
||||
def be_choosen(self) -> bool:
|
||||
img = cropimg(config.recog.img, self.first_pos)
|
||||
res = loadres("product_be_choosen")
|
||||
return cmatch(img, res, 60)
|
||||
|
||||
def timeout(self) -> bool:
|
||||
return datetime.now() > self.start_time + timedelta(seconds=10)
|
||||
|
||||
def wait_start(self):
|
||||
self.start_time = datetime.now()
|
||||
|
||||
def number(self, scope: tp.Scope, height: int, thres: int) -> int:
|
||||
"数字识别"
|
||||
img = cropimg(config.recog.gray, scope)
|
||||
default_height = 25
|
||||
if height != default_height:
|
||||
scale = 25 / height
|
||||
img = cv2.resize(img, None, None, scale, scale)
|
||||
img = thres2(img, thres)
|
||||
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,)
|
||||
)
|
||||
score = []
|
||||
for i in range(10):
|
||||
im = riic_base_digits[i]
|
||||
result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
|
||||
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
||||
score.append(min_val)
|
||||
value = value * 10 + score.index(min(score))
|
||||
|
||||
return value
|
||||
|
||||
def transition(self) -> bool:
|
||||
if (scene := self.scene()) == Scene.INFRA_DETAILS:
|
||||
self.ctap((200, 1000), 1, config.screenshot_avg / 1000)
|
||||
elif scene == Scene.FACTORY_ROOMS:
|
||||
if self.success:
|
||||
return True
|
||||
elif self.find("reload_check"):
|
||||
if self.num_flag:
|
||||
self.ctap((1400, 850), 1, config.screenshot_avg / 1000)
|
||||
elif self.timeout():
|
||||
logger.warning("切换成功,剩余材料不足1")
|
||||
send_message(f"切换成功,剩余材料不足1:{self.room}", level="WARNING")
|
||||
self.num_flag = True
|
||||
else:
|
||||
self.tap((1450, 300))
|
||||
if self.number(((1306, 462), (1391, 521)), 50, 200) != 1:
|
||||
self.num_flag = True
|
||||
else:
|
||||
self.tap((1750, 400))
|
||||
elif scene == Scene.CHOOSE_PRODUCT:
|
||||
if self.find("icon_notification_black"):
|
||||
logger.warning("切换产物材料不足")
|
||||
send_message(f"切换产物材料不足:{self.room}", level="WARNING")
|
||||
return True
|
||||
elif not self.be_choosen():
|
||||
self.tap(self.get_pos(self.first_pos))
|
||||
else:
|
||||
self.tap(self.second_pos)
|
||||
elif scene == Scene.PRODUCT_SWITCHING_CONFIRM:
|
||||
self.tap_element("double_confirm/main", x_rate=1)
|
||||
self.success = True
|
||||
elif scene in self.waiting_scene:
|
||||
self.waiting_solver()
|
||||
else:
|
||||
EnterRoomSolver().run(self.room, detail=False)
|
80
mower/solvers/infra/switch_product/get_remain_time.py
Normal file
80
mower/solvers/infra/switch_product/get_remain_time.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
import cv2
|
||||
|
||||
from mower.models import riic_base_digits
|
||||
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 typealias as tp
|
||||
from mower.utils.graph import SceneGraphSolver
|
||||
from mower.utils.image import cropimg, thres2
|
||||
from mower.utils.recognize import Scene
|
||||
|
||||
|
||||
class GetRemainTimeSolver(SceneGraphSolver, BaseMixin):
|
||||
"""
|
||||
返回剩余时间,换算为秒
|
||||
"""
|
||||
|
||||
def run(
|
||||
self,
|
||||
room: str,
|
||||
):
|
||||
self.room = room
|
||||
if (
|
||||
self.scene() == Scene.FACTORY_ROOMS
|
||||
and not self.detect_room_inside() == self.room
|
||||
):
|
||||
EnterRoomSolver().run(self.room, detail=False)
|
||||
super().run()
|
||||
return self.res
|
||||
|
||||
def number(self, scope: tp.Scope, height: int, thres: int) -> str:
|
||||
"数字识别"
|
||||
img = cropimg(config.recog.gray, scope)
|
||||
default_height = 25
|
||||
if height != default_height:
|
||||
scale = 25 / height
|
||||
img = cv2.resize(img, None, None, scale, scale)
|
||||
img = thres2(img, thres)
|
||||
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 = ""
|
||||
for x, y, w, h in rect:
|
||||
if h < 7 and w < 7:
|
||||
value += ":"
|
||||
continue
|
||||
digit = cropimg(img, ((x, y), (x + w, y + h)))
|
||||
digit = cv2.copyMakeBorder(
|
||||
digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
|
||||
)
|
||||
score = []
|
||||
for i in range(10):
|
||||
im = riic_base_digits[i]
|
||||
result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
|
||||
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
||||
score.append(min_val)
|
||||
value += str(score.index(min(score)))
|
||||
|
||||
return value
|
||||
|
||||
def read_remain_time(self) -> int:
|
||||
h, m, s = self.number(((758, 670), (960, 705)), 30, 100).split("::")
|
||||
return int(h) * 3600 + int(m) * 60 + int(s)
|
||||
|
||||
def transition(self) -> bool:
|
||||
if (scene := self.scene()) == Scene.INFRA_DETAILS:
|
||||
self.ctap((200, 1000), 1, config.screenshot_avg / 1000)
|
||||
elif scene == Scene.FACTORY_ROOMS:
|
||||
if pos := self.find("factory_accelerate"):
|
||||
self.tap(pos)
|
||||
|
||||
elif scene == Scene.DRONE_ACCELERATE:
|
||||
if res := self.read_remain_time():
|
||||
self.res = res
|
||||
return True
|
||||
elif scene in self.waiting_scene:
|
||||
self.waiting_solver()
|
||||
else:
|
||||
EnterRoomSolver().run(self.room, detail=False)
|
13
mower/solvers/infra/switch_product/utils.py
Normal file
13
mower/solvers/infra/switch_product/utils.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
product = [
|
||||
"经验",
|
||||
"赤金",
|
||||
"源石碎片",
|
||||
"先锋双芯片",
|
||||
"狙击双芯片",
|
||||
"医疗双芯片",
|
||||
"术师双芯片",
|
||||
"近卫双芯片",
|
||||
"重装双芯片",
|
||||
"辅助双芯片",
|
||||
"特种双芯片",
|
||||
]
|
36
mower/solvers/infra/switch_product/wait_for_product.py
Normal file
36
mower/solvers/infra/switch_product/wait_for_product.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
from datetime import datetime, 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.graph import SceneGraphSolver
|
||||
from mower.utils.recognize import Scene
|
||||
|
||||
|
||||
class WaitForProductSolver(SceneGraphSolver, BaseMixin):
|
||||
def run(self, room, wait_time) -> None:
|
||||
self.room = room
|
||||
self.wait_start()
|
||||
self.sleep(wait_time)
|
||||
super().run()
|
||||
|
||||
def timeout(self) -> bool:
|
||||
return datetime.now() > self.start_time + timedelta(seconds=180)
|
||||
|
||||
def wait_start(self):
|
||||
self.start_time = datetime.now()
|
||||
|
||||
def transition(self) -> bool:
|
||||
if (scene := self.scene()) == Scene.INFRA_DETAILS:
|
||||
self.ctap((200, 1000), 1, config.screenshot_avg / 1000)
|
||||
elif scene == Scene.FACTORY_ROOMS:
|
||||
if self.detect_product_complete():
|
||||
return True
|
||||
elif self.timeout():
|
||||
return True
|
||||
else:
|
||||
self.tap((1750, 950))
|
||||
elif scene in self.waiting_scene:
|
||||
self.waiting_solver()
|
||||
else:
|
||||
EnterRoomSolver().run(self.room, detail=False)
|
Loading…
Add table
Add a link
Reference in a new issue