Refactor: More compact route files

This commit is contained in:
LmeSzinc 2023-09-24 06:52:52 +08:00
parent 6eb9317ac4
commit 3fa28fba24
7 changed files with 192 additions and 19 deletions

View File

@ -0,0 +1,89 @@
import os
import re
from dataclasses import dataclass, fields
from module.base.code_generator import CodeGenerator
@dataclass
class RouteData:
name: str
route: str
plane: str
floor: str = 'F1'
position: tuple = None
class RouteExtract:
def __init__(self, folder):
self.folder = folder
def iter_files(self):
for path, folders, files in os.walk(self.folder):
path = path.replace('\\', '/')
for file in files:
if file.endswith('.py'):
yield f'{path}/{file}'
def extract_route(self, file):
print(f'Extract {file}')
with open(file, 'r', encoding='utf-8') as f:
content = f.read()
"""
def route_item_enemy(self):
self.enter_himeko_trial()
self.map_init(plane=Jarilo_BackwaterPass, position=(519.9, 361.5))
"""
regex = re.compile(
r'def (?P<func>[a-zA-Z0-9_]*?)\(self\):.*?'
r'self\.map_init\((.*?)\)'
, re.DOTALL)
file = file.replace(self.folder, '').replace('.py', '').replace('/', '_').strip('_')
module = f"{self.folder.strip('./').replace('/', '.')}.{file.replace('_', '.')}"
for result in regex.findall(content):
func, data = result
res = re.search(r'plane=([a-zA-Z_]*)', data)
if res:
plane = res.group(1)
else:
# Must contain plane
continue
res = re.search(r'floor=([\'"a-zA-Z0-9_]*)', data)
if res:
floor = res.group(1).strip('"\'')
else:
floor = 'F1'
res = re.search(r'position=\(([0-9.]*)[, ]+([0-9.]*)', data)
if res:
position = (float(res.group(1)), float(res.group(2)))
else:
position = None
yield RouteData(
name=f'{file}__{func}',
route=f'{module}:{func}',
plane=plane,
floor=floor,
position=position,
)
def write(self, file):
gen = CodeGenerator()
gen.Import("""
from tasks.map.route.base import RouteData
""")
for f in self.iter_files():
for row in self.extract_route(f):
with gen.Object(key=row.name, object_class='RouteData'):
for key in fields(row):
value = getattr(row, key.name)
gen.ObjectAttr(key.name, value)
gen.write(file)
if __name__ == '__main__':
os.chdir(os.path.join(os.path.dirname(__file__), '../'))
RouteExtract('./route/daily').write('./tasks/map/route/route/daily.py')

View File

@ -23,6 +23,7 @@ from tasks.dungeon.ui import DungeonUI
from tasks.item.consumable_usage import ConsumableUsageUI from tasks.item.consumable_usage import ConsumableUsageUI
from tasks.item.relics import RelicsUI from tasks.item.relics import RelicsUI
from tasks.map.route.loader import RouteLoader from tasks.map.route.loader import RouteLoader
from tasks.map.route.route import ROUTE_DAILY
class DailyQuestOcr(Ocr): class DailyQuestOcr(Ocr):
@ -260,7 +261,7 @@ class DailyQuestUI(DungeonUI, RouteLoader):
if RelicsUI(self.config, self.device).salvage_relic(): if RelicsUI(self.config, self.device).salvage_relic():
done += 1 done += 1
if KEYWORDS_DAILY_QUEST.Complete_Forgotten_Hall_1_time in quests: if KEYWORDS_DAILY_QUEST.Complete_Forgotten_Hall_1_time in quests:
self.route_run('daily.forgotten_hall.stage_1') self.route_run(ROUTE_DAILY.ForgottenHallStage1__route)
done += 1 done += 1
return done return done

View File

@ -1,8 +1,19 @@
from dataclasses import dataclass
from tasks.map.control.control import MapControl from tasks.map.control.control import MapControl
from tasks.map.control.waypoint import Waypoint from tasks.map.control.waypoint import Waypoint
from tasks.map.keywords import MapPlane from tasks.map.keywords import MapPlane
@dataclass
class RouteData:
name: str
route: str
plane: str
floor: str = 'F1'
position: tuple = None
class RouteBase(MapControl): class RouteBase(MapControl):
""" """
Base class of `Route` Base class of `Route`

