def try_solve(craft: Craft, line_to_finish, timeLimit=None): # 一个传统的bfs best = (craft, []) # 目前最佳项 第一个坑是数据,第二个是历史 queue = [(craft, [])] # 待办事项 start = time.perf_counter() # 开始的计时 last_print = start # 最后一次打印“临时方案”的时间 while queue: # 还有待办事项就继续 if timeLimit is not None and time.perf_counter() - start > timeLimit: return best # 超时了就直接返回 if time.perf_counter() > last_print + 1: # 打印一下临时方案证明不是程序炸了 last_print = time.perf_counter() debug( "solver bfs", "plan in {:.2f}s:{}({})".format(last_print - start, best[1], best[0].current_quality)) t_craft, t_history = queue.pop(0) # 获取一个待办事项 for skill in allowSkills(t_craft): # 获取可用的技能 try: tt_craft = t_craft.clone() tt_craft.use_skill(skill, True) except CheckUnpass: continue if tt_craft.current_cp < cpReq: continue tt_craft.status = DEFAULT_STATUS() new_data = (tt_craft, t_history + [skill] ) # 往上一坨都是模拟使用技能然后组成一个新的事项 if tt_craft.current_durability >= durReq and tt_craft.current_quality > best[ 0].current_quality: if best[0].current_quality >= line_to_finish: return new_data # 如果高于斩杀线就直接返回 best = new_data # 如果品质高点就存best queue.append(new_data) # 把数据塞进去待办事项 return best
def try_solve(craft: Craft, timeLimit=None): best = (craft, []) queue = [(craft, [])] start = time.perf_counter() last_print = start while queue: if timeLimit is not None and time.perf_counter() - start > timeLimit: return best if time.perf_counter() > last_print + 1: last_print = time.perf_counter() debug( "solver bfs", "plan in {:.2f}s:{}({})".format(last_print - start, best[1], best[0].current_quality), ) t_craft, t_history = queue.pop(0) for skill in allowSkills(t_craft): try: tt_craft = t_craft.clone() tt_craft.use_skill(skill, True) except Exception: continue if tt_craft.current_cp < cpReq: continue tt_craft.status = DEFAULT_STATUS() new_data = (tt_craft, t_history + [skill]) if (tt_craft.current_durability >= durReq and tt_craft.current_quality > best[0].current_quality): best = new_data queue.append(new_data) return best
def is_finished(self, craft, prev_skill=None): if not bool(self.queue) or craft.status.name in SpecialStatus: start = time.perf_counter() ans = try_solve(craft, 8) if ans is not None: self.queue = ans[1] debug( "solver dfs", "new plan in {:.2f}s:{}({})".format( time.perf_counter() - start, self.queue, ans[0].current_quality)) return not bool(self.queue)
def is_finished(self, craft, prev_skill=None): if craft.current_quality >= craft.recipe.max_quality: return True if self.is_first: debug("solver astar", "try to solve:\n%s" % craft.simple_str()) self.is_first = False if not bool( self.queue ) or craft.status.name in SpecialStatus or prev_skill != self.prev_skill: start = time.perf_counter() ans = try_solve(craft, TIME_LIMIT) if ans[1]: debug( "solver astar", "new plan in {:.2f}s:{}({})".format( time.perf_counter() - start, ans[1], ans[0].current_quality)) self.queue = ans[ 1] # if time.perf_counter() - start < TIME_LIMIT else ans[1][:4] return not bool(self.queue)
def is_finished(self, craft, prev_skill=None): if self.line_to_finish is None: temp = Craft(craft.recipe, craft.player) temp.add_effect('内静', 11) temp.merge_effects() for s in ['阔步', '改革', '观察', '注视加工', '阔步', '比尔格的祝福', '制作']: temp.use_skill(s) self.line_to_finish = temp.recipe.max_quality - temp.current_quality debug("solver bfs", f"the line to finish is {self.line_to_finish}") if not bool( self.queue ) or craft.status.name in SpecialStatus or prev_skill != self.prev_skill: start = time.perf_counter() ans = try_solve(craft, self.line_to_finish, 8) if ans[1]: self.queue = ans[1] debug( "solver bfs", "new plan in {:.2f}s:{}({})".format( time.perf_counter() - start, self.queue, ans[0].current_quality)) return len( self.queue ) < 3 or craft.current_quality >= self.line_to_finish # 这里是为了配合后续astar,让它有一点调整空间,同时如果高于斩杀线就直接下一步
def try_solve(craft: Craft, timeLimit=None): best = (craft, []) best_un_finish = None best_start_with = None best_start_with_l = 0 queue = [(craft, [])] start = time.perf_counter() last_print = start while queue: if timeLimit is not None and time.perf_counter() - start > timeLimit: return best if time.perf_counter() > last_print + 1: last_print = time.perf_counter() debug( "solver astar", "plan in {:.2f}s:{}({})".format(last_print - start, best[1], best[0].current_quality)) t_craft, t_history = queue.pop(0) if best_start_with is not None and len( t_history ) > 8 and t_history[:best_start_with_l] != best_start_with: continue skills = allowSkills(t_craft) for skill in skills: try: try: tt_craft = t_craft.clone() tt_craft.use_skill(skill, True) except CheckUnpass: continue except Exception: debug("solver astar", 'error at testing skill %s:\n%s' % (skill, t_craft)) debug("solver astar", format_exc()) continue tt_craft.status = DEFAULT_STATUS() new_data = (tt_craft, t_history + [skill]) if tt_craft.current_durability >= 1 and tt_craft.current_quality > best[ 0].current_quality: if tt_craft.current_quality >= tt_craft.recipe.max_quality: return new_data best = new_data if skill != "比尔格的祝福": if tt_craft.current_durability >= 1 and ( best_un_finish is None or tt_craft.current_quality > best_un_finish[0].current_quality): best_un_finish = new_data l = len(new_data[1]) c = (l // 4) + 1 if l > 9: best_start_with = new_data[1][:c] best_start_with_l = c queue.append(new_data) return best
def global_cool_down_ability( self, data: LogicData ) -> Optional[Union[UseAbility, UseItem, UseCommon]]: if data.config.query_skill: return data.config.get_query_skill() # 队列技能 res = res_lv(data) lv = data.me.level dis = data.target_distance min_mana = min(data.gauge.white_mana, data.gauge.black_mana) max_mana = max(data.gauge.white_mana, data.gauge.black_mana) use_white = data.gauge.white_mana < data.gauge.black_mana has_swift = 1249 in data.effects or 167 in data.effects swift_res_target = search_swift_res(data) if data.target is None and swift_res_target is not None and data.me.currentMP >= ( 2400 if has_swift else 3100): if has_swift: return UseAbility(7523, swift_res_target.id) elif not data.is_moving: return UseAbility(7514, data.me.id) elif not data[7561]: return UseAbility(7561) if dis > 25 or not data.valid_enemies: return if data.me.level >= 52 and count_enemy(data, 2) > 2 and min_mana >= ( 90 if not res else 20 if data[7521] else 50): return UseAbility(7513) # 人数够了就画圆 "处理魔连击开始" if data.combo_id == 7504 and lv >= 35: return UseAbility(7512) if dis < 4 else None elif data.combo_id == 7512 and lv >= 50: return UseAbility(7516) if dis < 4 else None elif data.combo_id == 7529 and lv >= 68: if lv < 70: return UseAbility(7525) if data.gauge.white_mana == data.gauge.black_mana: if 1234 in data.effects: return UseAbility(7526) if 1235 in data.effects: return UseAbility(7525) return UseAbility(7526 if use_white else 7525) elif (data.combo_id == 7525 or data.combo_id == 7526) and lv >= 80: return UseAbility(16530) "处理魔连击结束" if min_mana >= 5: # 续斩处理溢出魔元、走位 if max_mana >= (90 if res else 97) and dis > 10: return UseAbility(16529) # if res and data.is_moving and not has_swift: # if data.gcd: # return None # else: # return UseAbility(16529) # 续斩处理溢出魔元、走位 if (lv < 2 or res and min_mana >= (80 if lv >= 50 else 55 if lv >= 35 else 30)) and dis < 4: return UseAbility(7516) # 魔回刺、判断是否适合开始魔连击 cnt = count_enemy(data, 0) if has_swift: # 有瞬发 if swift_res_target is not None and data.me.currentMP >= 2400: debug('swift_res', swift_res_target.Name, hex(swift_res_target.id)) return UseAbility(7523, swift_res_target.id) if lv >= 15 and cnt > (1 if lv >= 66 else 2): return UseAbility(7509) # aoe 散碎、冲击 if lv >= 4: return UseAbility( 7507 if use_white and lv >= 10 else 7505) # 闪雷、疾风 if not data.is_moving and lv >= 2: if cnt > 2 and lv >= 18: return UseAbility(16525 if use_white and lv >= 22 else 16524) if use_white: if 1234 in data.effects: return UseAbility(7510) if 1235 in data.effects: return UseAbility(7511) else: if 1235 in data.effects: return UseAbility(7511) if 1234 in data.effects: return UseAbility(7510) return UseAbility(7503)