def calculateCurrentLane(self, wiz: Wizard): distances_to_lanes = [ min( self.ways[LaneType.TOP][1:6], key=lambda x: wiz.get_distance_to_unit(x) ), min( self.ways[LaneType.MIDDLE][1:6], key=lambda x: wiz.get_distance_to_unit(x) ), min( self.ways[LaneType.BOTTOM][1:6], key=lambda x: wiz.get_distance_to_unit(x) ) ] lane_index = distances_to_lanes.index(min( distances_to_lanes, key=lambda x: wiz.get_distance_to_unit(x) )) return lane_index
def get_nearest_target(me: Wizard, world: World): """ Находим ближайшую цель для атаки, независимо от её типа и других характеристик. """ targets = [] targets.extend(world.buildings) targets.extend(world.wizards) targets.extend(world.minions) nearest_target = None nearest_target_distance = 1.5 * me.vision_range for target in targets: # Нейтралов атакуем тоже если их хп меньше максимального - они стригеренны # if (target.faction == Faction.NEUTRAL and target.life == target.max_life) or \ # (target.faction == me.faction): if not is_enemy(target, me): continue distance = me.get_distance_to_unit(target) if distance < nearest_target_distance: nearest_target = target nearest_target_distance = distance return nearest_target
def attack_best_target(me: Wizard, world: World, game: Game, move: Move, skills: Set, attack_faction): targets = [ unit for unit in world.wizards if unit.faction == attack_faction and me.get_distance_to_unit(unit) < me.vision_range ] if targets: # Try to attack the weakest wizard. target = min(targets, key=(lambda unit: unit.life)) if MyStrategy.attack(me, game, move, skills, target, True): return True # Try to attack the nearest wizard. target = min(targets, key=(lambda unit: me.get_distance_to_unit(unit))) if MyStrategy.attack(me, game, move, skills, target, True): return True # Chase for it. MyStrategy.move_to(me, world, game, move, target.x, target.y) return True # Else try to attack an enemy building. targets = [ unit for unit in world.buildings if unit.faction == attack_faction and me.get_distance_to_unit(unit) < me.vision_range ] if targets: target = min(targets, key=(lambda unit: me.get_distance_to_unit(unit))) if MyStrategy.attack(me, game, move, skills, target, True): return True # Move closer to the building. MyStrategy.move_to(me, world, game, move, target.x, target.y) return True # Else try to attack an enemy minion. targets = [ unit for unit in world.minions if unit.faction == attack_faction and me.get_distance_to_unit(unit) < me.cast_range ] if targets: target = min(targets, key=(lambda unit: unit.life)) if MyStrategy.attack(me, game, move, skills, target, False): return True # Couldn't attack anyone. return False
def attack_nearest_enemy(me: Wizard, world: World, game: Game, move: Move, skills: Set, attack_faction): targets = sorted(( unit for unit in itertools.chain(world.wizards, world.minions, world.buildings) if unit.faction == attack_faction ), key=(lambda unit: me.get_distance_to_unit(unit))) for target in targets: if MyStrategy.attack(me, game, move, skills, target, not isinstance(target, Minion)): return True return False
def is_oriented_to_unit(me: Wizard, game: Game, unit: CircularUnit) -> (bool, float): angle_to_unit = me.get_angle_to_unit(unit) cast_angle = abs(angle_to_unit) - math.atan(unit.radius / me.get_distance_to_unit(unit)) if cast_angle < 0.0: # We can attack. return True, 0.0 # Attack with some cast angle. if cast_angle < game.staff_sector / 2.0: # Cast angle has the same sign as turn angle. return True, math.copysign(cast_angle, angle_to_unit) # :( return False, None
def get_nearest_neutral(me: Wizard, world: World): targets = [] targets.extend(world.trees) targets.extend(world.minions) nearest_neutral = None nearest_neutral_distance = me.cast_range for target in targets: if is_neutral(target): distance = me.get_distance_to_unit(target) if distance < nearest_neutral_distance: nearest_neutral = target nearest_neutral_distance = distance return nearest_neutral
def _find_problem_units(self, me: Wizard, reverse=False): def is_problem_unit(angle, reverse, problem_sector): if reverse: return fabs(angle) > radians(180) - problem_sector else: return fabs(angle) < problem_sector units = self.W.buildings + self.W.wizards + self.W.minions + self.W.trees connected_u = [ t for t in units if me.get_distance_to_unit(t) <= (me.radius + t.radius) * 1.05 and me.id != t.id ] problem_u = [ t for t in connected_u if is_problem_unit(me.get_angle_to_unit( t), reverse, self.PROBLEM_ANGLE) ] return None if not problem_u else problem_u[0]
def get_action(me: Wizard, game: Game, skills: Set[SkillType], unit: LivingUnit, allow_fireball: bool) -> (ActionType, float): distance_to_unit = me.get_distance_to_unit(unit) min_cast_distance = distance_to_unit - unit.radius if distance_to_unit < game.staff_range: return ActionType.STAFF, min_cast_distance if distance_to_unit > me.cast_range: return ActionType.NONE, min_cast_distance if ( # FIXME: StatusType.BURNING not in unit.statuses and SkillType.FIREBALL in skills and allow_fireball and me.mana > game.fireball_manacost and distance_to_unit > game.fireball_explosion_max_damage_range + me.radius ): return ActionType.FIREBALL, min_cast_distance if ( # FIXME: StatusType.FROZEN not in unit.statuses and SkillType.FROST_BOLT in skills and me.mana > game.frost_bolt_manacost ): return ActionType.FROST_BOLT, min_cast_distance if me.mana > game.magic_missile_manacost: return ActionType.MAGIC_MISSILE, min_cast_distance return ActionType.NONE, min_cast_distance
def move(self, me: Wizard, world: World, game: Game, move: Move): self.initTick( me, world, game, move ) self.debugFeatures(); if self.state == PlayerState.CHECK_BONUSES: if not self.walk_reached: if self.bonus_index != -1 and self.target_waypoint != self.center_point: if self.isVisible(self.target_waypoint, game.bonus_radius) and (world.tick_index > self.check_time + self.bonus_predict_time): print( 'I can see bonus!' ) target_bonus = list( filter( lambda x: self.target_waypoint.x == x.x and self.target_waypoint.y == x.y, world.bonuses ) ) if not target_bonus: if self.bonus_index == 0: self.bonus_index = 1 self.target_waypoint = self.bonus_points[self.bonus_index] else: self.target_waypoint = self.center_point if me.get_distance_to_unit(self.target_waypoint) < (self.wp_radius * 2 if self.target_waypoint == self.center_point \ else game.bonus_radius + me.radius): print( 'Reached' ) if self.target_waypoint in self.bonus_points: print( 'Go to center' ) self.target_waypoint = self.center_point else: self.walk_reached = True else: self.goToWaypointWithAttackNear( self.target_waypoint ) else: if self.bonus_index == -1: if self.current_lane == LaneType.MIDDLE or self.current_lane == LaneType.TOP: self.bonus_index = 0 else: self.bonus_index = 1 self.target_waypoint = self.bonus_points[self.bonus_index] self.walk_reached = False elif self.bonus_index == 0: self.bonus_index = 1 if self.current_lane == LaneType.MIDDLE: self.target_waypoint = self.bonus_points[self.bonus_index] self.walk_reached = False else: self.state = PlayerState.WALK_FORWARD if self.isEnemyInRange(game.wizard_vision_range): self.state = PlayerState.BATTLE print('Found enemy!') return if self.state == PlayerState.WALK_TO_BASE: if not self.walk_reached: if me.get_distance_to_unit(self.target_waypoint) < self.wp_radius: self.walk_reached = True if self.target_waypoint != self.base_point: self.target_waypoint = self.getPreviousWP() self.walk_reached = False else: self.goToWaypointWithAttackNear( self.target_waypoint ) if self.target_waypoint == self.base_point and self.isEnemyInRange( game.wizard_vision_range * 1.5 ): self.state = PlayerState.BATTLE print('Found enemy!') return else: if self.change_lane: self.setLane( (self.current_lane + 1) % 3 ) self.change_lane = False self.state = PlayerState.WALK_FORWARD return if self.state == PlayerState.WALK_FORWARD: next_wp = self.getNextWP() self.goToWaypointWithAttackNear( next_wp ) self.checkReachedLastWP() self.checkBaseUnderAttack() self.checkBonuses() if self.isEnemyInRange( game.wizard_vision_range * 1.5 ): self.state = PlayerState.BATTLE print('Found enemy!') return near_friendly = [] near_friendly.extend( self.friendly_wizards ) near_friendly.extend( self.friendly_minions ) near_friendly = list(filter( lambda x: x.get_distance_to_unit(next_wp) < me.get_distance_to_unit(next_wp), near_friendly)) if world.tick_index < 1500: if next_wp == self.center_point and me.get_distance_to_unit( next_wp ) < 600 and len( near_friendly ) < 2: move.speed = 0 move.strafe_speed = 0 return if self.state == PlayerState.BATTLE: self.checkReachedLastWP() self.checkBaseUnderAttack() self.checkBonuses() if self.isNearBase(): lane = self.calculateCurrentLane( me ) self.setLane( lane ) if not self.isEnemyInRange( game.wizard_vision_range * 1.5 ): self.state = PlayerState.WALK_FORWARD return target = self.getNearestEnemy() near_weak_enemies = list( filter( lambda x: self.isTargetInCastRange(x) and \ x.life <= game.magic_missile_direct_damage * 2, self.enemy_units ) ) enemy_minions_in_attack_range = \ list( filter( lambda x: x.faction != me.faction and x.faction != Faction.NEUTRAL and \ x.get_distance_to_unit( me ) <= \ (game.orc_woodcutter_attack_range if x.type == MinionType.ORC_WOODCUTTER \ else game.fetish_blowdart_attack_range) + x.radius + me.radius, world.minions ) ) enemy_wizards_on_target = \ list( filter( lambda x: x.get_distance_to_unit( me ) <= game.wizard_vision_range and \ abs(x.get_angle_to_unit( me )) < (game.staff_sector / 2.), self.enemy_wizards ) ) enemies_in_staff_range = list( filter( lambda x: self.isTargetInStaffRange(x), self.enemy_units ) ) enemy_wizards_in_cast_range = list( filter( lambda x: self.isTargetInCastRange(x), self.enemy_wizards ) ) enemies_in_cast_range = list( filter( lambda x: self.isTargetInCastRange(x), self.enemy_units ) ) # if self.enemy_wizards and not enemy_minions_in_attack_range: # nearest_enemy_wizard = min( self.enemy_wizards, key=lambda x: me.get_distance_to_unit( x ) ) # if me.get_distance_to_unit( nearest_enemy_wizard ) <= game.wizard_vision_range: # target = nearest_enemy_wizard # if near_weak_enemies and not enemy_minions_in_attack_range: # enemy_wizards_in_attack_range = \ # list( filter( lambda x: x.get_distance_to_unit( me ) < game.wizard_cast_range + me.radius and \ # x != near_weak_enemies[0], self.enemy_wizards ) ) # if not enemy_wizards_in_attack_range: # target = near_weak_enemies[0] # self.attackTarget( target ) if self.isHealthLow(0.4): self.keepDistanceToUnit( self.getNearestEnemy(), game.wizard_vision_range ) if enemy_wizards_in_cast_range: nearest_wiz = min( enemy_wizards_in_cast_range, key=lambda x: me.get_distance_to_unit(x) ) self.attackTarget( nearest_wiz ) elif enemies_in_cast_range: nearest = min( enemies_in_cast_range, key=lambda x: me.get_distance_to_unit(x) ) self.attackTarget( nearest ) elif len(enemy_wizards_on_target) > 2: nearest_wiz = min( enemy_wizards_on_target, key=lambda x: me.get_distance_to_unit(x) ) self.stepBackFromUnit( nearest_wiz, game.wizard_cast_range + (me.radius + target.radius) ) if enemy_wizards_in_cast_range: nearest_wiz = min( enemy_wizards_in_cast_range, key=lambda x: me.get_distance_to_unit(x) ) self.attackTarget( nearest_wiz ) elif enemies_in_cast_range: nearest = min( enemies_in_cast_range, key=lambda x: me.get_distance_to_unit(x) ) self.attackTarget( nearest ) elif len(enemy_minions_in_attack_range) >= 2: nearest = min( enemy_minions_in_attack_range, key=lambda x: me.get_distance_to_unit(x) ) self.stepBackFromUnit( nearest, (game.orc_woodcutter_attack_range if nearest.type == MinionType.ORC_WOODCUTTER \ else game.fetish_blowdart_attack_range) + nearest.radius + me.radius ) if enemy_wizards_in_cast_range: nearest_wiz = min( enemy_wizards_in_cast_range, key=lambda x: me.get_distance_to_unit(x) ) self.attackTarget( nearest_wiz ) elif enemies_in_cast_range: nearest = min( enemies_in_cast_range, key=lambda x: me.get_distance_to_unit(x) ) self.attackTarget( nearest ) elif not enemies_in_cast_range: nearest = self.getNearestEnemy() self.goForwardToUnit( nearest, game.wizard_cast_range - (me.radius + target.radius) ) self.attackTarget( nearest ) else: if self.isMagicMissileReady(): if enemy_wizards_in_cast_range: nearest_wiz = min( enemy_wizards_in_cast_range, key=lambda x: me.get_distance_to_unit(x) ) self.goForwardToUnit( nearest_wiz, game.wizard_cast_range - (me.radius + target.radius) ) self.attackTarget( nearest_wiz ) elif enemies_in_cast_range: nearest = min( enemies_in_cast_range, key=lambda x: me.get_distance_to_unit(x) ) if not self.isHealthLow(0.6): self.goForwardToUnit( nearest, game.staff_range + (me.radius + target.radius) ) self.attackTarget( nearest ) else: if len(enemy_wizards_on_target) > 0: nearest_wiz = min( enemy_wizards_on_target, key=lambda x: me.get_distance_to_unit(x) ) self.stepBackFromUnit( nearest_wiz, game.wizard_cast_range + (me.radius + target.radius) ) elif enemies_in_staff_range: nearest = min( enemies_in_staff_range, key=lambda x: me.get_distance_to_unit(x) ) self.attackTarget( nearest ) elif enemies_in_cast_range: nearest = min( enemies_in_cast_range, key=lambda x: me.get_distance_to_unit(x) ) if not self.isHealthLow(0.6): self.goForwardToUnit( nearest, game.staff_range + (me.radius) ) self.attackTarget( nearest ) safe_tower_distance = game.guardian_tower_attack_range + me.radius * 2 + game.guardian_tower_radius near_enemy_tower = next((x for x in world.buildings if me.get_distance_to_unit( x ) <= safe_tower_distance and x.faction != me.faction), None) if near_enemy_tower: if near_enemy_tower.life > near_enemy_tower.max_life * 0.25: if me.life > near_enemy_tower.damage: friend_minions_near_tower = list( filter( lambda x: near_enemy_tower.get_distance_to_unit( me ) > near_enemy_tower.get_distance_to_unit( x ), self.friendly_minions ) ) if len( friend_minions_near_tower ) < 2 and near_enemy_tower.remaining_action_cooldown_ticks < 150: self.stepBackFromUnit( near_enemy_tower, safe_tower_distance ) else: if near_enemy_tower.remaining_action_cooldown_ticks < 200: self.stepBackFromUnit( near_enemy_tower, safe_tower_distance ) return
def move(self, me: Wizard, world: World, game: Game, move: Move): if self.debug: self.debug.syncronize(world) if not self.initialized: self.initialize(me, world, game) self.initialize_tick(me, world, game) score_threshold = -0.1 wizard_score = me.life / (me.max_life * 0.5) - 1 if self.battle_front: self.battle_front.init(world, me) front_score = self.battle_front.get_front_score(me) k = 0.6 common_score = front_score * (1 - k) + wizard_score * k else: common_score = wizard_score nearest_target = Points2D.get_nearest_target(me, world) if nearest_target is not None: distance = me.get_distance_to_unit(nearest_target) angle = me.get_angle_to_unit(nearest_target) if (distance <= game.staff_range) and (abs(angle) < game.staff_sector / 2.0): move.action = ActionType.STAFF elif (distance <= me.cast_range) and (abs(angle) < game.staff_sector / 2.0): move.action = ActionType.MAGIC_MISSILE move.cast_angle = angle move.min_cast_distance = distance - nearest_target.radius + game.magic_missile_radius if common_score < score_threshold: previous_waypoint = Points2D.get_previous_waypoint( self.waypoints, me) self.map.note_enemy_angle = False self.go_to(move, previous_waypoint, note_angle=False) self.last_actions[world.tick_index % STOP_CHECK_TICK_COUNT] = Action.BACK return else: nearest_target = Points2D.get_nearest_target(me, world) if nearest_target is not None: distance = me.get_distance_to_unit(nearest_target) angle = me.get_angle_to_unit(nearest_target) if distance <= me.cast_range: self.go_to(move, None, note_angle=False) if abs(angle) < game.staff_sector / 2.0: move.action = ActionType.MAGIC_MISSILE move.cast_angle = angle move.min_cast_distance = distance - nearest_target.radius + game.magic_missile_radius self.last_actions[world.tick_index % STOP_CHECK_TICK_COUNT] = Action.ENEMY return else: min_attack_dist = min_atack_distance(me) * self.life_coef new_x = me.x + (nearest_target.x - me.x) / distance * ( distance - min_attack_dist) new_y = me.y + (nearest_target.y - me.y) / distance * ( distance - min_attack_dist) self.go_to(move, Point2D(new_x, new_y), note_angle=True) self.last_actions[world.tick_index % STOP_CHECK_TICK_COUNT] = Action.NEXT return else: next_waypoint = Points2D.get_next_waypoint(self.waypoints, me) note_angle = True if world.tick_index > STOP_CHECK_TICK_COUNT: last_pos_index = (world.tick_index + 1) % STOP_CHECK_TICK_COUNT dist_last_poses = me.get_distance_to_unit( self.last_poses[last_pos_index]) # если далеко не ушел и если последние все действия NEXT if ((dist_last_poses < STOP_CHECK_TICK_COUNT * 0.2 * game.wizard_forward_speed) and (sum([x == Action.NEXT for x in self.last_actions]) == STOP_CHECK_TICK_COUNT)): note_angle = False self.map.neutral_coef = 1000 * np.random.normal(1, 0.3) self.map.neutral_dist_coef = 1000 self.go_to(move, next_waypoint, note_angle=note_angle) self.last_actions[world.tick_index % STOP_CHECK_TICK_COUNT] = Action.NEXT if world.tick_index < SLOW_MOVE_TICK_COUNT: move.speed *= 0.5 move.strafe_speed *= 0.5 return
def _cast_distance(self, me: Wizard, e: LivingUnit): projectile_radius = self.G.magic_missile_radius return me.get_distance_to_unit(e) - e.radius - projectile_radius
def _enemy_in_staff_distance(self, me: Wizard, e): return me.get_distance_to_unit(e) <= (self.G.staff_range + e.radius)