View File

@ -1,38 +1,71 @@
import importlib import importlib
import os import os
from module.exception import RequestHumanTakeover from module.base.decorator import del_cached_property
from module.exception import ScriptError
from module.logger import logger from module.logger import logger
from tasks.base.ui import UI from tasks.base.ui import UI
from tasks.map.route.base import RouteBase from tasks.map.route.base import RouteBase, RouteData
class RouteLoader(UI): class RouteLoader(UI):
route: RouteBase route_module: str = ''
route_func: str = ''
route_obj: RouteBase
def route_run(self, route: str): def route_delete(self):
del_cached_property(self, 'route_obj')
self.route_module = ''
self.route_func = ''
def route_run(self, route: RouteData | str):
""" """
Args: Args:
route: .py module path such as `daily.forgotten_hall.stage1` route: .py module path such as `route.daily.ForgottenHallStage1:route`
which will load `./route/daily/forgotten_hall/stage1.py` which will load `./route/daily/ForgottenHallStage1.py` and run `Route.route()`
""" """
folder, name = route.rsplit('.', maxsplit=1) logger.hr('Route run', level=1)
path = f'./route/{route.replace(".", "/")}.py' if isinstance(route, RouteData):
route = route.route
logger.attr('Route', route)
try: try:
module = importlib.import_module(f'route.{folder}.{name}') module, func = route.split(':')
except ValueError:
logger.critical(f'Route invalid: {route}')
raise ScriptError
path = f'./{module.replace(".", "/")}.py'
# Import route file
try:
module_obj = importlib.import_module(f'{module}')
except ModuleNotFoundError: except ModuleNotFoundError:
logger.critical(f'Route file not found: {route} ({path})') logger.critical(f'Route file not found: {module} ({path})')
if not os.path.exists(path): if not os.path.exists(path):
logger.critical(f'Route file not exists: {path}') logger.critical(f'Route file not exists: {path}')
raise RequestHumanTakeover raise ScriptError
# config = copy.deepcopy(self.config).merge(module.Config()) # Create route object
config = self.config # Reuse the previous one
device = self.device if self.route_module != module:
# config = copy.deepcopy(self.config).merge(module.Config())
config = self.config
device = self.device
try:
self.route_obj = module_obj.Route(config=config, device=device)
except AttributeError as e:
logger.critical(e)
logger.critical(f'Route file {route} ({path}) must define class Route')
raise ScriptError
self.route_module = module
# Get route func
try: try:
self.route = module.Route(config=config, device=device) func_obj = self.route_obj.__getattribute__(func)
return self.route.route()
except AttributeError as e: except AttributeError as e:
logger.critical(e) logger.critical(e)
logger.critical(f'Route file {route} ({path}) must define Route.route()') logger.critical(f'Route class in {route} ({path}) does not have method {func}')
raise RequestHumanTakeover raise ScriptError
self.route_func = func
# Run
func_obj()

View File

@ -0,0 +1 @@
import tasks.map.route.route.daily as ROUTE_DAILY

View File

@ -0,0 +1,38 @@
from tasks.map.route.base import RouteData
ForgottenHallStage1__route = RouteData(
name='ForgottenHallStage1__route',
route='route.daily.ForgottenHallStage1:route',
plane='Jarilo_BackwaterPass',
floor='F1',
position=(369.4, 643.4),
)
HimekoTrial__route_item_enemy = RouteData(
name='HimekoTrial__route_item_enemy',
route='route.daily.HimekoTrial:route_item_enemy',
plane='Jarilo_BackwaterPass',
floor='F1',
position=(519.9, 361.5),
)
HimekoTrial__route_item = RouteData(
name='HimekoTrial__route_item',
route='route.daily.HimekoTrial:route_item',
plane='Jarilo_BackwaterPass',
floor='F1',
position=(519.9, 361.5),
)
HimekoTrial__route_enemy = RouteData(
name='HimekoTrial__route_enemy',
route='route.daily.HimekoTrial:route_enemy',
plane='Jarilo_BackwaterPass',
floor='F1',
position=(519.9, 361.5),
)
HimekoTrial__exit = RouteData(
name='HimekoTrial__exit',
route='route.daily.HimekoTrial:exit',
plane='Jarilo_BackwaterPass',
floor='F1',
position=(519.9, 361.5),
)