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 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 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 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 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_places_near_mothership(self): """ Выбор точки возле матери для защиты. :return: список доступных точек для защиты вокруг матери. """ point_list = list() directions_list = list() start_vector = Vector(x=self.mothership.x, y=self.mothership.y) for direction in range(0, 361, 5): point_dir = start_vector.from_direction(direction=direction, module=int(MOTHERSHIP_HEALING_DISTANCE * 0.9)) point_x = point_dir.x + start_vector.x point_y = point_dir.y + start_vector.y if (theme.FIELD_WIDTH - self.radius >= point_x >= self.radius) \ and (theme.FIELD_HEIGHT - self.radius >= point_y >= self.radius): directions_list.append(direction) start = 0 stop = len(directions_list) step = len(directions_list) // (len(self.teammates) + 1) angle_list = list() for angle in range(start, stop, step): angle_list.append(directions_list[angle]) new_point = start_vector.from_direction(direction=directions_list[angle], module=int(MOTHERSHIP_HEALING_DISTANCE * 0.9)) point_x = new_point.x + start_vector.x point_y = new_point.y + start_vector.y point_list.append(Point(point_x, point_y)) return point_list
def get_place_near(point, target, angle): """ Расчет места рядом с point с отклонением angle от цели target """ vec = Vector(point.x - target.x, point.y - target.y) vec.rotate(angle) return Point(target.x + vec.x, target.y + vec.y)
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 checking_the_line_of_fire(self, unit, target, point=None): """Проверить, что на линии огня нет союзников""" if point is None: point = unit.coord vec_target_self = Vector(target.coord.x - point.x, target.coord.y - point.y) norm_vec_target_self = self.normalize_vector(vec_target_self) for friendly in self.data.my_units: if friendly is not unit: if friendly.distance_to(point) <= friendly.radius * 1.5: return False vec_target_friendly = Vector(target.coord.x - friendly.coord.x, target.coord.y - friendly.coord.y) norm_vec_target_friendly = self.normalize_vector( vec_target_friendly) scalar = self.scalar_vector_multiplication( norm_vec_target_self, norm_vec_target_friendly) try: angle = degrees(acos(scalar)) except: return False if angle < _MIN_ANGLE_ATTACK \ and (friendly.distance_to(target) < point.distance_to(target)): return False else: return True
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 _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 _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 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_angle(self, drone, partner, victim): def scalar(v1, v2): return v1.x * v2.x + v1.y * v2.y to_partner = Vector(partner.x - drone.x, partner.y - drone.y) to_victim = Vector(victim.x - drone.x, victim.y - drone.y) cos_partner_victim = scalar(to_partner, to_victim) / (to_partner.module * to_victim.module) return degrees(acos(cos_partner_victim))
def pursue(self, target_coord): """Двигаемся немного вперед к цели""" vec1 = Vector.from_points(self.coord, target_coord, module=1) self.vector = vec1 vec2 = Vector.from_direction(round(self.direction), 30) new_coord = Point(round(int(self.x + vec2.x)), y=round(int(self.y + vec2.y))) self.task = (self.move_to, new_coord) self.next_action()
def get_angle_Ox(self, drone, victim): def scalar(v1, v2): return v1.x * v2.x + v1.y * v2.y v1 = Vector(victim.x - drone.x, victim.y - drone.y) v2 = Vector(1200, 0) cos = scalar(v1, v2) / (v1.module * v2.module) return degrees(acos(cos))
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 get_angle(self, partner: GameObject, target: GameObject): """ Получает угол между векторами self-target и partner-target """ def scalar(vec1, vec2): return vec1.x * vec2.x + vec1.y * vec2.y v12 = Vector(self.drone.coord.x - target.coord.x, self.drone.coord.y - target.coord.y) v32 = Vector(partner.coord.x - target.coord.x, partner.coord.y - target.coord.y) _cos = scalar(v12, v32) / (v12.module * v32.module + 1.e-8) return math.degrees(math.acos(_cos))
def _get_position_from_points(first_point, second_point, koef, angle): vec = Vector.from_points(first_point, second_point) dist = vec.module first_koef = 1 / dist norm_vec = Vector(vec.x * first_koef * koef, vec.y * first_koef * koef) vec_position = norm_vec * MOTHERSHIP_HEALING_DISTANCE if angle: vec_position.rotate(angle) position = Point(first_point.x + vec_position.x, first_point.y + vec_position.y) return position
def get_place_near(self, point, target, angle): """ Расчет места рядом с point с отклонением angle от цели target :param point: :param target: :param angle: :return: new place point """ vec = Vector(point.x - target.x, point.y - target.y) vec.rotate(angle) return Point(target.x + vec.x, target.y + vec.y)
def get_place_near(point, target, angle): """ Расчет места рядом с point с отклонением angle от цели target :param point: исходная точка :param target: предполагаемая цель :param angle: угол отклонения :return: точка недалеко от цели """ vec = Vector(point.x - target.x, point.y - target.y) vec.rotate(angle) return Point(target.x + vec.x, target.y + vec.y)
def get_near_base(self, target): base = self.me.my_mothership new_vec = Vector.from_points(base.coord, target.coord) other_vec = new_vec.module _koef = 1 / other_vec norm_vec = Vector(new_vec.x * _koef, new_vec.y * _koef) vec_position = Vector(norm_vec.x * MOTHERSHIP_HEALING_DISTANCE * 0.9, norm_vec.y * MOTHERSHIP_HEALING_DISTANCE * 0.9) new_position = Point(base.coord.x + vec_position.x, base.coord.y + vec_position.y) return new_position
def avoid_friendly_fire(self): """ Метод меняет позицию дрона-истребителя на случайно генерируемую в пределах 150 от изначальной. Вызывается если в других методах valide_position() вернул False. :return: None """ vec = Vector(self.drone.position.x, self.drone.position.y) point = vec.from_direction(direction=random.choice([90, 180, 270, 360]), module=random.choice([100, 150, 200])) self.change_position( position=Point(x=self.drone.coord.x + point.x, y=self.drone.coord.y + point.y))
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 get_point_near_target(self): """ Метод рассчитывает начальную точку атаки рядом с целью. :return: Point-class instance """ vec = Vector.from_points(self.drone.coord, self.drone.aim.coord) dist = (self.drone.distance_to(self.drone.aim) - self.drone.gun.shot_distance) * 1.05 direction = vec.direction vec_attack = Vector.from_direction(direction=direction + self.drone.hq.courses_k[self.drone.inner_team_id - 1], module=dist) point = Point(self.drone.coord.x + vec_attack.x, self.drone.coord.y + vec_attack.y) return point
def check_base_near(self, target): """ Найти новую точку рядом с базой. """ base = self.me.my_mothership new_vec = Vector.from_points(base.coord, target.coord) other_vec = new_vec.module _koef = 1 / other_vec norm_vec = Vector(new_vec.x * _koef, new_vec.y * _koef) vec_position = Vector(norm_vec.x * MOTHERSHIP_HEALING_DISTANCE * 0.9, norm_vec.y * MOTHERSHIP_HEALING_DISTANCE * 0.9) new_position = Point(base.coord.x + vec_position.x, base.coord.y + vec_position.y) return new_position
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 verify_angle(self, partner: GameObject, target: Point): """ :return: True, если partner перекрывает цель """ v_from_self_to_target = Vector(target.x - self.coord.x, target.y - self.coord.y) v_from_self_to_partner = Vector(partner.coord.x - self.coord.x, partner.coord.y - self.coord.y) if v_from_self_to_target.module > v_from_self_to_partner.module and v_from_self_to_partner.module > 0: angle_partner = math.atan( 70 / v_from_self_to_partner.module) * (180 / math.pi) return abs(v_from_self_to_target.direction - v_from_self_to_partner.direction) < angle_partner else: return False
def get_turret_points(self): """ Метод создает стартовые точки расстановки дронов :return: None """ vec = Vector.from_points(self.my_mothership.coord, self.hq.center_field) dir = vec.direction angles_set = [dir - 70, dir + 40, dir + 10, dir - 10, dir - 40] for angle in angles_set: vec_turret = Vector.from_direction(direction=angle, module=MOTHERSHIP_HEALING_DISTANCE * 0.9) point_turret = Point(self.my_mothership.coord.x + vec_turret.x, self.my_mothership.coord.y + vec_turret.y) self.hq.TURRET_POINTS.append(Point(x=point_turret.x, y=point_turret.y))
def __init__(self, coord=None, radius=None, direction=None): if self.__scene is None: raise RobogameException("You must create Scene instance at first!") if self.auto_team: self.__team = self.scene.get_team(cls=self.__class__) if radius is None: radius = self.__class__.radius self.coord = coord if coord else Point(0, 0) self.radius = radius self.__container.append(self) GameObject.__objects_count += 1 self.id = GameObject.__objects_count if direction is None: direction = randint(0, 360) self.vector = Vector.from_direction(direction, module=1) self.target = None self.state = StateStopped(obj=self) self._heartbeat_tics = theme.HEARTBEAT_INTERVAL self._events = Queue() self._commands = Queue() self._selected = False self.add_event(EventBorned(self)) self.debug('born {coord} {vector}')