Refactor: Auto-switch between domain exit implements

This commit is contained in:
LmeSzinc 2023-11-14 01:07:23 +08:00
parent 0bf4b45c8c
commit 2591a3aee0
4 changed files with 148 additions and 7 deletions

View File

@ -121,6 +121,12 @@ class ManualConfig:
OS_ACTION_POINT_PRESERVE = 0 OS_ACTION_POINT_PRESERVE = 0
OS_CL1_YELLOW_COINS_PRESERVE = 100000 OS_CL1_YELLOW_COINS_PRESERVE = 100000
"""
tasks.rogue
"""
# 2023.11.13 Migrate domain exit implementation, True to stop before domain exit
DOMAIN_EXIT_MIGRATE_DEV = False
ADDING = ''.join([chr(int(f)) for f in ManualConfig.OS_EXPLORE_CENTER.split('>')]) ADDING = ''.join([chr(int(f)) for f in ManualConfig.OS_EXPLORE_CENTER.split('>')])

View File

@ -51,6 +51,9 @@ class Waypoint:
__repr__ = __str__ __repr__ = __str__
def __bool__(self):
return True
def run_2x(self) -> "Waypoint": def run_2x(self) -> "Waypoint":
""" """
Product a Waypoint object with overridden "speed", Product a Waypoint object with overridden "speed",

View File

@ -267,6 +267,7 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward):
logger.hr('Domain single exit', level=1) logger.hr('Domain single exit', level=1)
waypoints = ensure_waypoints(waypoints) waypoints = ensure_waypoints(waypoints)
end_point = waypoints[-1] end_point = waypoints[-1]
end_point.min_speed = 'run'
end_point.interact_radius = 5 end_point.interact_radius = 5
end_point.expected_end.append(self._domain_exit_expected_end) end_point.expected_end.append(self._domain_exit_expected_end)
@ -274,6 +275,32 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward):
self._domain_exit_wait_next() self._domain_exit_wait_next()
return result return result
def _domain_exit_old(self):
"""
An old implementation that go along specific direction without retries
"""
logger.info(f'Using old predict_door()')
direction = self.predict_door_old()
direction_limit = 55
if direction is not None:
if abs(direction) > direction_limit:
logger.warning(f'Unexpected direction to go: {direction}, limited in {direction_limit}')
if direction > 0:
direction = direction_limit
elif direction < 0:
direction = -direction_limit
point = Waypoint(
position=(0, 0),
min_speed='run',
lock_direction=direction,
interact_radius=10000,
expected_end=[self._domain_exit_expected_end],
)
self.goto(point)
self._domain_exit_wait_next()
return True
def domain_exit( def domain_exit(
self, self,
*waypoints, *waypoints,
@ -281,22 +308,36 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward):
left_door: Waypoint = None, left_door: Waypoint = None,
right_door: Waypoint = None right_door: Waypoint = None
): ):
"""
Goto domain exit, choose one door, goto door
"""
logger.hr('Domain exit', level=1) logger.hr('Domain exit', level=1)
# Goto the front of the two doors
waypoints = ensure_waypoints(waypoints) waypoints = ensure_waypoints(waypoints)
end_point = waypoints[-1] end_point = waypoints[-1]
end_point.endpoint_threshold = 1.5 end_point.endpoint_threshold = 1.5
self.goto(*waypoints) self.goto(*waypoints)
# Rotate camera to insight two doors
logger.hr('End rotation', level=2) logger.hr('End rotation', level=2)
self.rotation_set(end_rotation, threshold=10) self.rotation_set(end_rotation, threshold=10)
# Choose a door
logger.hr('Find domain exit', level=2) logger.hr('Find domain exit', level=2)
logger.info(f'Migrate={self.config.DOMAIN_EXIT_MIGRATE_DEV}, left_door={left_door}, right_door={right_door}')
if not self.config.DOMAIN_EXIT_MIGRATE_DEV and (not left_door and not right_door):
return self._domain_exit_old()
logger.info(f'Using new predict_door()')
door = self.predict_door() door = self.predict_door()
if left_door is None or right_door is None: if self.config.DOMAIN_EXIT_MIGRATE_DEV and self.exit_has_double_door and (not left_door or not right_door):
logger.critical(f'Domain exit is not defined in: {self.route_func}') logger.critical(f'Domain exit is not defined in: {self.route_func}')
exit(1) exit(1)
# Goto door
if door == 'left_door': if door == 'left_door':
if not left_door:
return self._domain_exit_old()
if self.domain_single_exit(left_door): if self.domain_single_exit(left_door):
return True return True
else: else:
@ -306,6 +347,8 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward):
else: else:
return False return False
elif door == 'right_door': elif door == 'right_door':
if not right_door:
return self._domain_exit_old()
if self.domain_single_exit(right_door): if self.domain_single_exit(right_door):
return True return True
else: else:
@ -316,6 +359,10 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward):
return False return False
else: else:
logger.error('Cannot goto either exit doors, try both') logger.error('Cannot goto either exit doors, try both')
if not left_door:
return self._domain_exit_old()
if not right_door:
return self._domain_exit_old()
if self.domain_single_exit(left_door): if self.domain_single_exit(left_door):
return True return True
elif self.domain_single_exit(right_door): elif self.domain_single_exit(right_door):

View File

