def _replace_condition_value(self, condition: str, res, suc, show=False): for i, rp in self.CONDITION_KEY.items(): condition = condition.replace(i, str(rp(self, res, suc))) # TODO:{l[0]}.format(l=list) if show: logger.debug("condition:" + condition) return condition
def _autoDetect(self, name: str, overwrite=False): assert name in self.root["maps"], f"不存在地图{name}" root_stage = self.maps[name]["root"] assert root_stage != '', f"地图{name}中无数据!请先设置地图根节点" apd_stage = [] basex, basey = 0, 0 # rect_stage = [os.path.basename(i)[:-4] for i, v in rect] rect = self.getMapStageInfo(name) rect_stage = [os.path.basename(i)[:-4] for i, v in rect] logger.debug(rect_stage) for stage, where in self.maps[name]["stages"]: if stage in rect_stage: apd_stage = [stage, where] basex, basey = rect[rect_stage.index(stage)][1] logger.debug(f"寻找到已知根节点{apd_stage}位于{basex},{basey},退出循环") break assert apd_stage != [], f"在地图中未寻找到已知节点!" dx, dy = apd_stage[1] dx -= basex dy -= basey _exist_stage = [i for i, v in self.maps[name]["stages"]] for stage, where in rect: real_stage = os.path.basename(stage)[:-4] if (real_stage != apd_stage[0] and real_stage not in _exist_stage) or overwrite: self._addStageInMap(name, real_stage, int(dx + where[0]), int(dy + where[1])) logger.notice( f"add {real_stage} in {name} at {dx + where[0]},{dy + where[1]}" ) return True
def newTemplate( self, name, imgPath, default_threshold=0.98, # 默认识别度 onload=False, # 是否直接加载 showlog=False): # 是否显示成功添加 if name[0] not in string.ascii_letters: logger.error(f'名字首位必须为字母字符:{name}') return None if not os.path.isfile(imgPath): logger.error("图片不存在:" + imgPath) return None if name not in self.__namelist__: temp = None if onload: temp = cv2.imread(imgPath) if isinstance(temp, type(None)) and onload: logger.error("未找到%s的图片[位于\'%s\']" % (name, imgPath)) return None setattr(self, name, [temp, imgPath, default_threshold, onload]) self.addName(name) if showlog: logger.debug("成功添加定位标志%s" % name) else: try: setattr( self, name, [cv2.imread(imgPath), imgPath, default_threshold, True]) logger.warning("已覆盖名为%s的定位标志" % name) except: logger.warning('在覆盖标志时出现错误') return None
def clickEpNearist(self, mapId: int): targMap = mapId targStage = None currMap = None currStage = None for stage, include in ArkMap_MainStage.items(): if mapId in include: targStage = int(stage[-1]) if self.__ark.gameTemp.dingwei( f"main\\epstage\\{stage}.png", self.__ark.getScreenShot(121, 208, 86, 47)): currStage = int(stage[-1]) for inc in include: if self.__ark.gameTemp.dingwei( f"main\\epstage\\{inc}.png", self.__ark.getScreenShot(871, 383, 149, 68)): currMap = inc logger.debug(f"[{targStage}-{targMap} {currStage}-{currMap}]") if not (targMap and targStage and currMap and currStage): logger.error( f"未找到地图![{targStage}-{targMap} {currStage}-{currMap}]") raise KeyError(f"未找到地图") if currStage == targStage: self.__ark.clicker.mouse_click( 945 - 312 * min(3, max(currMap - targMap, 0)), 367, t=1.3) return -1 dist = targStage - currStage clk = (64, 133) if dist < 0 else (65, 517) for rg in range(abs(dist)): self.__ark.clicker.mouse_click(*clk, t=0.7) logger.debug(f"{clk},{dist}") return self.clickEpNearist(mapId)
def fromActivityMapMainToActivityMap(self, targMap: str, targStage: str): logger.debug(f"Activity MapMain To Map:{targMap} for {targStage}") if targMap in ACTIVITY_MAP: # TODO:适配所有的活动地图 time.sleep(1.5) if targMap == 'CB': return self.activityMapAdapter((1098, 438), 'CB-', (1061, 613), 'CB-EX-', 2, 2, 0, targStage, targMap) if targMap == 'MB': return self.activityMapAdapter((1140, 162), 'MB-', (1140, 295), 'MB-EX-', 2, 2, 1, targStage, targMap) if targMap == 'WR': return self.activityMapAdapter((1157, 598), 'WR-', (1030, 630), 'WR-EX-', 1.7, 1.7, 1.7, targStage, targMap) if targMap == 'OD': return self.activityMapAdapter((205, 317), 'OD-', (205, 397), 'OD-EX-', 2, 2, 1.5, targStage, targMap) if targMap == 'DM': return self.activityMapAdapter((789, 523), 'DM-', (870, 640), 'DM-EX-', 1.3, 1.5, 1.5, targStage, targMap) logger.error(f"当前地图'{targMap}'尚未适配进入模式,请在ArkMap中进行适配后再次运行") return False return True
def stopOperation(self, deduction=0, show=False): if self.in_operation: if show: logger.debug("强制结束作战(退出/非3星作战)") self.intellect -= deduction self.operation_cost = 0 self.in_operation = False return True
def fromMaptoMapMain(self, mapName: str = None): if mapName is None: # 自动定位当前位置 loc = self.__ark.getLocation(self.__ark.getScreenShot(), self.RECO, addon=True) logger.debug(f"获得当前定位于{loc}") if len(loc) == 0: if not self.toMain(): logger.error('无法定位当前位置且无法返回主界面!请检查!') # raise ValueError("cannot locate current location") return False else: self.__ark.clicker.mouse_click(923, 201, t=1.7) return True if loc[0] == 'map_atk': # 在攻击界面 但是不知道在哪个关卡的攻击界面 # 因此还需要返回后再次进行识别 self.__ark.clicker.mouse_click(86, 42, t=1) loc = self.__ark.getLocation(self.__ark.getScreenShot(), self.RECO, addon=True) logger.debug("在攻击界面中,返回后执行识别:" + str(loc)) if not loc: if not self.toMain(): logger.error("请切换至任一地图、主界面或地图主界面再试一次!") return None else: self.__ark.clicker.mouse_click(923, 201, t=1.7) return True if loc[0] == 'map_main': return True if loc[0] == 'main': self.__ark.clicker.mouse_click(923, 201, t=1.7) return True if loc[0] in ['map_ep', 'map_mat', 'map_exterm']: if 'map_exterm.inmap' in loc: self.__ark.clicker.mouse_click(86, 42, t=1.5) self.__ark.clicker.mouse_click(86, 42, t=1) self.__ark.clicker.mouse_click(80, 658, t=1) return True if loc[0] in ArkMap_ACTIVITY: self.__ark.clicker.mouse_click(86, 42, t=1) self.__ark.clicker.mouse_click(86, 42, t=1) self.__ark.clicker.mouse_click(80, 658, t=1) return True else: # TODO: 存在mapName 进行位置检测 # 否则需要有开始行动标志的界面介入 if mapName in ACTIVITY_MAP: # 需要额外返回上一级的特殊地图 self.__ark.clicker.mouse_click(86, 42, t=1.6) self.__ark.clicker.mouse_click(86, 42, t=1) self.__ark.clicker.mouse_click(86, 42, t=1) self.__ark.clicker.mouse_click(80, 658, t=1) return True
def delStage(self, stageName, times): if stageName not in self["list"]: return self max_atk = self["stages"][self["list"].index(stageName)]["max_atk"] if max_atk - times <= 0: logger.debug(f"删除Stage:{stageName}") _INDEX = self["list"].index(stageName) self["stages"].pop(_INDEX) self["list"].pop(_INDEX) else: self["stages"][self["list"].index(stageName)]["max_atk"] -= times return self
def update(self, filter_freq=200, filter_stages=[], url_stats='result/matrix?show_closed_zone=true', url_rules='formula', path_stats=MATRIX_FILE_DIR + '/data/matrix.json', path_rules=MATRIX_FILE_DIR + '/data/formula.json', force=True): """ To update parameters when probabilities change or new items added. Args: url_stats: string. url to the dropping rate stats data. url_rules: string. url to the composing rules data. path_stats: string. local path to the dropping rate stats data. path_rules: string. local path to the composing rules data. """ if os.path.isfile(f"{MATRIX_FILE_DIR}/data/matrix.json") and force: mtime = os.path.getmtime(f"{MATRIX_FILE_DIR}/data/matrix.json") logger.debug(f"planner MTime is {int(mtime)},creating backup folder") if not os.path.exists(f"{MATRIX_FILE_DIR}/data/b_{int(mtime)}/"): os.mkdir(f"{MATRIX_FILE_DIR}/data/b_{int(mtime)}/") for _ in os.listdir(f"{MATRIX_FILE_DIR}/data/"): if _ != ".gitkeep" and os.path.isfile(f"{MATRIX_FILE_DIR}/data/{_}"): shutil.move(f"{MATRIX_FILE_DIR}/data/{_}", f"{MATRIX_FILE_DIR}/data/b_{int(mtime)}/{_}") logger.debug("file backup successful") logger.notice(f'Start to update Planning data {time.asctime(time.localtime(time.time()))}.') if not force: # if not force to update, try loading data from file. try: material_probs, self.convertion_rules = load_data(path_stats, path_rules) except: # loading failed, try loading from server. force = True if force: # load from server. try: logger.common('Requesting data from web resources (i.e., penguin-stats.io)...') material_probs, self.convertion_rules = request_data(penguin_url + url_stats, penguin_url + url_rules, path_stats, path_rules) logger.common('done.') except: logger.error("Request data error.") return if filter_freq: filtered_probs = [] for drop in material_probs['matrix']: if drop['times'] >= filter_freq and drop['stageId'] not in filter_stages: filtered_probs.append(drop) material_probs['matrix'] = filtered_probs if self._pre_processing(material_probs, forceupdate=force) != -1: self._set_lp_parameters()
def getCost(self): from ArkTagData.TagGetter import getMapCost map_cost = getMapCost() cost = [0, 0] _INF = [False, False] for _ in self['stages']: _COTYPE, _COST = map_cost.where(_['stage']).split('_') _COTYPE = [0, 1][['ap', 'et'].index(_COTYPE)] _COST = int(_COST) * _['max_atk'] if _['max_atk'] == -1: _INF[_COTYPE] = True cost[_COTYPE] += _COST logger.debug(f"共计消耗{cost[0] if _INF[0] is False else 'INFINITE'}理智," f"{cost[1] if _INF[1] is False else 'INFINITE'}门票") return cost
def addLocate_Auto(self, name, imagePath, image, area=None, threshold=0.95, diff=5): assert self.checkMapExist(name), '地图不存在!' assert self.__gameTemp__.has(imagePath), '图片不存在!' template = self.__gameTemp__.getTemplate(imagePath)[0] max_thres, where = self.__gameTemp__.matchDifference(image, template, method=1) logger.debug("在(%d,%d)匹配到%s的最佳阈值%s" % (where[0], where[1], imagePath, max_thres)) if max_thres < threshold: logger.error("未找到最为匹配的源,请尝降低变最高阈值") return False y, x = int(where[0]), int(where[1]) h, w, depth = template.shape area = [x - 2, y - 2, w + 4, h + 4] if area is None else area self.getMap(name).get("detector") \ .addDetector(type_='I', imagePath=imagePath, threshold=max_thres - 0.07, locationarea=area, diff=diff) logger.notice( "成功在%s中添加关键图像%s[area=[%d,%d,%d,%d],threshold=%.2f]" % (name, imagePath, x, y, w, h, max_thres - 0.07)) return True
def dragTo(self, x1, y1, x2, y2, duration=None, r=None, f_back=5): if r is None: x1 += random.randint(*self.rx) x2 += random.randint(*self.rx) y1 += random.randint(*self.ry) y2 += random.randint(*self.ry) else: x1 += random.randint(*r[0]) x2 += random.randint(*r[0]) y1 += random.randint(*r[1]) y2 += random.randint(*r[1]) logger.debug("滑动初始坐标:({},{}) 结束坐标({} {})".format(x1, y1, x2, y2), backlevel=f_back) command = "input swipe {} {} {} {} ".format(x1, y1, x2, y2) if duration is not None: command += str(int(duration)) self.adb.run_device_cmd(command)
def mouse_click(self, x, y, rx=None, ry=None, t=0, at=0, condition=True): if condition: if rx is None: x += random.randint(*self.rx) else: x += random.randint(*rx) if ry is None: y += random.randint(*self.ry) else: y += random.randint(*ry) x, y = int(x), int(y) logger.debug("点击坐标:({},{})".format(x, y), backlevel=5) time_sleep(at) command = "input tap {} {}".format(x, y) self.adb.run_device_cmd(command) time_sleep(t) return condition
def locateImage(self, image, ranges=None, group=None, method=3, sub_area=None, addon=False, show_debug=False): _s = time.time() if group is not None: assert self.hasGroup(group) ranges = self.mapping.get("group").get(group) else: if isinstance(ranges, str): ranges = [ranges] ranges = ranges if ranges is not None else self.mapping.get("reflector").keys() result = {} for _ in ranges: result.update({_: self.mapping.get("map").get(_).get("detector") .detect(image, self.mapping.get("map").get(_).get('condition', ''), sub_area=sub_area, show=show_debug)}) if show_debug: logger.debug('Solve locating in %.3f secs' % (time.time() - _s)) if method == 3: ret = [] for name, v in result.items(): if v[1] is True: ret.append(name) # parse addon if not addon: return ret ret2 = ret.copy() for mapname in ret: for addon_name, v in self.getMap(mapname).get('addon').items(): nil, suc = v.get('detector').detect(image, v['condition'], sub_area=sub_area, show=show_debug) if suc: ret2.append(f"{mapname}.{addon_name}") return ret2 elif method == 0: return result elif method == 1: return {_: sum([i * k for n, i, k in result.get(_)[0]]) for _ in result} elif method == 2: return {_: sum([i * k for n, i, k in result.get(_)[0]]) / sum([i for n, i, k in result.get(_)[0]]) for _ in result} raise ValueError("unknown method for locateImage()")
def changeStage(self, currentStageName, targetStageName, currentMapName=None, targetMapName=None): currentMapName = self.getStageInfo( currentStageName)[0] if currentMapName is None else currentMapName targetMapName = self.getStageInfo( targetStageName)[0] if targetMapName is None else targetMapName if currentMapName != targetMapName: logger.debug(f"从{currentMapName}到主页") if not self.fromMaptoMapMain(currentMapName): logger.error("出现错误,停止。请检查") return False logger.debug(f"从主页到{targetMapName}") assert self.fromMapMainToMap(targetMapName) assert self.fromActivityMapMainToActivityMap( targetMapName, targetStageName) self.changeStageInMap(targetMapName, targetStageName) return True
def selectHandle(self, handle): if isinstance(handle, int): if handle >= 100: if handle in self.hwnds: logger.debug("Find handle:%d" % handle) self._selectGame = self.get_window_info(handle) return self else: logger.error("未找到该句柄对应的客户端!") self._selectGame = [-1, -1] return False elif handle <= 99: if handle <= self.hwnds.__len__() - 1: self._selectGame = [self.windows[handle], self.hwnds[handle]] return self else: logger.error("句柄超过全部句柄数量!") self._selectGame = [-1, -1] return False else: logger.error("expect type:int got %s instead" % type(handle))
def startOperation(self, cost, use_any_suply=False): if self.in_operation: logger.debug("正在作战中!") return True if self.have(cost): self.intellect -= 1 self.in_operation = True self.operation_cost = cost logger.debug(f"当前持有理智({self.intellect})>消耗({cost}),允许开始作战。") return True if use_any_suply: logger.debug("持有理智不足但允许使用回复,允许开始作战。") return True logger.debug(f"当前持有理智({self.intellect})<消耗({cost})且不允许使用回复,禁止开始作战。") return False
def writeConfig(self, filename=None, debug=False): if hasattr(self, 'data'): if self._fileopen == True: self.data.close() self._fileopen = False if self._filename is not None: f = open(self._filename, 'wb+') elif not filename: logger.error("<!>未指定配置文件") return False else: f = open(filename, 'wb+') f.write(self.magic.encode()) f.write(struct.pack('>f', self.version)) f.write(struct.pack('>?', False)) # 非空预设 if debug: logger.debug('<->--------WriteConfig-S------') for it in self.cfg.item: if self.cfg.get(it) != None: f.write(struct.pack('>h', it.__len__())) f.write(it.encode()) f.write(self.gettype(self.cfg.get(it))) f.write(self.encoder(self.cfg.get(it))) f.write(struct.pack('>?', False)) if self.cfg.folder == []: f.seek(f.tell() - 1) f.write(struct.pack('>?', True)) # 补充空集 if debug: logger.debug('<->-----------WriteConfig-E-----------') return True for fld in self.cfg.folder: f.write(struct.pack('>h', fld.__len__())) f.write(fld.encode()) f.write(self.gettype(self.cfg.get(fld))) ret = self._writeConfig(getattr(self.cfg, fld), f, fld, debug=debug) if debug: logger.debug('<->R%s' % ret) f.seek(f.tell() - 1) f.write(struct.pack('>?', True)) # 结束所有folder if debug: logger.debug('<->-----------WriteConfig-E-----------') return True
def _writeConfig(self, cfg, f, name, debug=False): f.write(struct.pack('>?', False)) # 非空预设 if debug: logger.debug('<->%s:i%s f%s' % (cfg._root, cfg.item, cfg.folder)) # TODO:如果folder和item都是空的 会出错 for it in cfg.item: if cfg.get(it) != None: f.write(struct.pack('>h', it.__len__())) f.write(it.encode()) f.write(self.gettype(cfg.get(it))) f.write(self.encoder(cfg.get(it))) f.write(struct.pack('>?', False)) if debug: logger.debug('<->[i]%s(%s): %s <%s>' % (it, it.__len__(), str(cfg.get(it)), str(type(cfg.get(it)))[8:-2:])) if cfg.folder == []: f.seek(f.tell() - 1) f.write(struct.pack('>?', True)) # 补充空集 return [True, 'emp_folder', cfg._root] for fld in cfg.folder: f.write(struct.pack('>h', fld.__len__())) f.write(fld.encode()) f.write(self.gettype(cfg.get(fld))) ret = self._writeConfig(getattr(cfg, fld), f, fld, debug) if debug: logger.debug('<->R%s' % ret) f.seek(f.tell() - 1) f.write(struct.pack('>?', True)) # 结束当前folder if cfg.item == []: if cfg.folder == []: return [True, 'emp_all', cfg._root] f.write(struct.pack('>?', True)) # item为空 补充结尾 return [True, 'emp_item', cfg._root] f.write(struct.pack('>?', True)) # item和folder都非空 补充结尾 return [True, 'full', cfg._root]
def readConfig(self, config=None, keepseek=False, inner=False, debug=False): # logger.system('<+>读取配置文件中...',flush=True) # TODO:处理空文件时异常:mrfzdata.mr ->list型数据 # F = Config(cfgdata=json.load(open('.\MRFZdata\ArknightsGameData\excel\mission_table.json','rb')),Magic='mrfzd',file='mrfzdata2.mr') # D = Config(Magic='mrfzd',file='mrfzdata.mr') # R = Config(Magic='test',file='testcfg.t') # seek在mission->guide_1->unlockParam 166 if config == None: config = self.cfg if self._fileopen: hand = self.data.tell() else: if hasattr(self, '_filename'): self.readFile(self._filename) hand = 0 else: logger.error('<!>无可读取内容!配置不存在') return False if self._cfgread: self.cfg = configTree() config = self.cfg if self.checkHeader(): self.version = struct.unpack('>f', self.readData(4))[0] if round(self.version, 1) < RootData.get("Config_version") and not inner: logger.error("<!>配置文件版本(%.1f)高于程序版本!无法读取" % self.version) return False elif round(self.version, 1) > RootData.get("Config_version") and not inner: logger.warning("<!>配置文件版本(%.1f)低于程序版本!保存时将使用当前版本存储配置!" % self.version) if keepseek: self.data.seek(hand) _ts = time.time() if not inner: if self.readfromType(bool): logger.error("<!>配置是空的!") return False if debug is True: logger.debug('<+>-----------ReadConfig-S-----------') while True: try: name_len = struct.unpack('>h', self.readData(2))[0] # 命名长度 name = self.readData(name_len).decode() type_ = self.type(self.readData(1)) if not type_ == configTree: # list? ---->item value = self.readfromType(type_) config.set(name, value) if debug is True: logger.debug('<+>[%s]%s.%s(%s): %s' % (self.data.tell(), config._root, name, name_len, str(type_)[8:-2:])) if self.readfromType(bool): if debug is True: logger.debug('<+>[%s][t1]end,exit' % (self.data.tell())) break else: # folder config.addChild(name) # -> # empty folder ? if self.readfromType(bool): if debug is True: logger.debug('<+>[%s][t2]end,exit' % (self.data.tell())) break # <- self.readConfig(getattr(config, name), keepseek=True, inner=True, debug=debug) if self.readfromType(bool): if debug is True: logger.debug('<+>[%s][t3]end,exit' % (self.data.tell())) break else: self.data.seek(self.data.tell() - 1) except: logger.error('<!>读取时出现错误 [seek:%s]' % self.data.tell()) # logger.system('<r>读取配置文件完成![用时%f秒]' % _ts) return False _ts = time.time() - _ts if not inner: self._cfgread = True if debug is True: logger.debug('<+>[%s]-----------ReadConfig-E-----------' % self.data.tell()) logger.system('<+>读取配置文件完成![用时%.2f毫秒]' % (_ts * 1000)) return True else: logger.error('<!>Magic Code不匹配!') return False
def endOperation(self): logger.debug("结束作战!") self.intellect = self.intellect - self.operation_cost + 1 self.operation_cost = 0 self.in_operation = False return True
def testCondition(self, name, image, sub_area=None): assert self.hasMap(name), '地图不存在!' condition = self.getMap(name).get("condition") logger.debug("current condition(of %s) is %s" % (name, condition)) detector = self.getMap(name).get("detector") ret, pass_ = detector.detect(image, condition, sub_area=sub_area) logger.debug("==================Detail==================") index = 0 for t, diff, val, skip in ret: if skip: msg = color.print.BLUE + "[SKIP]" + color.print.END else: msg = color.print.DARKGREEN + "[PASS]" + color.print.END if val < 0.2 else \ color.print.RED + "[FAIL]" + color.print.END msg += color.print.CYAN + t.replace("I", "[IMAGE]").replace("C", "[COLOR]") + color.print.END if t == 'C': msg += f"POS{str(detector[index]['pos'])},VALUE={val}" elif t == 'I': msg += f'[{detector[index]["image"]},thr={round(detector[index]["threshold"], 2)}]' msg += ',diff=%.1f' % diff logger.debug(msg) index += 1 logger.debug(color.print.GREEN + "condition pass through" + color.print.END) \ if pass_ else logger.debug(color.print.RED + "condition failed" + color.print.END) logger.debug("==================Detail==================")