From 16a88bb16402f7d39b3a708ae65b6191ebac7540 Mon Sep 17 00:00:00 2001 From: Hengyu Date: Fri, 11 Aug 2023 02:43:06 +0800 Subject: [PATCH] Upd: add MultiLangFilter --- module/base/filter.py | 34 ++++++++++++++++ module/ocr/keyword.py | 5 ++- tasks/rogue/blessing.py | 69 +++++++++++++++------------------ tasks/rogue/keywords/classes.py | 21 ++++++++++ 4 files changed, 89 insertions(+), 40 deletions(-) diff --git a/module/base/filter.py b/module/base/filter.py index baac89085..ed0918f28 100644 --- a/module/base/filter.py +++ b/module/base/filter.py @@ -101,3 +101,37 @@ class Filter: # Invalid filter will be ignored. # Return strange things and make it impossible to match return ['1nVa1d'] + [None] * (len(self.attr) - 1) + + +class MultiLangFilter(Filter): + """ + To support multi-language, there might be different correct matches of same object. + """ + + def apply_filter_to_obj(self, obj, filter): + """ + Args: + obj (object): In this case, attributes of object are array (instead of plain string). + Any match of element in it will return True + filter (list[str]): + + Returns: + bool: If an object satisfy a filter. + """ + for attr, value in zip(self.attr, filter): + if not value: + continue + if not hasattr(obj, attr): + continue + + obj_value = obj.__getattribute__(attr) + if isinstance(obj_value, str): + if obj_value.lower() != str(value): + return False + if isinstance(obj_value, list): + for candidate in obj_value: + if str(candidate) == str(value): + return True + return False + + return True diff --git a/module/ocr/keyword.py b/module/ocr/keyword.py index c2781c9b6..1c0e3e4c6 100644 --- a/module/ocr/keyword.py +++ b/module/ocr/keyword.py @@ -3,10 +3,10 @@ from dataclasses import dataclass from functools import cached_property from typing import ClassVar -from module.exception import ScriptError import module.config.server as server +from module.exception import ScriptError -REGEX_PUNCTUATION = re.compile(r'[ ,.\'"“”,。!!??·•\-—/\\\n\t()\[\]()「」『』【】]') +REGEX_PUNCTUATION = re.compile(r'[ ,.\'"“”,。::!!??·•\-—/\\\n\t()\[\]()「」『』【】《》]') def parse_name(n): @@ -26,6 +26,7 @@ class Keyword: """ Instance attributes and methods """ + @cached_property def ch(self) -> str: return self.cn diff --git a/tasks/rogue/blessing.py b/tasks/rogue/blessing.py index 948aff672..9c4f27012 100644 --- a/tasks/rogue/blessing.py +++ b/tasks/rogue/blessing.py @@ -2,18 +2,18 @@ import re import numpy as np -from dev_tools.keyword_extract import UI_LANGUAGES -from module.base.filter import Filter +from module.base.filter import MultiLangFilter from module.base.timer import Timer from module.base.utils import area_offset, get_color from module.logger import logger +from module.ocr.keyword import Keyword from module.ocr.ocr import Ocr, OcrResultButton, DigitCounter from tasks.rogue.assets.assets_rogue_blessing import * from tasks.rogue.keywords import * from tasks.rogue.preset import * from tasks.rogue.ui import RogueUI -REGEX_PUNCTUATION = re.compile(r'[ ,.\'"“”,。::!!??·•—/()()「」『』【】]') +REGEX_PUNCTUATION = re.compile(r'[ ,.\'"“”,。::!!??·•—/()()「」『』【】《》]') def parse_name(n): @@ -22,44 +22,44 @@ def parse_name(n): def get_regex_from_keyword_name(keyword, attr_name): - regex_pat = "" - attrs = tuple() - for server in UI_LANGUAGES: - string = "" - for path in keyword.instances.values(): - string += f"{parse_name(getattr(path, server))}|" - # some pattern contain each other, make sure each pattern end with "-" or the end of string - regex_pat += f"(?:({string[:-1]})(?:-|$))?" - attrs += (f"{attr_name}_{server}",) - return regex_pat, attrs + string = "" + for instance in keyword.instances.values(): + if hasattr(instance, attr_name): + for name in instance.__getattribute__(attr_name): + string += f"{name}|" + # some pattern contain each other, make sure each pattern end with "-" or the end of string + return f"(?:({string[:-1]})(?:-|$))?" # normal blessing pattern = "" BLESSING_FILTER_ATTR = tuple() -PATH_ATTR_NAME = 'path' -path_regex, path_attr = get_regex_from_keyword_name(RoguePath, PATH_ATTR_NAME) +PATH_ATTR_NAME = 'path_name' +path_regex = get_regex_from_keyword_name(RoguePath, PATH_ATTR_NAME) pattern += path_regex -BLESSING_FILTER_ATTR += path_attr +BLESSING_FILTER_ATTR += (PATH_ATTR_NAME,) pattern += "([123])?-?" BLESSING_FILTER_ATTR += ("rarity",) -BLESSING_ATTR_NAME = 'blessing' -blessing_regex, blessing_attr = get_regex_from_keyword_name(RogueBlessing, BLESSING_ATTR_NAME) +BLESSING_ATTR_NAME = 'blessing_name' +blessing_regex = get_regex_from_keyword_name(RogueBlessing, BLESSING_ATTR_NAME) pattern += blessing_regex -BLESSING_FILTER_ATTR += blessing_attr +BLESSING_FILTER_ATTR += (BLESSING_ATTR_NAME,) FILETER_REGEX = re.compile(pattern) BLESSING_FILTER_PRESET = ("reset", "same_path", "random") -BLESSING_FILTER = Filter(FILETER_REGEX, BLESSING_FILTER_ATTR, BLESSING_FILTER_PRESET) +print(FILETER_REGEX) +print(BLESSING_FILTER_ATTR) +print(BLESSING_FILTER_PRESET) +BLESSING_FILTER = MultiLangFilter(FILETER_REGEX, BLESSING_FILTER_ATTR, BLESSING_FILTER_PRESET) # resonance -RESONANCE_ATTR_NAME = 'resonance' -pattern, RESONANCE_FILTER_ATTR = get_regex_from_keyword_name(RogueResonance, 'resonance') +RESONANCE_ATTR_NAME = 'resonance_name' +pattern = get_regex_from_keyword_name(RogueResonance, 'resonance') FILETER_REGEX = re.compile(pattern) RESONANCE_FILTER_PRESET = ("random",) -RESONANCE_FILTER = Filter(FILETER_REGEX, RESONANCE_FILTER_ATTR, RESONANCE_FILTER_PRESET) +RESONANCE_FILTER = MultiLangFilter(FILETER_REGEX, (RESONANCE_ATTR_NAME,), RESONANCE_FILTER_PRESET) class RogueBuffOcr(Ocr): @@ -226,20 +226,16 @@ class RogueBlessingSelector(RogueBlessingUI): """ def apply_filter(self): - paths = RoguePath.instances + def match_ocr_result(matched_keyword: Keyword): + for blessing in self.blessings: + if blessing.matched_keyword == matched_keyword: + return blessing + return None if not self.blessings: return [] if isinstance(self.blessings[0].matched_keyword, RogueBlessing): - for blessing in self.blessings: - path = paths[blessing.matched_keyword.path_id] - for server in UI_LANGUAGES: - setattr(blessing, f"{PATH_ATTR_NAME}_{server}", parse_name(getattr(path, server))) - setattr(blessing, f"{BLESSING_ATTR_NAME}_{server}", - parse_name(getattr(blessing.matched_keyword, server))) - setattr(blessing, "rarity", getattr(blessing.matched_keyword, "rarity")) - if self.config.Rogue_PresetBlessingFilter == 'preset-1': BLESSING_FILTER.load(parse_name(BLESSING_PRESET_1)) if self.config.Rogue_PresetBlessingFilter == 'custom': @@ -248,17 +244,14 @@ class RogueBlessingSelector(RogueBlessingUI): if isinstance(self.blessings[0].matched_keyword, RogueResonance): if len(self.blessings) == 1: # resonance can not be reset. So have not choice when there's only one option return self.blessings - for blessing in self.blessings: - for server in UI_LANGUAGES: - setattr(blessing, f"{RESONANCE_ATTR_NAME}_{server}", - parse_name(getattr(blessing.matched_keyword, server))) - if self.config.Rogue_PresetResonanceFilter == 'preset-1': RESONANCE_FILTER.load(parse_name(RESONANCE_PRESET_1)) if self.config.Rogue_PresetResonanceFilter == 'custom': RESONANCE_FILTER.load(parse_name(self.config.Rogue_CustomResonanceFilter)) - priority = BLESSING_FILTER.apply(self.blessings) + blessing_keywords = [blessing.matched_keyword for blessing in self.blessings] + priority = BLESSING_FILTER.apply(blessing_keywords) + priority = [option if isinstance(option, str) else match_ocr_result(option) for option in priority] return priority def select_blessing(self, priority: list): diff --git a/tasks/rogue/keywords/classes.py b/tasks/rogue/keywords/classes.py index 5941d0d8e..93011e0cf 100644 --- a/tasks/rogue/keywords/classes.py +++ b/tasks/rogue/keywords/classes.py @@ -1,6 +1,7 @@ from dataclasses import dataclass from typing import ClassVar +from dev_tools.keyword_extract import UI_LANGUAGES from module.ocr.keyword import Keyword @@ -10,14 +11,34 @@ class RogueBlessing(Keyword): path_id: int rarity: int + @property + def path_name(self): + path = RoguePath.instances[self.path_id] + return path.path_name + + @property + def blessing_name(self): + return [self.__getattribute__(f"{server}_parsed") + for server in UI_LANGUAGES if hasattr(self, f"{server}_parsed")] + @dataclass(repr=False) class RoguePath(Keyword): instances: ClassVar = {} + @property + def path_name(self): + return [self.__getattribute__(f"{server}_parsed") + for server in UI_LANGUAGES if hasattr(self, f"{server}_parsed")] + @dataclass(repr=False) class RogueResonance(Keyword): instances: ClassVar = {} path_id: int rarity: int + + @property + def resonance_name(self): + return [self.__getattribute__(f"{server}_parsed") + for server in UI_LANGUAGES if hasattr(self, f"{server}_parsed")]