def gen_input_hero(self, hero, rival_towers): if hero.state == 'out' or hero.hp <= 0: return list(np.zeros(13+3*19)) dis_rival = 10000 if len(rival_towers) > 0: dis_list = [StateUtil.cal_distance2(hero.pos, t.pos) for t in rival_towers] dis_rival = min(dis_list) hero_input = [self.normalize_value(int(hero.hero_name)), self.normalize_value(hero.pos.x), self.normalize_value(hero.pos.z), self.normalize_value(hero.speed), self.normalize_value(hero.att), # todo: 2 是普攻手长,现只适用于1,2号英雄,其他英雄可能手长不同 0.2, self.normalize_value(hero.mag), self.normalize_value(hero.hp), hero.hp/float(hero.maxhp), self.normalize_value(hero.mp), self.normalize_value(dis_rival), hero.team] is_enemy_visible = hero.is_enemy_visible() hero_input.append(int(is_enemy_visible)) skill_info1 = SkillUtil.get_skill_info(hero.cfg_id, 1) skill_info2 = SkillUtil.get_skill_info(hero.cfg_id, 2) skill_info3 = SkillUtil.get_skill_info(hero.cfg_id, 3) skill_input1 = self.gen_input_skill(skill_info1, hero.skills[1]) skill_input2 = self.gen_input_skill(skill_info2, hero.skills[2]) skill_input3 = self.gen_input_skill(skill_info3, hero.skills[3]) hero_input=hero_input+skill_input1+skill_input2+skill_input3 return hero_input
def gen_input_hero(hero, query_hero, revert=False): if hero is None or hero.state == 'out' or hero.hp <= 0: return list(np.zeros(20 + 3 * 23)) # 添加英雄基础信息 11个 hero_input = [ TeamBattleInput.normalize_value(hero.pos.x - query_hero.pos.x if not revert else -(hero.pos.x - query_hero.pos.x)), TeamBattleInput.normalize_value(hero.pos.z - query_hero.pos.z if not revert else -(hero.pos.z - query_hero.pos.z)), TeamBattleInput.normalize_value(hero.speed), # # todo: 2 是普攻手长,现只适用于1,2号英雄,其他英雄可能手长不同 # 0.2, TeamBattleInput.normalize_value(hero.hp), hero.hp / float(hero.maxhp), TeamBattleInput.normalize_value(hero.hprec), TeamBattleInput.normalize_value(hero.mp), TeamBattleInput.normalize_value(hero.mag), TeamBattleInput.normalize_value(hero.magpen), TeamBattleInput.normalize_value(hero.magpenrate), hero.team if not revert else 1 - hero.team ] # 添加物理攻击信息,预留5个攻击对象位,9个 hero_input.append(TeamBattleInput.normalize_value(hero.att)), hero_input.append(TeamBattleInput.normalize_value(hero.attspeed)), hero_input.append(TeamBattleInput.normalize_value(hero.attpen)), hero_input.append(TeamBattleInput.normalize_value(hero.attpenrate)), hero_input.append(0) hero_input.append(0) hero_input.append(0) hero_input.append(0) hero_input.append(0) # 添加技能信息 skill_info1 = SkillUtil.get_skill_info(hero.cfg_id, 1) skill_info2 = SkillUtil.get_skill_info(hero.cfg_id, 2) skill_info3 = SkillUtil.get_skill_info(hero.cfg_id, 3) skill_input1 = TeamBattleInput.gen_input_skill(skill_info1, hero.skills[1]) skill_input2 = TeamBattleInput.gen_input_skill(skill_info2, hero.skills[2]) skill_input3 = TeamBattleInput.gen_input_skill(skill_info3, hero.skills[3]) #TODO 添加历史信息,之前3帧,每次的位置,动作(移动,攻击,技能1-3),hitinfo,tgtpos hero_input = hero_input + skill_input1 + skill_input2 + skill_input3 return hero_input
def gen_input_hero(self, hero, query_hero, rival_towers, revert=False): if hero.state == 'out' or hero.hp <= 0: return list(np.zeros(16 + 3 * 17)) dis_rival = 10000 if len(rival_towers) > 0: dis_list = [ StateUtil.cal_distance2(hero.pos, t.pos) for t in rival_towers ] dis_rival = min(dis_list) hero_input = [ self.normalize_value(hero.pos.x - query_hero.pos.x if not revert else -( hero.pos.x - query_hero.pos.x)), self.normalize_value(hero.pos.z - query_hero.pos.z if not revert else -( hero.pos.z - query_hero.pos.z)), self.normalize_value(hero.speed), self.normalize_value(hero.att), self.normalize_value(hero.attspeed), self.normalize_value(hero.attpen), self.normalize_value(hero.attpenrate), # # todo: 2 是普攻手长,现只适用于1,2号英雄,其他英雄可能手长不同 # 0.2, self.normalize_value(hero.hp), hero.hp / float(hero.maxhp), self.normalize_value(hero.hprec), self.normalize_value(hero.mp), self.normalize_value(hero.mag), self.normalize_value(hero.magpen), self.normalize_value(hero.magpenrate), self.normalize_value(dis_rival), hero.team if not revert else 1 - hero.team ] # is_enemy_visible = hero.is_enemy_visible() # hero_input.append(int(is_enemy_visible)) skill_info1 = SkillUtil.get_skill_info(hero.cfg_id, 1) skill_info2 = SkillUtil.get_skill_info(hero.cfg_id, 2) skill_info3 = SkillUtil.get_skill_info(hero.cfg_id, 3) skill_input1 = self.gen_input_skill(skill_info1, hero.skills[1]) skill_input2 = self.gen_input_skill(skill_info2, hero.skills[2]) skill_input3 = self.gen_input_skill(skill_info3, hero.skills[3]) hero_input = hero_input + skill_input1 + skill_input2 + skill_input3 return hero_input
def gen_attack_cast_action_indic(hero_info, enemy_info, friends, opponents): recommmend_actions = [] tgt_idx = TeamBattleUtil.get_hero_index(enemy_info.hero_name) # 添加物理攻击 action_idx = TeamBattleUtil.get_action_index(tgt_idx, 0) recommmend_actions.append(action_idx) # 添加技能攻击 for skill_id in range(1, 4): skill_info = SkillUtil.get_skill_info(hero_info.cfg_id, skill_id) if skill_info.cast_target == SkillTargetEnum.rival: action_idx = TeamBattleUtil.get_action_index(tgt_idx, skill_id) recommmend_actions.append(action_idx) elif skill_info.cast_target == SkillTargetEnum.self: # 如果技能对象是自己,添加自己作为目标, # 注意,这里的合理性会由技能检查条件来覆盖 action_idx = TeamBattleUtil.get_action_index(0, skill_id) recommmend_actions.append(action_idx) return recommmend_actions, tgt_idx
def get_action_cmd(action_list, unaval_list, recommmend_list, state_info, hero_name, friends, opponents, revert=False): hero = state_info.get_hero(hero_name) found = False # 如果有推荐的行为,只从中挑选 if len(recommmend_list) > 0: for i in range(len(action_list)): if i not in recommmend_list: action_list[i] = -1 print("battle_id", state_info.battleid, "tick", state_info.tick, "hero", hero_name, "根据推荐,只从以下行为中挑选", ",".join(str("%f" % float(act)) for act in action_list), ",".join(str("%f" % float(act)) for act in recommmend_list)) while not found: max_q = max(action_list) if max_q <= -1: action = CmdAction(hero_name, CmdActionEnum.HOLD, None, None, hero.pos, None, None, 48, None) return action, max_q, -1 selected = action_list.index(max_q) avail_type = unaval_list[selected] if avail_type == -1: #TODO avail_type == 0: 是否考虑技能不可用时候不接近对方 # 不可用行为 action_list[selected] = -1 continue if selected < 8: # move fwd = StateUtil.mov(selected, revert) # 根据我们的移动公式计算一个目的地,缺点是这样可能被障碍物阻挡,同时可能真的可以移动距离比我们计算的长 tgtpos = TeamBattleUtil.set_move_target(hero, fwd) # tgtpos = PosStateInfo(hero.pos.x + fwd.x * 15, hero.pos.y + fwd.y * 15, hero.pos.z + fwd.z * 15) action = CmdAction(hero.hero_name, CmdActionEnum.MOVE, None, None, tgtpos, None, None, selected, None) return action, max_q, selected elif selected < 13: # 对敌英雄使用普攻 target_index = selected - 8 target_hero = TeamBattleUtil.get_target_hero( hero.hero_name, friends, opponents, target_index) target_hero_info = state_info.get_hero(target_hero) avail_type = unaval_list[selected] if avail_type == 0: action = CmdAction(hero.hero_name, CmdActionEnum.MOVE, None, None, target_hero_info.pos, None, None, selected, None) else: action = CmdAction(hero.hero_name, CmdActionEnum.ATTACK, 0, target_hero, None, None, None, selected, None) return action, max_q, selected elif selected < 28: # skill skillid = int((selected - 13) / 5 + 1) tgt_index = selected - 13 - (skillid - 1) * 5 skill_info = SkillUtil.get_skill_info(hero.cfg_id, skillid) is_buff = True if skill_info.cast_target == SkillTargetEnum.buff else False is_self = True if skill_info.cast_target == SkillTargetEnum.self else False tgt_hero = TeamBattleUtil.get_target_hero( hero.hero_name, friends, opponents, tgt_index, is_buff, is_self) tgt_pos = state_info.get_hero(tgt_hero).pos fwd = tgt_pos.fwd(hero.pos) avail_type = unaval_list[selected] if avail_type == 0: action = CmdAction(hero.hero_name, CmdActionEnum.MOVE, None, None, tgt_pos, None, None, selected, None) else: action = CmdAction(hero.hero_name, CmdActionEnum.CAST, skillid, tgt_hero, tgt_pos, fwd, None, selected, None) return action, max_q, selected
def list_unaval_actions(act_size, state_info, hero_name, team_battle_heros, battle_range, debug=False): friends, opponents = TeamBattleUtil.get_friend_opponent_heros( team_battle_heros, hero_name) avail_list = [-1] * act_size for i in range(act_size): hero = state_info.get_hero(hero_name) selected = i if selected < 8: # move # 不再检查movelock,因为攻击硬直也会造成这个值变成false(false表示不能移动) # 屏蔽会离开战圈的移动 fwd = StateUtil.mov(selected) move_pos = TeamBattleUtil.play_move(hero, fwd) in_range = TeamBattleTrainer.in_battle_range( move_pos, battle_range) if in_range != -1: avail_list[selected] = -1 else: avail_list[selected] = 1 continue elif selected < 13: # 物理攻击:五个攻击目标 target_index = selected - 8 target_hero = TeamBattleUtil.get_target_hero( hero_name, friends, opponents, target_index) if target_hero is None: avail_list[selected] = -1 if debug: print("找不到对应目标英雄") continue rival_info = state_info.get_hero(target_hero) dist = StateUtil.cal_distance(hero.pos, rival_info.pos) # 英雄不可见 if not rival_info.is_enemy_visible(): avail_list[selected] = -1 if debug: print("英雄不可见") continue # 英雄太远,放弃普攻 # if dist > self.att_dist: if dist > StateUtil.ATTACK_HERO_RADIUS: avail_list[selected] = 0 if debug: print("英雄太远,放弃普攻") continue # 对方英雄死亡时候忽略这个目标 elif rival_info.hp <= 0: avail_list[selected] = -1 if debug: print("对方英雄死亡") continue avail_list[selected] = 1 elif selected < 28: # skill1 # TODO 处理持续施法,目前似乎暂时还不需要 skillid = int((selected - 13) / 5 + 1) if hero.skills[skillid].canuse != True: # 被沉默,被控制住(击晕击飞冻结等)或者未学会技能 avail_list[selected] = -1 if debug: print("技能受限,放弃施法" + str(skillid) + " hero.skills[x].canuse=" + str(hero.skills[skillid].canuse) + " tick=" + str(state_info.tick)) continue if hero.skills[skillid].cost is not None and hero.skills[ skillid].cost > hero.mp: # mp不足 # 特殊情况,德古拉1,2技能是扣除血量 if not (hero.cfg_id == '103' and (skillid == 1 or skillid == 2)): avail_list[selected] = -1 if debug: print("mp不足,放弃施法" + str(skillid)) continue if hero.skills[skillid].cd > 0: # 技能未冷却 avail_list[selected] = -1 if debug: print("技能cd中,放弃施法" + str(skillid)) continue tgt_index = selected - 13 - (skillid - 1) * 5 skill_info = SkillUtil.get_skill_info(hero.cfg_id, skillid) # TODO 这个buff逻辑还没有测试对应的英雄 is_buff = True if skill_info.cast_target == SkillTargetEnum.buff else False is_self = True if skill_info.cast_target == SkillTargetEnum.self else False tgt_hero = TeamBattleUtil.get_target_hero( hero.hero_name, friends, opponents, tgt_index, is_buff, is_self) if tgt_hero is None: avail_list[selected] = -1 if debug: print("找不到对应目标英雄") continue [tgtid, tgtpos] = TeamBattleTrainer.choose_skill_target( tgt_index, state_info, skill_info, hero_name, hero.pos, tgt_hero, debug) if tgtid == -1 or tgtid == 0: avail_list[selected] = tgtid if debug: print("目标不符合施法要求") continue else: # 根据规则再去过滤 policy_avail = TeamBattlePolicy.check_skill_condition( skill_info, state_info, hero, tgt_hero, friends, opponents) if not policy_avail: avail_list[selected] == -1 else: avail_list[selected] = 1 return avail_list
def guess_player_action(prev_state_info, state_info, next_state_info, next_next_state_info, hero_name, rival_hero_name): #针对每一帧,结合后一帧信息,判断英雄在该帧的有效操作 #仅对于一对一线上模型有效 #技能>攻击>走位 #技能:检查cd和mp变化,hitstateinfo,attackstateinfo,dmgstateinifo,回推pos,fwd,tgt,selected #攻击:检查hit,damage,attack #检查pos变化 prev_hero = prev_state_info.get_hero(hero_name) prev_viral_hero = prev_state_info.get_hero(rival_hero_name) current_hero = state_info.get_hero(hero_name) hero_attack_info = state_info.get_hero_attack_info(hero_name) if hero_attack_info is not None: skill = hero_attack_info.skill # 看十位来决定技能id skillid = int(hero_attack_info.skill % 100 / 10) tgtid = int(hero_attack_info.defer) if ( hero_attack_info.defer is not None and hero_attack_info.defer != 'None') else 0 tgtpos = hero_attack_info.tgtpos # 回城 if hero_attack_info.skill == 10000: action = CmdAction(hero_name, CmdActionEnum.CAST, 6, None, None, None, None, 49, None) return action # 普攻,不会以自己为目标 output_idx = None if skillid == 0: # 打塔 if StateUtil.if_unit_tower(tgtid): output_idx = 8 # 普通攻击敌方英雄 elif tgtid == prev_viral_hero.hero_name: # 普通攻击敌方英雄 output_idx = 9 # 普通攻击敌方小兵 elif tgtid != 0: creeps = StateUtil.get_nearby_enemy_units( prev_state_info, hero_name) n = len(creeps) for i in range(n): if creeps[i].unit_name == str(tgtid): output_idx = i + 10 # attacinfo里没有目标,从hit里找目标 elif tgtid == 0: # hitinfo 和 dmginfo都有延迟,尤其是超远距离的攻击技能 hit_infos = state_info.get_hero_hit_with_skill( hero_name, skill) hit_infos.extend( next_state_info.get_hero_hit_with_skill( hero_name, skill)) if len(hit_infos) > 0: # 首先检查是否敌方英雄被击中,这种优先级最高 if rival_hero_name in [hit.tgt for hit in hit_infos]: output_idx = 9 else: # 找到被攻击者中血量最少的,认为是目标对象 tgtid_list = [ state_info.get_obj(hit.tgt) for hit in hit_infos ] tgt_unit = min(tgtid_list, key=lambda x: x.hp) if StateUtil.if_unit_tower(tgt_unit.unit_name): output_idx = 8 else: # 从英雄附近的小兵中,检索它的编号 # 注:极端情况下有可能丢失,比如在这0.5秒钟内,英雄接近了小兵并进行了攻击 # 扩大搜索的范围 creeps = StateUtil.get_nearby_enemy_units( prev_state_info, hero_name, max_distance=StateUtil.ATTACK_HERO_RADIUS + 2) for i in range(len(creeps)): if creeps[i].unit_name == tgtid: output_idx = i + 10 if output_idx is not None: action = CmdAction(hero_name, CmdActionEnum.ATTACK, 0, tgtid, tgtpos, None, None, output_idx, None) return action # 使用技能,不考虑以敌方塔为目标(若真以敌方塔为目标则暂时先不管吧,现在的两个英雄技能都对建筑无效) # TODO 暂时忽略技能为方向/范围型并且放空的情况(部分技能无任何目标,tgt为0)。这种情况下应该会有个pos记录释放点,后续可以考虑如何学习 else: # 对自身施法 if tgtid == int( hero_name ): # or (tgtid=='0' and Replayer.skill_tag[skillid]==1): tgtpos = prev_hero.pos output_idx = 8 + skillid * 10 # 对敌方英雄施法 elif tgtid == int(rival_hero_name): tgtpos = prev_viral_hero.pos output_idx = 9 + skillid * 10 # 对小兵施法 elif tgtid != 0 and not StateUtil.if_unit_tower(tgtid): creeps = StateUtil.get_nearby_enemy_units( prev_state_info, hero_name) n = len(creeps) for i in range(n): if creeps[i].unit_name == str(tgtid): output_idx = i + skillid * 10 + 10 # attacinfo里没有目标,从hit里找目标 elif tgtid == 0: # 远程技能的伤害延迟可能会比较长 hit_infos = state_info.get_hero_hit_with_skill( hero_name, skill) hit_infos.extend( next_state_info.get_hero_hit_with_skill( hero_name, skill)) hit_infos.extend( next_next_state_info.get_hero_hit_with_skill( hero_name, skill)) if len(hit_infos) > 0: # 首先检查是否敌方英雄被击中,这种优先级最高 if rival_hero_name in [hit.tgt for hit in hit_infos]: tgtid = rival_hero_name output_idx = 9 + skillid * 10 else: # 找到被攻击者中血量最少的,认为是目标对象 tgtid_list = [ state_info.get_obj(hit.tgt) for hit in hit_infos ] tgt_unit = min(tgtid_list, key=lambda x: x.hp) # 从英雄附近的小兵中,检索它的编号 # 注:极端情况下有可能丢失,比如在这0.5秒钟内,英雄接近了小兵并进行了攻击 creeps = StateUtil.get_nearby_enemy_units( prev_state_info, hero_name) for i in range(len(creeps)): if creeps[i].unit_name == tgt_unit.unit_name: tgtid = creeps[i].unit_name output_idx = i + 10 + skillid * 10 # 组装结果 if output_idx is not None: action = CmdAction(hero_name, CmdActionEnum.CAST, skillid, tgtid, tgtpos, None, None, output_idx, None) return action # 任然没有hit,技能空放 if tgtid == 0: # attackinfo里没有攻击目标id,只有坐标,根据位置找最近的目标作为输出 if tgtpos != None: search_radius = 1 # 首先寻找目标为对方英雄, 目前,如果在范围内有敌人英雄,选第一个作为主目标 nearby_rival_heros = StateUtil.get_nearby_enemy_heros( prev_state_info, hero_name, search_radius) if len(nearby_rival_heros) > 0: tgtid = nearby_rival_heros[0].hero_name output_idx = 9 + skillid * 10 else: # 其次检查是否可以释放给自己 skill_info = SkillUtil.get_skill_info( prev_hero.cfg_id, skillid) if skill_info is not None: if skill_info.cast_target != SkillTargetEnum.rival: tgtid = hero_name output_idx = 8 + skillid * 10 # 最后检查是否可以释放给小兵 else: nearby_soldiers = StateUtil.get_nearby_enemy_units( prev_state_info, hero_name, search_radius) if len(nearby_soldiers) > 0: target_unit = min(nearby_soldiers, key=lambda u: u.hp) for i in range(len(nearby_soldiers)): if nearby_soldiers[ i].unit_name == target_unit.unit_name: tgtid = nearby_soldiers[ i].unit_name output_idx = i + 10 + skillid * 10 # 组装结果 if output_idx is not None: action = CmdAction(hero_name, CmdActionEnum.CAST, skillid, tgtid, tgtpos, None, None, output_idx, None) return action else: # 真的技能空放了 action = CmdAction(hero_name, CmdActionEnum.HOLD, None, None, prev_hero.pos, None, None, 48, None) return action action = CmdAction(hero_name, CmdActionEnum.HOLD, None, None, prev_hero.pos, None, None, 48, None) return action # 没有角色进行攻击或使用技能,英雄在移动或hold if current_hero.pos.x != prev_hero.pos.x or current_hero.pos.z != prev_hero.pos.z or current_hero.pos.y != prev_hero.pos.y: # 移动 fwd = current_hero.pos.fwd(prev_hero.pos) [fwd, output_index] = Replayer.get_closest_fwd(fwd) action = CmdAction(hero_name, CmdActionEnum.MOVE, None, None, None, fwd, None, output_index, None) return action else: # hold action = CmdAction(hero_name, CmdActionEnum.HOLD, None, None, prev_hero.pos, None, None, 48, None) return action
def choose_skill_target(selected, stateinformation, skill, hero_name, pos, rival_hero, debug=False): hero_info = stateinformation.get_hero(hero_name) skill_info = SkillUtil.get_skill_info(hero_info.cfg_id, skill) if selected == 0: # 施法目标为自己 # 首先判断施法目标是不是只限于敌方英雄 if skill_info.cast_target == SkillTargetEnum.rival: return [-1, None] tgtid = hero_name # TODO 这里有点问题,如果是目标是自己的技能,是不是要区分下目的,否则fwd计算会出现问题 tgtpos = None elif selected == 1: # 攻击对方英雄 # 首先判断施法目标是不是只限于自己 if skill_info.cast_target == SkillTargetEnum.self: return [-1, None] rival = stateinformation.get_hero(rival_hero) if not rival.is_enemy_visible(): if debug: print("敌方英雄不可见") tgtid = -1 tgtpos = None elif StateUtil.cal_distance(rival.pos, pos) > skill_info.cast_distance: if debug: print("技能攻击不到对方 %s %s %s" % (rival_hero, StateUtil.cal_distance( rival.pos, pos), skill_info.cast_distance)) tgtid = -1 tgtpos = None # 对方英雄死亡时候忽略这个目标 elif rival.hp <= 0: if debug: print("技能攻击不了对方,对方已经死亡") tgtid = -1 tgtpos = None else: tgtid = rival_hero tgtpos = rival.pos else: # 对敌方小兵施法 if skill_info.cast_target == SkillTargetEnum.self: return [-1, None] # 寻找合适的小兵作为目标 creeps = StateUtil.get_nearby_enemy_units(stateinformation, hero_name) n = selected - 2 if n >= len(creeps): # 没有这么多小兵 if debug: print("技能不能攻击,没有指定的小兵") return [-1, None] elif not creeps[n].is_enemy_visible(): if debug: print("敌方小兵不可见") tgtid = -1 tgtpos = None elif StateUtil.cal_distance( pos, creeps[n].pos) > skill_info.cast_distance: if debug: print("技能不能攻击,小兵距离过远") tgtid = -1 tgtpos = None elif creeps[n].hp <= 0: if debug: print("技能不能攻击,小兵已经死亡") tgtid = -1 tgtpos = None else: tgtid = creeps[n].unit_name tgtpos = creeps[n].pos return [tgtid, tgtpos]