def will_meet(): if q.collinear(shell_speed): #TODO: may be bug good_direction = sign(shell_speed.x) != sign( q.x) and sign(shell_speed.y) != sign(q.y) if good_direction: return (True, (shell_v - tank_v).length() / (shell_speed.length() + INITIAL_SHELL_VELOCITY)) else: return (False, -1) else: d_tank, d_shell = intersect_lines( tank_v, q, shell_v, shell_speed.normalize()) d_tank = d_tank - 40 if d_tank < 0 or d_shell < 0: return (False, -1) t_tank = solve_quadratic(SHELL_ACCELERATION / 2, INITIAL_SHELL_VELOCITY, -d_tank) t_shell = solve_quadratic(SHELL_ACCELERATION / 2, shell_speed.length(), -d_shell) if fabs(t_tank - t_shell) < 1.5: return (True, t_tank) else: return (False, -1)
def shell_will_hit(self, shell, target, factor=1): """ Returns True if shell will hit rectangular object """ b = shell.angle e = Vector(1, 0) q = e.rotate(b) center = Vector(target.x - shell.x, target.y - shell.y) if center.scalar_product(q) < 0: return False a = target.angle c1 = center + Vector(target.width / 2 * factor, target.height / 2 * factor).rotate(a) c2 = center + Vector(-target.width / 2 * factor, target.height / 2 * factor).rotate(a) c3 = center + Vector(-target.width / 2 * factor, -target.height / 2 * factor).rotate(a) c4 = center + Vector(target.width / 2 * factor, -target.height / 2 * factor).rotate(a) if sign(c1.cross_product(q)) == sign(q.cross_product(c3)): #print("TEST", c1, c2, c3, c4, q) return True if sign(c2.cross_product(q)) == sign(q.cross_product(c4)): #print("TEST", c1, c2, c3, c4, q) return True return False
def shell_will_hit(self, shell, target, factor=1): """ Returns True if shell will hit rectangular object """ b = shell.angle e = Vector(1, 0) q = e.rotate(b) center = Vector(target.x - shell.x, target.y - shell.y) if center.scalar_product(q) < 0: return False a = target.angle c1 = center + Vector(target.width/2 * factor, target.height/2 * factor).rotate(a) c2 = center + Vector(- target.width/2 * factor, target.height/2 * factor).rotate(a) c3 = center + Vector(- target.width/2 * factor, - target.height/2 * factor).rotate(a) c4 = center + Vector(target.width/2 * factor, - target.height/2 * factor).rotate(a) if sign(c1.cross_product(q)) == sign(q.cross_product(c3)): #print("TEST", c1, c2, c3, c4, q) return True if sign(c2.cross_product(q)) == sign(q.cross_product(c4)): #print("TEST", c1, c2, c3, c4, q) return True return False
def shell_will_hit_tank_going_to(self, shell, tank, x, y, et=None): if et is None: et = self.estimate_time_to_position(x, y, tank) dist = tank.get_distance_to(x, y) v0 = hypot(shell.speedX, shell.speedY) a = SHELL_ACCELERATION d = tank.get_distance_to_unit(shell) #d = shell.get_distance_to(x, y) t = solve_quadratic(a / 2, v0, -d) if self.shell_will_hit(shell, tank, factor=1.05) and (et > t): return 1 #self.max_move_distance(fabs(v0), FICTIVE_ACCELERATION, 3, t) < dist): if dist < 150: # short distance result = self.shell_will_hit(shell, fictive_unit(tank, x, y), factor=1.05) if result: pt_v = Vector(x, y) tank_v = Vector(tank.x, tank.y) dir = pt_v - tank_v shell_speed = Vector(shell.speedX, shell.speedY) if dir.is_zero() or shell_speed.is_zero(): return float(result) if dir.angle(shell_speed) < PI / 8 and shell.get_distance_to( x, y) > d: return 0.6 return result else: # long distance, check if our direction is intersecting segment between shell and shell + v_shell*t pt_v = Vector(x, y) tank_v = Vector(tank.x, tank.y) dir = tank_v - pt_v shell_v = Vector(shell.x, shell.y) shell_speed = Vector(shell.speedX, shell.speedY) next_shell = shell_v + shell_speed * (t + 5) if sign((shell_v - tank_v).cross_product(dir)) == sign( dir.cross_product(next_shell - tank_v)): return True else: return False
def vector_is_intersecting_object(self, p, d, target, factor=1): center = Vector(target.x - p.x, target.y - p.y) if center.scalar_product(d) < 0: return False a = target.angle w, h = target.width / 2 * factor, target.height / 2 * factor c1 = center + Vector(w, h).rotate(a) c2 = center + Vector(-w, h).rotate(a) c3 = center + Vector(-w, -h).rotate(a) c4 = center + Vector(w, -h).rotate(a) if sign(c1.cross_product(d)) == sign(d.cross_product(c3)): return True if sign(c2.cross_product(d)) == sign(d.cross_product(c4)): return True return False
def vector_is_intersecting_object(self, p, d, target, factor=1): center = Vector(target.x - p.x, target.y - p.y) if center.scalar_product(d) < 0: return False a = target.angle w, h = target.width/2 * factor, target.height/2 * factor c1 = center + Vector(w ,h).rotate(a) c2 = center + Vector(- w, h).rotate(a) c3 = center + Vector(- w, - h).rotate(a) c4 = center + Vector(w, - h).rotate(a) if sign(c1.cross_product(d)) == sign(d.cross_product(c3)): return True if sign(c2.cross_product(d)) == sign(d.cross_product(c4)): return True return False
def process(self, cur_target, move): tank = self.context.tank world = self.context.world self.physics = self.context.physics world = self.context.world est_pos = self.physics.estimate_target_position(cur_target, tank) def bonus_is_attacked(): for bonus in world.bonuses: if (self.physics.will_hit(tank, bonus, BONUS_FACTOR) and tank.get_distance_to_unit(bonus) < tank.get_distance_to(*est_pos)): return bonus return False def obstacle_is_attacked(): obstacles = chain(filter(DEAD_TANK, world.tanks), filter(ALLY_TANK(tank.id), world.tanks), world.obstacles) for obstacle in obstacles: next_position = self.physics.estimate_target_position( obstacle, tank) next_unit = fictive_unit(obstacle, next_position[0], next_position[1]) blocked = ((self.physics.will_hit(tank, next_unit, DEAD_TANK_OBSTACLE_FACTOR) or self.physics.will_hit( tank, obstacle, DEAD_TANK_OBSTACLE_FACTOR)) and tank.get_distance_to_unit(obstacle) < tank.get_distance_to(*est_pos)) if blocked: return obstacle return False cur_angle = tank.get_turret_angle_to(*est_pos) good_to_shoot = self.physics.will_hit( tank, fictive_unit(cur_target, est_pos[0], est_pos[1]), TARGETING_FACTOR) if good_to_shoot: if self.context.health_fraction > 0.8 and self.context.hull_fraction > 0.5 and tank.get_distance_to_unit( cur_target) > 400 and tank.premium_shell_count <= 3: move.fire_type = FireType.REGULAR else: move.fire_type = FireType.PREMIUM_PREFERRED else: move.fire_type = FireType.NONE if bonus_is_attacked() or obstacle_is_attacked(): self.context.debug('!!! Obstacle is attacked, don\'t shoot') move.fire_type = FireType.NONE if world.tick < 10 + tank.teammate_index * 10: move.fire_type = FireType.NONE if fabs(cur_angle) > PI / 180 * 0.5: move.turret_turn = sign(cur_angle)
def shell_will_hit_tank_going_to(self, shell, tank, x, y, et=None): if et is None: et = self.estimate_time_to_position(x, y, tank) dist = tank.get_distance_to(x, y) v0 = hypot(shell.speedX, shell.speedY) a = SHELL_ACCELERATION d = tank.get_distance_to_unit(shell) #d = shell.get_distance_to(x, y) t = solve_quadratic(a/2, v0, -d) if self.shell_will_hit(shell, tank, factor=1.05) and (et > t): return 1 #self.max_move_distance(fabs(v0), FICTIVE_ACCELERATION, 3, t) < dist): if dist < 150: # short distance result = self.shell_will_hit(shell, fictive_unit(tank, x, y), factor=1.05) if result: pt_v = Vector(x, y) tank_v = Vector(tank.x, tank.y) dir = pt_v - tank_v shell_speed = Vector(shell.speedX, shell.speedY) if dir.is_zero() or shell_speed.is_zero(): return float(result) if dir.angle(shell_speed) < PI/8 and shell.get_distance_to(x, y) > d: return 0.6 return result else: # long distance, check if our direction is intersecting segment between shell and shell + v_shell*t pt_v = Vector(x, y) tank_v = Vector(tank.x, tank.y) dir = tank_v - pt_v shell_v = Vector(shell.x, shell.y) shell_speed = Vector(shell.speedX, shell.speedY) next_shell = shell_v + shell_speed * (t + 5) if sign((shell_v - tank_v).cross_product(dir)) == sign(dir.cross_product(next_shell - tank_v)): return True else: return False
def process(self, cur_target, move): tank = self.context.tank world = self.context.world self.physics = self.context.physics world = self.context.world est_pos = self.physics.estimate_target_position(cur_target, tank) def bonus_is_attacked(): for bonus in world.bonuses: if (self.physics.will_hit(tank, bonus, BONUS_FACTOR) and tank.get_distance_to_unit(bonus) < tank.get_distance_to(*est_pos)): return bonus return False def obstacle_is_attacked(): obstacles = chain( filter(DEAD_TANK, world.tanks), filter(ALLY_TANK(tank.id), world.tanks), world.obstacles ) for obstacle in obstacles: next_position = self.physics.estimate_target_position(obstacle, tank) next_unit = fictive_unit(obstacle, next_position[0], next_position[1]) blocked = ((self.physics.will_hit(tank, next_unit, DEAD_TANK_OBSTACLE_FACTOR) or self.physics.will_hit(tank, obstacle, DEAD_TANK_OBSTACLE_FACTOR)) and tank.get_distance_to_unit(obstacle) < tank.get_distance_to(*est_pos)) if blocked: return obstacle return False cur_angle = tank.get_turret_angle_to(*est_pos) good_to_shoot = self.physics.will_hit( tank, fictive_unit(cur_target, est_pos[0], est_pos[1]), TARGETING_FACTOR ) if good_to_shoot: if self.context.health_fraction > 0.8 and self.context.hull_fraction > 0.5 and tank.get_distance_to_unit(cur_target) > 400 and tank.premium_shell_count <= 3: move.fire_type = FireType.REGULAR else: move.fire_type = FireType.PREMIUM_PREFERRED else: move.fire_type = FireType.NONE if bonus_is_attacked() or obstacle_is_attacked(): self.context.debug('!!! Obstacle is attacked, don\'t shoot') move.fire_type = FireType.NONE if world.tick < 10 + tank.teammate_index * 10: move.fire_type = FireType.NONE if fabs(cur_angle) > PI/180 * 0.5: move.turret_turn = sign(cur_angle)
def will_meet(): if q.collinear(shell_speed): #TODO: may be bug good_direction = sign(shell_speed.x) != sign(q.x) and sign(shell_speed.y) != sign(q.y) if good_direction: return (True, (shell_v - tank_v).length()/(shell_speed.length() + INITIAL_SHELL_VELOCITY)) else: return (False, -1) else: d_tank, d_shell = intersect_lines(tank_v, q, shell_v, shell_speed.normalize()) d_tank = d_tank - 40 if d_tank < 0 or d_shell < 0: return (False, -1) t_tank = solve_quadratic(SHELL_ACCELERATION/2, INITIAL_SHELL_VELOCITY, -d_tank) t_shell = solve_quadratic(SHELL_ACCELERATION/2, shell_speed.length(), -d_shell) if fabs(t_tank - t_shell) < 1.5: return (True, t_tank) else: return (False, -1)
def process_shooting(): targets = filter(ALIVE_ENEMY_TANK, world.tanks) if not targets: return def get_target_priority(tank, target): health_fraction = tank.crew_health / tank.crew_max_health # ================ # DISTANCE # ================ angle_penalty_factor = ( 1 + (1 - health_fraction) * 1.5 - (1 - max(0, 150 - tank.remaining_reloading_time) / 150) * 1) angle_degrees = fabs( tank.get_turret_angle_to_unit(target)) / PI * 180 distance_penalty = tank.get_distance_to_unit(target) / 10 angle_penalty = angle_penalty_factor * (angle_degrees**1.2) / 2 # ================ # FINISH # ================ if ((target.crew_health <= 20 or target.hull_durability <= 20) or (tank.premium_shell_count > 0 and (target.crew_health <= 35 or target.hull_durability <= 35))): finish_bonus = 30 else: finish_bonus = 0 # ================ # RESPONSE # ================ if self.physics.attacked_area( tank.x, tank.y, target, cache=self.EA_cache) > 0.5: attacking_me_bonus = 20 else: attacking_me_bonus = 0 # ================ # LAST TARGET # ================ last_target_bonus = 0 if self.memory.last_turret_target_id: if self.memory.last_turret_target_id == target.id: last_target_bonus = 5 result = 180 + finish_bonus + attacking_me_bonus + last_target_bonus - distance_penalty - angle_penalty self.debug( 'TARGET [%20s] (x=%8.2f, y=%8.2f, |v|=%8.2f) finish_B=%8.2f, AM_B=%8.2f, LT_B=%8.2f, D_P=%8.2f, A_P=%8.2f, APF=%8.2f, result=%8.2f' % (target.player_name, target.x, target.y, hypot(target.speedX, target.speedY), finish_bonus, attacking_me_bonus, last_target_bonus, distance_penalty, angle_penalty, angle_penalty_factor, result)) return result if self.debug_mode: targets = sorted(targets, key=lambda t: t.player_name) targets_f = [(t, get_target_priority(tank, t)) for t in targets] cur_target = max(targets_f, key=operator.itemgetter(1))[0] self.memory.last_turret_target_id = cur_target.id est_pos = self.physics.estimate_target_position(cur_target, tank) def bonus_is_attacked(): for bonus in world.bonuses: if (self.physics.will_hit(tank, bonus, BONUS_FACTOR) and tank.get_distance_to_unit(bonus) < tank.get_distance_to(*est_pos)): return bonus return False def obstacle_is_attacked(): obstacles = chain(filter(DEAD_TANK, world.tanks), filter(ALLY_TANK(tank.id), world.tanks), world.obstacles) for obstacle in obstacles: next_position = self.physics.estimate_target_position( obstacle, tank) next_unit = fictive_unit(obstacle, next_position[0], next_position[1]) blocked = ((self.physics.will_hit( tank, next_unit, DEAD_TANK_OBSTACLE_FACTOR) or self.physics.will_hit( tank, obstacle, DEAD_TANK_OBSTACLE_FACTOR)) and tank.get_distance_to_unit(obstacle) < tank.get_distance_to(*est_pos)) if blocked: return obstacle return False cur_angle = tank.get_turret_angle_to(*est_pos) good_to_shoot = self.physics.will_hit( tank, fictive_unit(cur_target, est_pos[0], est_pos[1]), TARGETING_FACTOR) if good_to_shoot: if self.health_fraction > 0.8 and self.hull_fraction > 0.5 and tank.get_distance_to_unit( cur_target) > 400 and tank.premium_shell_count <= 3: move.fire_type = FireType.REGULAR else: move.fire_type = FireType.PREMIUM_PREFERRED else: move.fire_type = FireType.NONE if bonus_is_attacked() or obstacle_is_attacked(): self.debug('!!! Obstacle is attacked, don\'t shoot') move.fire_type = FireType.NONE if world.tick < 10 + tank.teammate_index * 10: move.fire_type = FireType.NONE if fabs(cur_angle) > PI / 180 * 0.5: move.turret_turn = sign(cur_angle)
def process_shooting(): targets = filter(ALIVE_ENEMY_TANK, world.tanks) if not targets: return def get_target_priority(tank, target): health_fraction = tank.crew_health / tank.crew_max_health # ================ # DISTANCE # ================ angle_penalty_factor = (1 + (1 - health_fraction) * 1.5 - (1 - max(0, 150 - tank.remaining_reloading_time)/150) * 1) angle_degrees = fabs(tank.get_turret_angle_to_unit(target)) / PI * 180 distance_penalty = tank.get_distance_to_unit(target) / 10 angle_penalty = angle_penalty_factor * (angle_degrees**1.2)/2 # ================ # FINISH # ================ if ((target.crew_health <= 20 or target.hull_durability <= 20) or (tank.premium_shell_count > 0 and (target.crew_health <= 35 or target.hull_durability <= 35))): finish_bonus = 30 else: finish_bonus = 0 # ================ # RESPONSE # ================ if self.physics.attacked_area(tank.x, tank.y, target, cache=self.EA_cache) > 0.5: attacking_me_bonus = 20 else: attacking_me_bonus = 0 # ================ # LAST TARGET # ================ last_target_bonus = 0 if self.memory.last_turret_target_id: if self.memory.last_turret_target_id == target.id: last_target_bonus = 5 result = 180 + finish_bonus + attacking_me_bonus + last_target_bonus - distance_penalty - angle_penalty self.debug('TARGET [%20s] (x=%8.2f, y=%8.2f, |v|=%8.2f) finish_B=%8.2f, AM_B=%8.2f, LT_B=%8.2f, D_P=%8.2f, A_P=%8.2f, APF=%8.2f, result=%8.2f' % (target.player_name, target.x, target.y, hypot(target.speedX, target.speedY), finish_bonus, attacking_me_bonus, last_target_bonus, distance_penalty, angle_penalty, angle_penalty_factor, result)) return result if self.debug_mode: targets = sorted(targets, key=lambda t : t.player_name) targets_f = [(t, get_target_priority(tank, t)) for t in targets] cur_target = max(targets_f, key=operator.itemgetter(1))[0] self.memory.last_turret_target_id = cur_target.id est_pos = self.physics.estimate_target_position(cur_target, tank) def bonus_is_attacked(): for bonus in world.bonuses: if (self.physics.will_hit(tank, bonus, BONUS_FACTOR) and tank.get_distance_to_unit(bonus) < tank.get_distance_to(*est_pos)): return bonus return False def obstacle_is_attacked(): obstacles = chain( filter(DEAD_TANK, world.tanks), filter(ALLY_TANK(tank.id), world.tanks), world.obstacles ) for obstacle in obstacles: next_position = self.physics.estimate_target_position(obstacle, tank) next_unit = fictive_unit(obstacle, next_position[0], next_position[1]) blocked = ((self.physics.will_hit(tank, next_unit, DEAD_TANK_OBSTACLE_FACTOR) or self.physics.will_hit(tank, obstacle, DEAD_TANK_OBSTACLE_FACTOR)) and tank.get_distance_to_unit(obstacle) < tank.get_distance_to(*est_pos)) if blocked: return obstacle return False cur_angle = tank.get_turret_angle_to(*est_pos) good_to_shoot = self.physics.will_hit( tank, fictive_unit(cur_target, est_pos[0], est_pos[1]), TARGETING_FACTOR ) if good_to_shoot: if self.health_fraction > 0.8 and self.hull_fraction > 0.5 and tank.get_distance_to_unit(cur_target) > 400 and tank.premium_shell_count <= 3: move.fire_type = FireType.REGULAR else: move.fire_type = FireType.PREMIUM_PREFERRED else: move.fire_type = FireType.NONE if bonus_is_attacked() or obstacle_is_attacked(): self.debug('!!! Obstacle is attacked, don\'t shoot') move.fire_type = FireType.NONE if world.tick < 10 + tank.teammate_index * 10: move.fire_type = FireType.NONE if fabs(cur_angle) > PI/180 * 0.5: move.turret_turn = sign(cur_angle)