def shot(self, enemy): vector = Vector.from_points(self.coord, enemy.coord, module=1) self.vector = vector able_to_shot = True if any(self.point_in_circle(self.coord, teammate) for teammate in self.teammates) or \ any(self.point_in_circle(self.coord, mother_ship.coord) for mother_ship in self.scene.motherships): point = self.make_a_step( self.coord, Point(scene.theme.FIELD_WIDTH / 2, scene.theme.FIELD_HEIGHT / 2)) self.move_at(point) return for teammate in self.teammates: vector = Vector.from_points(self.coord, teammate.coord, module=1) difference = abs(self.vector.direction - vector.direction) distance = self.distance_to(teammate) if difference < 15 and distance < self.gun.shot_distance: able_to_shot = False break if able_to_shot and self.distance_to(enemy) <= self.gun.shot_distance: if enemy.is_moving: self.gun.shot( Point(enemy.coord.x + enemy.vector.x**enemy.vector.module, enemy.coord.y + enemy.vector.y**enemy.vector.module)) else: self.gun.shot(enemy)
def check_not_friendly_fire(self, potential_target: object): """ Проверка выстрела на friendly fair (циклом просматривает всех сокомандников и, если хотя бы один не проходит все проверки то возвращает False) :param potential_target: Цель, по которой дрону отдан приказ стрелять :type potential_target: Object :return: возвращает True, если выстрелить можно, и возвращает False, если стрелять нельзя :rtype: bool """ vector_to_target = Vector.from_points( self.coord, potential_target.coord) # ветор от дрона до цели for my_team_drone in self.my_brain_center.ret_drones_by_status( filter_alive=True, filter_our=True): if my_team_drone != self: # в себя никак не попадешь, поэтому себя проверять на friendly fair не нужно if self.distance_to(potential_target) > self.distance_to( my_team_drone ): # если дистанция от дрона до цели больше, чем до сокомандника, то теоретически сокомандник может быть прямо между вами и целью - требуется дальнейшая проверка if my_team_drone.near( potential_target ) is False: # если цель и сокомандник находятся слишком близко друг к другу, то игра засчитает урон дрону или базе из другой команды vector_to_teammate = Vector.from_points( self.coord, my_team_drone.coord) if vector_to_target.direction - self.ACCURACY_CANCEL_SHOT_FRIENDLY_FIRE <= \ vector_to_teammate.direction <= \ vector_to_target.direction + self.ACCURACY_CANCEL_SHOT_FRIENDLY_FIRE: # проверка на зону поражения - если сокомандник находится в зоне поражения, то при выстреле с высокой вероятностью будет поражен return False # возвращаем False - нельзя стрелять return True # возвращаем True, если все дроны прошли все проверки в цикле
def game_step(self): if not self.have_gun: super(HunterDrone, self).game_step() return self.native_game_step() if self._hunting_strategy is None: return self._hunting_strategy.game_step(self) if self.victim is not None: vector = Vector.from_points(self.coord, self.victim.coord, module=self.gun.shot_distance) if int(self.distance_to(self.victim)) < 1 or ( self.distance_to(self.victim) < vector.module and abs( nearest_angle_distance(vector.direction, self.direction)) < 7): self.gun.shot(self.victim) else: enemies = [ enemy for enemy in self.scene.drones if enemy.team != self.team and enemy.is_alive and enemy.distance_to(self) < self.gun.shot_distance ] enemie = sorted(enemies, key=lambda x: -x.cargo.payload) for enemy in enemies: vector = Vector.from_points(self.coord, enemy.coord) if abs(nearest_angle_distance(vector.direction, self.direction)) < 7: self.gun.shot(enemy) break pass
def _get_point_nearby_enemy(self, target): """ Находит и возвращает валидную точку, которую можно занять для атаки цели :param target: цель атаки :return: оптимальную(нет) точку рядом с врагом """ if isinstance(target, GameObject): vec = Vector.from_points(target.coord, self.drone.coord) elif isinstance(target, Point): vec = Vector.from_points(target, self.drone.coord) else: raise Exception("target must be GameObject or Point!".format(target, )) dist = vec.module _koef = 1 / dist norm_vec = Vector(vec.x * _koef, vec.y * _koef) vec_gunshot = norm_vec * min(int(self.drone.attack_range), int(dist)) purpose = Point(target.coord.x + vec_gunshot.x, target.coord.y + vec_gunshot.y) angles = [0, 60, -60, 40, -40, 20, -20] shuffle(angles) if self.drone.drone_number == 1: place = self.get_place_near(purpose, target, angles[self.drone.drone_number]) return place for ang in angles: place = self.get_place_near(purpose, target, ang) if place and self.valid_place(place): return place return Point(theme.FIELD_WIDTH // 2, theme.FIELD_HEIGHT // 2)
def get_place_for_attack(self, soldier, target): """ Выбор места для атаки цели, если цель не в радиусе атаки :param soldier: атакующий :param target: цель/объект атаки :return: Point - место атаки или None - если не выбрано место атаки """ if isinstance(target, GameObject): vec = Vector.from_points(target.coord, soldier.coord) elif isinstance(target, Point): vec = Vector.from_points(target, soldier.coord) else: raise Exception("target must be GameObject or Point!".format( target, )) dist = vec.module _koef = 1 / dist norm_vec = Vector(vec.x * _koef, vec.y * _koef) vec_gunshot = norm_vec * min(int(soldier.attack_range), int(dist)) purpose = Point(target.coord.x + vec_gunshot.x, target.coord.y + vec_gunshot.y) angles = [0, 60, -60, 30, -30] random.shuffle(angles) for ang in angles: place = self.get_place_near(purpose, target, ang) if place and soldier.validate_place(place): return place return None
def devide_step_path(self, drone, target): if drone.distance_to(target) > 150: for angle_delta in range(12): vec = Vector.from_points(drone.coord, target.coord) vec = Vector.from_direction( vec.direction + randint(angle_delta * 5, angle_delta * 5), 150) flag = True for d in drone.scene.drones: if d.team != drone.team and drone.distance_to( d) < drone.gun.shot_distance: vec2 = Vector.from_points(d.coord, target.coord) if ((abs(180 - abs(d.direction - vec.direction)) < 5 or (abs(d.direction - vec.direction) < 5 and abs(vec2.direction - vec.direction) < 5))): flag = False if flag: break else: vec = Vector.from_points(drone.coord, target.coord) vec = Vector.from_direction(vec.direction, 150) new_coord = Point(x=drone.coord.x + vec.x, y=drone.coord.y + vec.y) drone.actions.append(['move_step', new_coord]) return # передвигаемся не по прямой до цели, а по 150 точек, со случайным углом new_coord = Point(x=drone.coord.x + vec.x, y=drone.coord.y + vec.y) drone.actions.append(['move_step', new_coord]) else: if drone.distance_to(target) > 1: drone.actions.append(['move', target])
def move_to(self, object): self.cost_forpost = 0 self.target_move_to = object self.headquarters.save_static_move(self, object) vector_target = Vector.from_points(self.coord, object, module=1) if isinstance(object, Point) else \ Vector.from_points(self.coord, object.coord, module=1) self.vector = vector_target super().move_at(object)
def get_coords_for_attak(self, drones_in_my_team, mothership_name): mothership = self.scene.get_mothership(mothership_name) drones = list(d for d in self.scene.drones if d.team == mothership.team) quantity = drones_in_my_team if mothership != self.mothership: distance = 400 else: distance = 300 angle = 85 / (quantity - 1) rotate = -5 self.target_for_attack = None if mothership.x > self.scene.field[ 0] / 2 and mothership.y > self.scene.field[1] / 2: p = Point(mothership.x, mothership.y - distance) v = Vector.from_points(mothership.coord, p) angle = 40 / (quantity - 1) rotate = 0 elif mothership.x > self.scene.field[ 0] / 2 and mothership.y < self.scene.field[1] / 2: p = Point(mothership.x - distance, mothership.y) v = Vector.from_points(mothership.coord, p) angle = 40 / (quantity - 1) rotate = -50 elif mothership.x < self.scene.field[ 0] / 2 and mothership.y > self.scene.field[1] / 2: p = Point(mothership.x + distance, mothership.y) v = Vector.from_points(mothership.coord, p) angle = 40 / (quantity - 1) rotate = -50 elif mothership.x < self.scene.field[ 0] / 2 and mothership.y < self.scene.field[1] / 2: p = Point(mothership.x, mothership.y + distance) v = Vector.from_points(mothership.coord, p) angle = 40 / (quantity - 1) rotate = 0 len_alive = len(set(m for m in self.scene.motherships if m.is_alive)) if len_alive == 2: angle = 90 / (quantity - 1) rotate = 0 v.rotate(rotate) p = Point(mothership.x + v.x, mothership.y + v.y) points_to_stop = [p] difference = angle while len(points_to_stop) < quantity: next_v = Vector.from_direction(v.direction - angle, v.module) next_p = Point(mothership.x + next_v.x, mothership.y + next_v.y) points_to_stop.append(next_p) angle += difference for d in my_team: d.point_to_attack_mothership = points_to_stop.pop() d.move_at(self.point_to_attack_mothership) self.action = 'move_to_plase_for_attack'
def _friendly_fire(self, enemy): enemy_vector = Vector.from_points(self.drone.coord, enemy.coord) for mate in self.drone.teammates: if self.drone.near(mate): return True mate_vector = Vector.from_points(self.drone.coord, mate.coord) scalar = int(enemy_vector.x * mate_vector.x + enemy_vector.y * mate_vector.y) modules = int(enemy_vector.module * mate_vector.module) angle = math.degrees(math.acos(min(scalar / modules, 1))) if angle < 20 and self.drone.distance_to(enemy) > self.drone.distance_to(mate) \ and not isinstance(mate.state, StateMoving): return True return False
def get_angle_delta(self, friend_drone): """ Метод возвращает дельту углов между углом дрона к цели и углом дрона к союзнику. Используется для дальнейшей проверки не находится ли союзник на линии огня. :param friend_drone: Drone-class instance :return: float """ my_aim_vec = Vector.from_points(self.drone.coord, self.drone.aim.coord) my_aim_direction = my_aim_vec.direction friend_drone_vec = Vector.from_points(self.drone.coord, friend_drone.coord) friend_drone_direction = friend_drone_vec.direction result = abs(friend_drone_direction - my_aim_direction) return result
def get_place_for_attack(self, drone, purpose): """ Получение места для атаки :param drone: экземпляр класса OkhotnikovFNDrone :param purpose: обект, колученный в методе next_purpose :return: точка на карте, из которой будет вестись огонь по цели """ safe_distance = round(drone.radius * 1.6) vec = Vector.from_points(purpose.coord, drone.coord, module=1) dist = drone.distance_to(purpose) vec_gunshot = vec * min(int(drone.attack_range), int(dist)) target = Point(purpose.coord.x + vec_gunshot.x, purpose.coord.y + vec_gunshot.y) possible_points_attack = self._get_possible_points_attack( target, purpose, drone) # print(possible_points_attack) for point_attack in possible_points_attack: place_free = True for occupied_point in self.occupied_points_attack: place_free = place_free and point_attack.distance_to( occupied_point) >= safe_distance if (point_attack and self.valid_place(point_attack) and self.valid_bullet_trajectory(point_attack, purpose) and place_free): self.occupied_points_attack.append(point_attack) return point_attack return None
def is_safe_way_to_target(self, target): # проверяет безопасно ли лететь до цели enemies = [ drone for drone in self.scene.drones if self.team != drone.team and drone.is_alive ] vector_to_target = Vector.from_points( self.coord, target.coord if not isinstance(target, Point) else target) distance_to_target = vector_to_target.module if vector_to_target.module > 1 else 1 _koef = 1 / distance_to_target normalize_vector = Vector(vector_to_target.x * _koef, vector_to_target.y * _koef) start_distance = 100 if self.is_near_base() else 1 drones_on_way = [] for i in range(start_distance, int(distance_to_target), self.radius // 2): current_vector = normalize_vector * i check_point = Point(self.x + current_vector.x, self.y + current_vector.y) for drone in enemies: if drone.distance_to( check_point ) <= self.attack_range + 2 * self.radius: # если по пути будем под обстрелом, то небезопасно drones_on_way.append(drone.id) drones_on_way = set(drones_on_way) if len( drones_on_way ) <= 1: # если во время перелёта по дрону будет стретять не больше 1 врага, то можно лететь return True else: return False
def move_to(self, target): if isinstance(target, Point): coord = target else: coord = target.coord self.vector = Vector.from_points(self.coord, coord, module=0.1) self.move_at(target)
def _shoot_or_move(self, drone, purpose): """ Происходит выбор делать выстрел или сменить позицию :param drone: экземпляр класса OkhotnikovFNDrone :param purpose: обект, колученный в методе next_purpose """ safe_mothership_radius = round(drone.mothership.radius * 1.6) if self.valid_place(drone.coord) and self.valid_bullet_trajectory( drone.coord, purpose): if drone.mothership.distance_to( drone.coord) < safe_mothership_radius: safe_mothership_vec = Vector.from_points( drone.mothership.coord, purpose.coord, module=1) dist_to_move = round(safe_mothership_radius - drone.distance_to(drone.mothership) + 2) safe_mothership_vec = safe_mothership_vec * dist_to_move point_attack = Point(drone.coord.x + safe_mothership_vec.x, drone.coord.y + safe_mothership_vec.y) drone.actions.append(['move', point_attack]) return drone.actions.append(['shoot', purpose]) else: point_attack = self.get_place_for_attack(drone, purpose) if point_attack: self.remove_occupied_point_attack(drone) drone.actions.append(['move', point_attack])
def get_point_on_way_to(unit, target, at_distance=None): if at_distance is None: at_distance = theme.CARGO_TRANSITION_DISTANCE * 0.9 va = Vector.from_points(unit.coord, target.coord) vb = Vector.from_direction(va.direction, at_distance) vb.rotate(180.0) return Point(unit.x + va.x + vb.x, unit.y + va.y + vb.y)
def on_overlap_with(self, obj_status): if not hasattr(obj_status, "damage_taken"): return # Пролетаем некомандные объекты if obj_status.team is None: return if theme.TEAM_DRONES_FRIENDLY_FIRE: # Не наносим урон себе if obj_status.id == self._owner.id: return else: # Пролетаем свои объекты if obj_status.team == self._owner.team: return # За премя жизни ни в кого не попали if not obj_status.is_alive or not self.is_alive: return self.__ttl = 0 self.stop() self.state.stop() obj_status.damage_taken(theme.PROJECTILE_DAMAGE) if self.death_animation is not None: self.__attached = self.death_animation( projectile=self, target=obj_status, distance=int(obj_status.distance_to(self) / 2), direction=Vector.from_points(obj_status.coord, self.coord).direction, ttl=self.__attached_ttl)
def on_born(self): self.born_soldier() nearesst_aster = [(self.distance_to(aster), aster) for aster in self.asteroids] nearesst_aster.sort(key=lambda x: x[0]) idx = len(self.headquarters.soldiers) - 1 if self.have_gun: vec = self.headquarters.get_vec_near_mothership(self, self.basa) angles = [] if isinstance(self.role, Turel): angles = self.headquarters.angles_turel.copy() self.headquarters.angles_turel.remove(angles[0]) elif isinstance(self.role, Collector): angles = self.headquarters.angles_collector.copy() self.headquarters.angles_collector.remove(angles[0]) self.start_angle = angles[0] vec.rotate(angles[0]) point_attack = self.headquarters.get_place_near_mothership(self, vec) self.start_point = point_attack self.vector = Vector.from_points(self.coord, self.headquarters.center_map, module=1) if point_attack: self.actions.append(['move_to', point_attack, 1, 1]) else: self.actions.append(["move_to", nearesst_aster[idx][1], 1, 1]) self.next_action()
def shot(self, target): vector_to_target = Vector.from_points(self.coord, target.coord) self.turn_to(target) if vector_to_target.direction - 2 <= self.direction <= vector_to_target.direction + 2: self.gun.shot(target) else: self.turn_to(target)
def pursue(self, target_coord): """Двигаемся немного вперед к цели""" vec1 = Vector.from_points(self.coord, target_coord, module=20) new_coord = Point(round(int(self.x + vec1.x)), y=round(int(self.y + vec1.y))) self.task = (self.move_to, new_coord) self.next_action()
def valid_bullet_trajectory(self, point, purpose): """ Проверка, нету ли дронов из своей команды на траектории выстрела из точки point :param point: одна из точек на карте из списка полученного в _get_possible_points_attack :param purpose: обект, колученный в методе next_purpose :return: булевое значение, валидна или нет """ drone = self.unit safe_distance = round(drone.radius * 1.2) is_valid = 0 < point.x < theme.FIELD_WIDTH and 0 < point.y < theme.FIELD_HEIGHT mothership = drone.mothership is_valid = is_valid and (mothership.distance_to(purpose) > mothership.radius) for length in range(1, round(point.distance_to(purpose.coord)), round(safe_distance / 2)): attack_vec = Vector.from_points(point, purpose.coord, module=1) vec_gunshot = attack_vec * length point_on_trajectory = Point(point.x + vec_gunshot.x, point.y + vec_gunshot.y) for team_mate in drone.headquarters.drones: if not team_mate.is_alive or team_mate is drone: continue is_valid = is_valid and ( team_mate.distance_to(point_on_trajectory) >= safe_distance) return is_valid
def get_distance_from_points(cls, first_point, second_point): first_point, second_point = cls.check_on_points_errors( first_point, second_point) enemy_vector = Vector.from_points(first_point, second_point) enemy_distance_to_target = Vector.calc_module(enemy_vector.x, enemy_vector.y) return enemy_distance_to_target
def _turn_to_purpose(self, drone, purpose): """ Поворот к цели :param drone: экземпляр класса OkhotnikovFNDrone :param purpose: обект, колученный в методе next_purpose """ if hasattr(purpose, 'is_moving') and purpose.is_moving: purpose_vec = Vector.from_direction(direction=purpose.direction, module=1) drone_purpose_vec = Vector.from_points(purpose.coord, drone.coord) length = drone_purpose_vec.module if drone_purpose_vec.module > 0 else 1 _cos = ((purpose_vec.x * drone_purpose_vec.x + purpose_vec.y * drone_purpose_vec.y) / (length * purpose_vec.module)) coef_b = (PROJECTILE_SPEED**2 - DRONE_SPEED**2) coef_d = (2 * DRONE_SPEED * length * _cos)**2 + 4 * coef_b * (length**2) coef = round((-2 * DRONE_SPEED * length + coef_d**0.5) / (2 * coef_b) * DRONE_SPEED) purpose_vec = purpose_vec * coef possible_purpose = Point(purpose.coord.x + purpose_vec.x, purpose.coord.y + purpose_vec.y) drone.actions.append(['turn', possible_purpose]) else: drone.actions.append(['turn', purpose])
def shoot(self, target): vec = Vector.from_points(self.coord, target.coord, module=self.attack_range) for base in self.scene.motherships: if target is base and self.distance_to(base) < 250: self.task = (self.change_locate, self.center_map) return self.next_action() if hasattr(target, 'distance_to') and target.distance_to( base) < 300 and self.distance_to(base) < 300: self.task = (self.change_locate, self.center_map) return self.next_action() if self.distance_to(base) < 150: self.task = (self.pursue, target.coord) return self.next_action() if not self.check_shoot(target, vec): return self.next_action() if self.distance_to(target.coord) > self.attack_range: self.task = (self.pursue, target.coord) return self.next_action() if self.gun_cooldown: self.task = (self.turn_to, target) return self.next_action() if target.is_alive: self.vector = vec self.shot_count += 1 self.gun.shot(target) else: self.task = (self.change_locate, self.center_map) self.next_action()
def fast_turning_to(self, point): vector_to_shot = Vector.from_points(self.coord, point) point_from_shooting = self.get_near_point(point=point, x_distance=10**-10) self.move_at(point_from_shooting) if math.fabs(self.vector.direction - vector_to_shot.direction) < 10: return True
def _shoot_to_the_enemy(self): """Дрон стреляет""" self.drone.turn_to(self.drone.enemy) vector = Vector.from_points(self.drone.coord, self.drone.enemy.coord) if abs(self.drone.direction - vector.direction) <= 5 and self.drone.checking_the_trajectory_of_the_shot( enemy=self.drone.enemy): self.drone.gun.shot(self.drone.enemy) SivkovDrone.shots_at_enemy += 1
def create_point_behind_comandor(self): """Создание точки между командиром и базой""" vec = Vector.from_points(OlshannikovDron.our_commander.coord, self.my_mothership.coord) vec.rotate(180) self.point_bihind_comandor = Point( self.my_mothership.coord.x + vec.x / 3, self.my_mothership.coord.y + vec.y / 3)
def has_any_enemy_going_harvest(self): if not self._target_point: return False enemy_drones = [d for d in self.unit.scene.drones if d.team != self.unit.team and d.is_alive and d.distance_to(self._target) < theme.CARGO_TRANSITION_DISTANCE * 4.0 and math.fabs(d.direction - Vector.from_points( d.coord, self._target.coord.copy() ).direction) < (math.pi / 180.0)] # 1 degree return len(enemy_drones) > 0
def get_place_near_mothership(self, soldier): center_field = Point(theme.FIELD_WIDTH // 2, theme.FIELD_HEIGHT // 2) vec = Vector.from_points(soldier.my_mothership.coord, center_field) dist = vec.module _koef = 1 / dist norm_vec = Vector(vec.x * _koef, vec.y * _koef) vec_position = norm_vec * MOTHERSHIP_HEALING_DISTANCE * 0.9 position = Point(soldier.my_mothership.coord.x + vec_position.x, soldier.my_mothership.coord.y + vec_position.y) return position
def check_count_drone_attack(self): result = 0 for d in self.scene.drones: if d.team != self.team and d.is_alive and self.distance_to( d) < self.gun.shot_distance + 50: vec2 = Vector.from_points(d.coord, self.coord) if abs(d.direction - vec2.direction) < 5: result += 1 return result
def get_place_for_attack(self, soldier, target): if isinstance(target, GameObject): vec = Vector.from_points(target.coord, soldier.coord) elif isinstance(target, Point): vec = Vector.from_points(target, soldier.coord) else: raise Exception("target must be GameObject or Point!".format(target, )) dist = vec.module _koef = 1 / dist norm_vec = Vector(vec.x * _koef, vec.y * _koef) vec_gunshot = norm_vec * min(self.unit.gun_range, int(dist)) purpose = Point(target.coord.x + vec_gunshot.x, target.coord.y + vec_gunshot.y) angles = [0, 60, -60, 30, -30, 15, -15, 45, -45] random.shuffle(angles) for ang in angles: place = self.get_place_near(purpose, target, ang) if place and self.valide_place(place): return place return None