@ -139,6 +139,83 @@ class RogueExit(CombatInteract):
logger.info(f'PlanarDoor: {planar_door}, direction: {direction}') logger.info(f'PlanarDoor: {planar_door}, direction: {direction}')
return direction return direction
def predict_door_by_name_old(self, image) -> float | None:
# Paint current name black
x1, y1, x2, y2 = OCR_MAP_NAME.area
image[y1:y2, x1:x2] = (0, 0, 0)
ocr = OcrDomainExit(OCR_DOMAIN_EXIT)
results = ocr.matched_ocr(image, keyword_classes=MapPlane)
centers = [area_center(result.area) for result in results]
logger.info(f'DomainDoor: {centers}')
directions = [self.screen2direction(center) for center in centers]
count = len(centers)
if count == 0:
logger.warning('No domain exit found')
return None
if count == 1:
logger.info(f'Goto next domain: {results[0]}')
return directions[0]
# Doors >= 2
for expect in [
KEYWORDS_MAP_PLANE.Rogue_DomainBoss,
KEYWORDS_MAP_PLANE.Rogue_DomainElite,
KEYWORDS_MAP_PLANE.Rogue_DomainRespite,
]:
for domain, direction in zip(results, directions):
if domain == expect:
logger.warning('Found multiple doors but has unique domain in it')
logger.info(f'Goto next domain: {domain}')
return direction
logger.attr('DomainStrategy', self.config.RogueWorld_DomainStrategy)
if self.config.RogueWorld_DomainStrategy == 'occurrence':
for expect in [
KEYWORDS_MAP_PLANE.Rogue_DomainTransaction,
KEYWORDS_MAP_PLANE.Rogue_DomainOccurrence,
KEYWORDS_MAP_PLANE.Rogue_DomainEncounter,
KEYWORDS_MAP_PLANE.Rogue_DomainCombat,
]:
for domain, direction in zip(results, directions):
if domain == expect:
logger.info(f'Goto next domain: {domain}')
return direction
elif self.config.RogueWorld_DomainStrategy == 'combat':
for expect in [
KEYWORDS_MAP_PLANE.Rogue_DomainCombat,
KEYWORDS_MAP_PLANE.Rogue_DomainEncounter,
KEYWORDS_MAP_PLANE.Rogue_DomainOccurrence,
KEYWORDS_MAP_PLANE.Rogue_DomainTransaction,
]:
for domain, direction in zip(results, directions):
if domain == expect:
logger.info(f'Goto next domain: {domain}')
return direction
else:
logger.error(f'Unknown domain strategy: {self.config.RogueWorld_DomainStrategy}')
logger.error('No domain was selected, return the first instead')
logger.info(f'Goto next domain: {results[0]}')
return directions[0]
def predict_door_old(self, skip_first_screenshot=True) -> float | None:
timeout = Timer(3, count=6).start()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if timeout.reached():
logger.error('Predict door timeout')
return None
direction = self.predict_door_by_name_old(self.device.image)
if direction is not None:
return direction
def predict_door_by_name(self, image) -> tuple[MapPlane | None, MapPlane | None]: def predict_door_by_name(self, image) -> tuple[MapPlane | None, MapPlane | None]:
""" """
Args: Args:
@ -223,16 +300,18 @@ class RogueExit(CombatInteract):
logger.error(f'Unknown domain strategy: {self.config.RogueWorld_DomainStrategy}') logger.error(f'Unknown domain strategy: {self.config.RogueWorld_DomainStrategy}')
logger.error('No domain was selected, return the first instead') logger.error('No domain was selected, return the first instead')
if left_door is not None: if left_door:
logger.info(f'Goto next domain: left_door={left_door}') logger.info(f'Goto next domain: left_door={left_door}')
return 'left_door' return 'left_door'
elif right_door is not None: elif right_door:
logger.info(f'Goto next domain: right_door={right_door}') logger.info(f'Goto next domain: right_door={right_door}')
return 'right_door' return 'right_door'
else: else:
logger.error(f'No domain door') logger.error(f'No domain door')
return None return None
exit_has_double_door = False
def predict_door(self, skip_first_screenshot=True) -> str | None: def predict_door(self, skip_first_screenshot=True) -> str | None:
""" """
Args: Args:
@ -242,19 +321,25 @@ class RogueExit(CombatInteract):
str: 'left_door' or 'right_door' or None str: 'left_door' or 'right_door' or None
""" """
timeout = Timer(3, count=6).start() timeout = Timer(3, count=6).start()
self.exit_has_double_door = False
while 1: while 1:
if skip_first_screenshot: if skip_first_screenshot:
skip_first_screenshot = False skip_first_screenshot = False
else: else:
self.device.screenshot() self.device.screenshot()
# End
if timeout.reached(): if timeout.reached():
logger.error('Predict door timeout') logger.error('Predict door timeout')
return None return None
left_door, right_door = self.predict_door_by_name(self.device.image) left_door, right_door = self.predict_door_by_name(self.device.image)
logger.info(f'DomainExit: left_door={left_door}, right_door={right_door}') logger.info(f'DomainExit: left_door={left_door}, right_door={right_door}')
if left_door is not None or right_door is not None: if not left_door and not right_door:
continue
# End
self.exit_has_double_door = left_door and right_door
door = self.choose_door(left_door, right_door) door = self.choose_door(left_door, right_door)
if door is not None: if door is not None:
return door return door