def keep_away_from(state_info, hero_info, rival_hero_info, action_ratios, danger_pos, danger_radius): changed = False maxQ = max(action_ratios) selected = action_ratios.index(maxQ) if maxQ == -1: return action_ratios for selected in range(len(action_ratios)): if action_ratios[selected] == -1: continue if selected < 8: fwd = StateUtil.mov(selected) tgtpos = PosStateInfo(hero_info.pos.x + fwd.x * 0.5, hero_info.pos.y + fwd.y * 0.5, hero_info.pos.z + fwd.z * 0.5) if StateUtil.cal_distance(tgtpos, danger_pos) <= danger_radius: print('策略选择', state_info.battleid, hero_info.hero_name, '移动方向会进入危险区域', hero_info.pos.to_string(), tgtpos.to_string()) action_ratios[selected] = -1 elif selected < 18: # 对敌英雄,塔,敌小兵1~8使用普攻, 针对近战英雄的检测 if selected == 8: # 敌方塔 print('策略选择', state_info.battleid, hero_info.hero_name, '不要去攻击塔') action_ratios[selected] = -1 elif selected == 9: # 敌方英雄 if StateUtil.cal_distance(rival_hero_info.pos, danger_pos) <= danger_radius: print('策略选择', state_info.battleid, hero_info.hero_name, '不要去近身攻击塔范围内的英雄') action_ratios[selected] = -1 else: # 小兵 creeps = StateUtil.get_nearby_enemy_units( state_info, hero_info.hero_name) n = selected - 10 tgt = creeps[n] if StateUtil.cal_distance(tgt.pos, danger_pos) <= danger_radius: print('策略选择', state_info.battleid, hero_info.hero_name, '不要去近身攻击塔范围内的小兵') action_ratios[selected] = -1 elif hero_info.cfg_id == '101' and 28 <= selected < 38: # 专门针对查尔斯的跳跃技能 skillid = int((selected - 18) / 10 + 1) [tgtid, tgtpos] = LineModel.choose_skill_target( selected - 18 - (skillid - 1) * 10, state_info, skillid, hero_info.hero_name, hero_info.pos, rival_hero_info.hero_name) if tgtpos is not None: if StateUtil.cal_distance(tgtpos, danger_pos) <= danger_radius: print('策略选择', state_info.battleid, hero_info.hero_name, '跳跃技能在朝着塔下的目标') action_ratios[selected] = -1 return action_ratios
def play_unit_action(state_info, unit, hero_info, hero_action, near_enemy_units): #TODO 需要界定攻击英雄的小兵的范围 # 最高优先级:如果英雄攻击了对方英雄,周围小兵会优先攻击英雄 # 考虑攻击范围,当前默认都使用塔的攻击范围 if PlayEngine.if_hero_attack_opponent(hero_action) \ and StateUtil.cal_distance(hero_info.pos, unit.pos) <= StateUtil.TOWER_ATTACK_RADIUS: state_info = PlayEngine.play_attack(state_info, unit.unit_name, hero_action.hero_name) # 根据attack info来执行动作 else: find_new_tgt = False att = state_info.get_attack_info(unit.unit_name) if att is not None: # 判断被攻击对象是否已经挂了 defender = state_info.get_unit(att.defer) if defender is None or defender.hp <= 0: find_new_tgt = True else: state_info = PlayEngine.play_attack( state_info, unit.unit_name, att.defer) # 如果丢失对象,攻击最近的敌人 # 为了节省计算量,我们只从英雄附近的小兵中寻找被攻击者,或者是英雄 if att is None or find_new_tgt: tgt = PlayEngine.find_next_tgt(state_info, unit, near_enemy_units) if tgt is not None: state_info = PlayEngine.play_attack( state_info, unit.unit_name, tgt) return state_info
def units_in_tower_range(units, target_pos): num = 0 for unit in units: if StateUtil.cal_distance( unit.pos, target_pos) <= StateUtil.TOWER_ATTACK_RADIUS: num += 1 return num
def choose_skill_target(selected, state_info, skill_info, hero_name, pos, tgt_hero_name, debug=False): hero_info = state_info.get_hero(hero_name) if selected == 0: # 施法目标为自己 # 首先判断施法目标是不是只限于敌方英雄 if skill_info.cast_target == SkillTargetEnum.self and hero_name != str( tgt_hero_name): if debug: print("施法目标为self,但是对象不是自己") return [-1, None] tgtid = hero_name # TODO 这里有点问题,如果是目标是自己的技能,是不是要区分下目的,否则fwd计算会出现问题 tgtpos = None if selected <= 4: # 攻击对方英雄 tgt_hero = state_info.get_hero(tgt_hero_name) if tgt_hero.team != hero_info.team and not tgt_hero.is_enemy_visible( ): if debug: print("敌方英雄不可见") tgtid = -1 tgtpos = None elif StateUtil.cal_distance(tgt_hero.pos, pos) > skill_info.cast_distance: if debug: print("技能攻击不到对方 %s %s %s" % (tgt_hero_name, StateUtil.cal_distance( tgt_hero.pos, pos), skill_info.cast_distance)) tgtid = 0 tgtpos = None # 对方英雄死亡时候忽略这个目标 elif tgt_hero.hp <= 0: if debug: print("技能攻击不了对方,对方已经死亡") tgtid = -1 tgtpos = None else: tgtid = tgt_hero_name tgtpos = tgt_hero.pos return tgtid, tgtpos
def use_skill3_correctly(state_info, hero_info, rival_hero_info, rival_near_units, action_ratios): if hero_info.cfg_id == '101': rival_hero_dis = StateUtil.cal_distance(hero_info.pos, rival_hero_info.pos) if rival_hero_dis > LineTrainerPolicy.SKILL_RANGE_CHAERSI_SKILL3: if LineTrainerPolicy.units_in_range( rival_near_units, hero_info.pos, LineTrainerPolicy.SKILL_RANGE_CHAERSI_SKILL3) == 0: print('策略选择', state_info.battleid, hero_info.hero_name, '查尔斯大招不应该在没人的时候使用') for i in range(38, 48): action_ratios[i] = -1 return action_ratios
def get_attack_tower_action(hero_name, hero_info, tower_unit): # 因为目前模型中侦测塔的范围较大,可能出现攻击不到塔的情况 # 所以需要先接近塔 # 使用tgtpos,而不是fwd。move命令中fwd坐标系比较奇怪 if StateUtil.cal_distance( hero_info.pos, tower_unit.pos) > StateUtil.ATTACK_UNIT_RADIUS: fwd = tower_unit.pos.fwd(hero_info.pos) [fwd, output_index] = Replayer.get_closest_fwd(fwd) tgtpos = PosStateInfo(hero_info.pos.x + fwd.x * 15, hero_info.pos.y + fwd.y * 15, hero_info.pos.z + fwd.z * 15) print("朝塔移动,", hero_name, "hero_pos", hero_info.pos.to_string(), "tower_pos", tower_unit.pos.to_string(), "fwd", fwd.to_string(), "output_index", output_index) action = CmdAction(hero_name, CmdActionEnum.MOVE, None, None, tgtpos, None, None, output_index, None) else: action_idx = 11 action = CmdAction(hero_name, CmdActionEnum.ATTACK, 0, tower_unit.unit_name, None, None, None, action_idx, None) return action
def policy_attack_rival_unit(hero_info, rival_hero_info, state_info, hero_name, rival_near_units, rival_near_tower, near_friend_units): # 如果附近没有敌方英雄,而且不在塔下,且有己方小兵 # 攻击敌方小兵 if (rival_hero_info.hp <= 0 or StateUtil.cal_distance(hero_info.pos, rival_hero_info.pos) >= LineTrainerPolicy.SAFE_RIVAL_HERO_DISTANCE) and \ rival_near_tower is None and len(near_friend_units) > 0: # 优先攻击快没有血的 for unit in rival_near_units: if unit.hp <= hero_info.att - 20: action = LineTrainerPolicy.get_attack_unit_action( state_info, hero_name, unit.unit_name, 0) print("启动策略 如果附近没有敌方英雄,而且不在塔下,补兵 " + hero_name) return action # 如果敌方小兵在攻击自己,后撤到己方的小兵后面 for unit in rival_near_units: att = state_info.if_unit_attack_hero(unit.unit_name, hero_name) if att is not None: # 优先物理攻击 retreat = LineTrainerPolicy.policy_move_retreat(hero_info) print("启动策略 被小兵攻击的情况下后撤 " + hero_name) return retreat # 物理攻击,不攻击血量较少的,留给补刀 # 选择距离较近的(离己方塔) rival_near_units_sorted = list(rival_near_units) basement_pos = StateUtil.get_basement(hero_info) rival_near_units_sorted.sort( key=lambda u: math.fabs(basement_pos.x - u.pos.x), reverse=False) for unit in rival_near_units_sorted: if unit.hp > hero_info.att * 3: action = LineTrainerPolicy.get_attack_unit_action( state_info, hero_name, unit.unit_name, 0) print("启动策略 如果附近没有敌方英雄,而且不在塔下,攻击敌方小兵 " + hero_name) return action return None
def choose_action(state_info, action_ratios, hero_name, rival_hero, rival_near_units, near_friend_units): hero_info = state_info.get_hero(hero_name) rival_hero_info = state_info.get_hero(rival_hero) original_max_q = max(action_ratios) original_selected = action_ratios.index(original_max_q) #TODO 注意这里的逻辑是只限于第一个塔的 rival_near_tower = StateUtil.get_first_tower(state_info, rival_hero_info) rival_tower_distance = StateUtil.cal_distance(hero_info.pos, rival_near_tower.pos) # 如果附近没有敌方英雄,而且不在塔下 # 攻击敌方小兵 action = LineTrainerPolicy.policy_attack_rival_unit( hero_info, rival_hero_info, state_info, hero_name, rival_near_units, rival_near_tower, near_friend_units) if action is not None: return action # 如果在塔附近,且周围没有友军,也没有敌方英雄,则直接撤退 # 如果友方只剩下一个小兵,有一点点血,也开始撤退 if rival_tower_distance <= LineTrainerPolicy.RIVAL_TOWER_NEARBY_RADIUS: if len(near_friend_units) == 0 or ( len(near_friend_units) == 1 and near_friend_units[0].hp / float(near_friend_units[0].maxhp) <= 0.5): if rival_hero_info.hp <= 0 or StateUtil.cal_distance( hero_info.pos, rival_hero_info.pos ) >= LineTrainerPolicy.SAFE_RIVAL_HERO_DISTANCE: print("启动策略 有敌方塔且己方掩护不足时候撤退 " + hero_name) return LineTrainerPolicy.policy_move_retreat(hero_info) # 如果附近没有敌方英雄,在敌方塔下,且有小兵掩护 if rival_tower_distance <= LineTrainerPolicy.RIVAL_TOWER_NEARBY_RADIUS and len( near_friend_units) > 0: units_in_tower_range = LineTrainerPolicy.units_in_tower_range( near_friend_units, rival_near_tower.pos) if (rival_hero_info.hp <= 0 or StateUtil.cal_distance(hero_info.pos, rival_hero_info.pos) >= LineTrainerPolicy.SAFE_RIVAL_HERO_DISTANCE) and \ rival_near_tower is not None and \ units_in_tower_range > 0: # 被塔攻击的情况下后撤 if state_info.if_unit_attack_hero(rival_near_tower.unit_name, hero_name): print("启动策略 被塔攻击的情况下后撤 " + hero_name) return LineTrainerPolicy.policy_move_retreat(hero_info) # 有敌方小兵先打小兵 if len(rival_near_units) > 0: action = LineTrainerPolicy.policy_attack_rival_unit( hero_info, rival_hero_info, state_info, hero_name, rival_near_units, rival_near_tower, near_friend_units) if action is not None: print("启动策略 如果附近没有地方英雄,在敌方塔下,且有小兵掩护,有敌方小兵先打小兵 " + hero_name) return action # 掩护充足的情况下攻击对方塔 if units_in_tower_range >= 2: print("启动策略 如果附近没有地方英雄,在敌方塔下,且有小兵掩护,掩护充足的情况下攻击对方塔 " + hero_name) return LineTrainerPolicy.get_attack_tower_action( hero_name, hero_info, rival_near_tower) # 不足的情况下后撤(如果在塔的攻击范围内) if units_in_tower_range <= 2 and StateUtil.cal_distance( hero_info.pos, rival_near_tower.pos) <= StateUtil.TOWER_ATTACK_RADIUS: print( "启动策略 如果附近没有地方英雄,在敌方塔下,且有小兵掩护,不足的情况下后撤(如果在塔的攻击范围内) " + hero_name) return LineTrainerPolicy.policy_move_retreat(hero_info) #TODO 超低血量下撤退 # 如果对方英雄血量高,且差距明显,不要接近对方英雄 if hero_info.hp/float(hero_info.maxhp) <= 0.3 and \ rival_hero_info.hp/float(rival_hero_info.maxhp) >= hero_info.hp/float(hero_info.maxhp) + 0.2: # 另外一个条件是双方应该目前有一定的距离 heros_distance = StateUtil.cal_distance(hero_info.pos, rival_hero_info.pos) if heros_distance >= LineTrainerPolicy.KEEP_AWAY_FROM_HERO_START_DISTANCE: print('策略选择', state_info.battleid, hero_name, '差距明显情况下不要接近对方英雄') action_ratios = LineTrainerPolicy.keep_away_from( state_info, hero_info, rival_hero_info, action_ratios, rival_hero_info.pos, heros_distance) # 不应该贸然进入对方的塔下 # 在一个扩大的范围下进行侦查 if rival_tower_distance <= LineTrainerPolicy.RIVAL_TOWER_NEARBY_RADIUS + 4: units_in_tower_range = LineTrainerPolicy.units_in_tower_range( near_friend_units, rival_near_tower.pos) if units_in_tower_range <= 2: print('策略选择', state_info.battleid, hero_name, '检测会不会贸然进入对方的塔下') action_ratios = LineTrainerPolicy.keep_away_from( state_info, hero_info, rival_hero_info, action_ratios, rival_near_tower.pos, StateUtil.TOWER_ATTACK_RADIUS) # 大招的使用 # 对于查尔斯,如果周围没有敌人则不应该使用大招 action_ratios = LineTrainerPolicy.use_skill3_correctly( state_info, hero_info, rival_hero_info, rival_near_units, action_ratios) # 如果对方英雄血量很低,且不在塔下,且我方英雄血量较高 if rival_hero_info.hp > 0 and rival_hero_info.hp / float(rival_hero_info.maxhp) <= 0.3 and \ hero_info.hp / float(hero_info.maxhp) >= rival_hero_info.hp / float( rival_hero_info.maxhp) + 0.1 and \ rival_tower_distance > LineTrainerPolicy.RIVAL_TOWER_NEARBY_RADIUS: # 选择模型分数较高的行为 selected_skill = -1 skill_score = -1 print('启动策略, 如果对方英雄血量很低,且不在塔下,且我方英雄血量较高, ', state_info.battleid, hero_name, rival_tower_distance) for i in range(4): action_id = 10 * i + 9 if action_ratios[action_id] > skill_score: skill_score = action_ratios[action_id] selected_skill = i if selected_skill >= 0: print("启动策略 如果对方英雄血量很低,且不在塔下,且我方英雄血量较高, 从技能中选择 ", hero_name, selected_skill, skill_score) return LineTrainerPolicy.get_attack_hero_action( state_info, hero_name, rival_hero, selected_skill) current_max_q = max(action_ratios) current_selected = action_ratios.index(current_max_q) if original_max_q != current_max_q: print('策略选择', state_info.battleid, hero_name, '策略改变', original_selected, current_selected) if current_max_q == -1: return LineTrainerPolicy.policy_move_retreat(hero_info) else: return LineModel.select_actions(action_ratios, state_info, hero_name, rival_hero) return None
def units_in_range(units, pos, distance): num = 0 for unit in units: if StateUtil.cal_distance(unit.pos, pos) <= distance: num += 1 return num
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 build_response(self, state_info, prev_state_info, line_model, hero_names=None): battle_id = state_info.battleid tick = state_info.tick if tick >= 139062: db = 1 action_strs=[] if hero_names is None: hero_names = [hero.hero_name for hero in state_info.heros] for hero_name in hero_names: hero = state_info.get_hero(hero_name) prev_hero = prev_state_info.get_hero(hero.hero_name) if prev_state_info is not None else None # 检查是否重启游戏 # 线上第一个塔被摧毁 # 如果有可以升级的技能,优先升级技能3 skills = StateUtil.get_skills_can_upgrade(hero) if len(skills) > 0: skillid = 3 if 3 in skills else skills[0] update_cmd = CmdAction(hero.hero_name, CmdActionEnum.UPDATE, skillid, None, None, None, None, None, None) update_str = StateUtil.build_command(update_cmd) action_strs.append(update_str) # 检查周围状况 near_enemy_heroes = StateUtil.get_nearby_enemy_heros(state_info, hero.hero_name, StateUtil.LINE_MODEL_RADIUS) near_enemy_units = StateUtil.get_nearby_enemy_units(state_info, hero.hero_name, StateUtil.LINE_MODEL_RADIUS) nearest_enemy_tower = StateUtil.get_nearest_enemy_tower(state_info, hero.hero_name, StateUtil.LINE_MODEL_RADIUS + 3) # 回城相关逻辑 # 如果在回城中且没有被打断则继续回城,什么也不用返回 if prev_hero is not None: if self.hero_strategy[hero.hero_name] == ActionEnum.town_ing and prev_hero.hp <= hero.hp \ and not StateUtil.if_hero_at_basement(hero): if not hero.skills[6].canuse: print('回城中,继续回城') continue else: print('回城失败') if hero.hp <= 0: self.hero_strategy[hero.hero_name] = None continue # 处在少血状态是,且周围没有地方单位的情况下选择回城 # if len(near_enemy_heroes) == 0 and len(near_enemy_units) == 0 and nearest_enemy_tower is None: # if hero.hp/float(hero.maxhp) < LineTrainer.TOWN_HP_THRESHOLD: # print('策略层:回城') # # 检查英雄当前状态,如果在回城但是上一帧中受到了伤害,则将状态设置为正在回城,开始回城 # if self.hero_strategy[hero.hero_name] == ActionEnum.town_ing: # if prev_hero.hp > hero.hp: # town_action = CmdAction(hero.hero_name, CmdActionEnum.CAST, 6, hero.hero_name, None, None, None, None, None) # action_str = StateUtil.build_command(town_action) # action_strs.append(action_str) # # 检查英雄当前状态,如果不在回城,则将状态设置为正在回城,开始回城 # elif self.hero_strategy[hero.hero_name] != ActionEnum.town_ing: # self.hero_strategy[hero.hero_name] = ActionEnum.town_ing # town_action = CmdAction(hero.hero_name, CmdActionEnum.CAST, 6, hero.hero_name, None, None, None, None, None) # action_str = StateUtil.build_command(town_action) # action_strs.append(action_str) # # # 无论上面怎么操作,玩家下面的动作应该都是在回城中,所以跳过其它的操作 # continue # 处在泉水之中的时候设置策略层为吃线 if StateUtil.if_hero_at_basement(hero): if hero.hp < hero.maxhp: continue # 撤退逻辑 # TODO 甚至可以使用移动技能移动 if hero.hero_name in self.hero_strategy and self.hero_strategy[hero.hero_name] == ActionEnum.retreat: dist = StateUtil.cal_distance(hero.pos, self.retreat_pos) if dist <= 2: print('到达撤退点附近') self.hero_strategy[hero.hero_name] = None elif prev_hero is not None and prev_hero.pos.to_string() == hero.pos.to_string(): print('英雄卡住了,取消撤退') self.hero_strategy[hero.hero_name] = None else: print('仍然在撤退 ' + str(dist)) continue # 开始根据策略决定当前的行动 # 对线情况下,首先拿到兵线,朝最前方的兵线移动 # 如果周围有危险(敌方单位)则启动对线模型 # 如果周围有小兵或者塔,需要他们都是在指定线上的小兵或者塔 line_index = 1 near_enemy_units_in_line = StateUtil.get_units_in_line(near_enemy_units, line_index) nearest_enemy_tower_in_line = StateUtil.get_units_in_line([nearest_enemy_tower], line_index) if len(near_enemy_units_in_line) == 0 and len(nearest_enemy_tower_in_line) == 0 and (len(near_enemy_heroes) == 0 or StateUtil.if_in_line(hero, line_index, 4000) == -1): self.hero_strategy[hero.hero_name] = ActionEnum.line_1 # print("策略层:因为附近没有指定兵线的敌人所以开始吃线 " + hero.hero_name) # 跟兵线 front_soldier = StateUtil.get_frontest_soldier_in_line(state_info, line_index, hero.team) if front_soldier is None: action_str = StateUtil.build_action_command(hero.hero_name, 'HOLD', {}) action_strs.append(action_str) else: # 得到最前方的兵线位置 move_action = CmdAction(hero.hero_name, CmdActionEnum.MOVE, None, None, front_soldier.pos, None, None, None, None) action_str = StateUtil.build_command(move_action) action_strs.append(action_str) else: # 使用模型进行决策 # print("使用对线模型决定英雄%s的行动" % hero.hero_name) self.hero_strategy[hero.hero_name] = ActionEnum.line_model enemies = [] enemies.extend((hero.hero_name for hero in near_enemy_heroes)) enemies.extend((unit.unit_name for unit in near_enemy_units)) if nearest_enemy_tower is not None: enemies.append(nearest_enemy_tower.unit_name) # print('对线模型决策,因为周围有敌人 ' + ' ,'.join(enemies)) # 目前对线只涉及到两名英雄 rival_hero = '28' if hero.hero_name == '27' else '27' action = line_model.get_action(prev_state_info, state_info, hero.hero_name, rival_hero) action_str = StateUtil.build_command(action) action_strs.append(action_str) # 如果是要求英雄施法回城,更新英雄状态,这里涉及到后续多帧是否等待回城结束 if action.action == CmdActionEnum.CAST and int(action.skillid) == 6: print("英雄%s释放了回城" % hero_name) self.hero_strategy[hero.hero_name] = ActionEnum.town_ing # 如果是选择了撤退,进行特殊标记,会影响到后续的行为 if action.action == CmdActionEnum.RETREAT: print("英雄%s释放了撤退,撤退点为%s" % (hero_name, action.tgtpos.to_string())) self.hero_strategy[hero.hero_name] = ActionEnum.retreat self.retreat_pos = action.tgtpos # 保存action信息到状态帧中 state_info.add_action(action) return action_strs
def gen_line_input(self): state=[] my_hero_info = self.stateInformation.get_hero(self.hero_name) rival_hero_info = self.stateInformation.get_hero(self.rival_hero) # 添加附近塔信息(2个),搜索半径为self.NEAR_TOWER_RADIUS nearest_towers = StateUtil.get_near_towers_in_line(self.stateInformation, my_hero_info, self.line_idx, self.NEAR_TOWER_RADIUS) nearest_towers_rival = [t for t in nearest_towers if t.team != my_hero_info.team] nearest_towers_team = [t for t in nearest_towers if t.team == my_hero_info.team] # 添加双方英雄信息,对线模型暂时只考虑1v1的情况 my_hero_input = self.gen_input_hero(my_hero_info, nearest_towers_rival) # 首先判断对手英雄的位置,如果距离过远则不加入队伍中 if StateUtil.cal_distance(my_hero_info.pos, rival_hero_info.pos) > self.NEAR_TOWER_RADIUS: rival_hero_input = np.zeros(len(my_hero_input)).tolist() # print "对手距离过远,不作为输入信息" else: rival_hero_input = self.gen_input_hero(rival_hero_info, nearest_towers_rival) state += my_hero_input state += rival_hero_input # print(self.hero_name + ' 训练输入信息,敌方塔信息:' + ','.join([str(t.unit_name) for t in nearest_towers_rival])) # print(self.hero_name + ' 训练输入信息,己方塔信息:' + ','.join([str(t.unit_name) for t in nearest_towers_team])) if len(nearest_towers_rival) == 0: tower_input1 = self.gen_input_building(None) tower_input2 = self.gen_input_building(None) elif len(nearest_towers_rival) == 1: tower_input1 = self.gen_input_building(nearest_towers_rival[0], self.stateInformation, self.hero_name) tower_input2 = self.gen_input_building(None) # 当玩家处在高地时候会有超过2个塔 elif len(nearest_towers_rival) >= 2: tower_input1 = self.gen_input_building(nearest_towers_rival[0], self.stateInformation, self.hero_name) tower_input2 = self.gen_input_building(nearest_towers_rival[1], self.stateInformation, self.hero_name) else: tower_input1 = self.gen_input_building(nearest_towers_rival[0], self.stateInformation, self.hero_name) tower_input2 = self.gen_input_building(nearest_towers_rival[1], self.stateInformation, self.hero_name) # 添加一个己方塔 if len(nearest_towers_team) == 0: tower_input3 = self.gen_input_building(None) else: tower_input3 = self.gen_input_building(nearest_towers_team[0], self.stateInformation, self.hero_name) state += tower_input1 state += tower_input2 state += tower_input3 # 小兵信息 enermy_creeps=StateUtil.get_nearby_enemy_units(self.stateInformation,self.hero_name) m=len(enermy_creeps) n=8 for i in range(n): if i < m: state=state+self.gen_input_creep(enermy_creeps[i], self.stateInformation, self.hero_name, nearest_towers_rival) else: temp=self.gen_input_creep(None) state=state+list(temp) friend_creeps=StateUtil.get_nearby_friend_units(self.stateInformation, self.hero_name) m=len(friend_creeps) for i in range(n): if i <m: state=state+self.gen_input_creep(friend_creeps[i], self.stateInformation, self.hero_name, nearest_towers_rival) else: temp = self.gen_input_creep(None) state=state+list(temp) return state
def in_skill_range(pos1, pos2, range): dis = StateUtil.cal_distance(pos1, pos2) if dis < range: return dis return -1
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]
def remove_unaval_actions(acts, stateinformation, hero_name, rival_hero, debug=False): for i in range(len(acts)): hero = stateinformation.get_hero(hero_name) selected = i if selected < 8: # move # 不再检查movelock,因为攻击硬直也会造成这个值变成false(false表示不能移动) continue elif selected < 18: # 对敌英雄,塔,敌小兵1~8使用普攻 if hero.skills[0].canuse != True and ( hero.skills[0].cd == 0 or hero.skills[0].cd == None): # 普通攻击也有冷却,冷却时canuse=false,此时其实我们可以给出攻击指令的 # 所以只有当普通攻击冷却完成(cd=0或None)时,canuse仍为false我们才认为英雄被控,不能攻击 # 被控制住 acts[selected] = -1 if debug: print("普攻受限,放弃普攻") continue if selected == 8: # 敌方塔 tower = StateUtil.get_nearest_enemy_tower( stateinformation, hero_name, StateUtil.ATTACK_UNIT_RADIUS) if tower is None: acts[selected] = -1 if debug: print("塔太远,放弃普攻") continue dist = StateUtil.cal_distance(hero.pos, tower.pos) # if dist > self.att_dist: if dist > StateUtil.ATTACK_UNIT_RADIUS: # 在攻击范围外 acts[selected] = -1 if debug: print("塔太远,放弃普攻") continue elif selected == 9: # 敌方英雄 tgtid = rival_hero rival_info = stateinformation.get_hero(rival_hero) dist = StateUtil.cal_distance(hero.pos, rival_info.pos) # 英雄不可见 if not rival_info.is_enemy_visible(): acts[selected] = -1 if debug: print("英雄不可见") continue # 英雄太远,放弃普攻 # if dist > self.att_dist: if dist > StateUtil.ATTACK_HERO_RADIUS: acts[selected] = -1 if debug: print("英雄太远,放弃普攻") continue # 对方英雄死亡时候忽略这个目标 elif rival_info.hp <= 0: acts[selected] = -1 if debug: print("对方英雄死亡") continue else: # 小兵 creeps = StateUtil.get_nearby_enemy_units( stateinformation, hero_name) n = selected - 10 # 小兵不可见 if n >= len(creeps): # 没有这么多小兵 acts[selected] = -1 if debug: print("没有这么多兵,模型选错了") continue if not creeps[n].is_enemy_visible(): acts[selected] = -1 if debug: print("小兵不可见") continue dist = StateUtil.cal_distance(hero.pos, creeps[n].pos) # if dist > self.att_dist: if dist > StateUtil.ATTACK_UNIT_RADIUS: acts[selected] = -1 if debug: print("小兵太远,放弃普攻") continue # print ("英雄%s可以攻击到小兵%s,英雄位置%s,小兵位置%s,距离%s" % (str(hero_name), str(creeps[n].unit_name), # hero.pos.to_string(), creeps[n].pos.to_string(), str(dist))) elif selected < 48: # skill1 skillid = int((selected - 18) / 10 + 1) if hero.skills[skillid].canuse != True: # 被沉默,被控制住(击晕击飞冻结等)或者未学会技能 acts[selected] = -1 if debug: print("技能受限,放弃施法" + str(skillid) + " hero.skills[x].canuse=" + str(hero.skills[skillid].canuse) + " tick=" + str(stateinformation.tick)) continue if hero.skills[skillid].cost is not None and hero.skills[ skillid].cost > hero.mp: # mp不足 acts[selected] = -1 if debug: print("mp不足,放弃施法" + str(skillid)) continue if hero.skills[skillid].cd > 0: # 技能未冷却 acts[selected] = -1 if debug: print("技能cd中,放弃施法" + str(skillid)) continue [tgtid, tgtpos] = LineModel.choose_skill_target( selected - 18 - (skillid - 1) * 10, stateinformation, skillid, hero_name, hero.pos, rival_hero, debug) if tgtid == -1: acts[selected] = -1 if debug: print("目标不符合施法要求") continue elif selected == 48: # 撤退 acts[selected] = -1 elif selected == 49: # 撤退 acts[selected] = -1 return acts