def multiple_attackers(attackers): cnt = len(allies_targeting) ind = index_in_sorted(allies_targeting, tank.id) segment = (target_avoid_distance_forward - target_avoid_distance_backward) / (cnt + 1) shift = segment * (ind + 1) + target_avoid_distance_backward #shift * if cnt == 2: if ind == 0: shift = target_avoid_distance_backward + 45 else: shift = target_avoid_distance_forward - 45 estimate_pos = target_v + target_direction * shift shift_fu = fictive_unit(target, estimate_pos.x, estimate_pos.y) shoot = ( physics.will_hit_precise(tank, shift_fu, factor=0.8) and all([ lambda a: a.remaining_reloading_time < MULTIPLE_MAX_TIME_DIFFERENCE or a.reloading_time - a. remaining_reloading_time < MULTIPLE_MAX_TIME_DIFFERENCE, attackers ]) #and target_avoid_distance_forward - target_avoid_distance_backward < 200 ) comment = 'MULTIPLE(%d), shift=%8.2f' % (ind, shift) if obstacle_is_attacked(context, estimate_pos): shoot = False comment += ' blocked' if context.debug_mode: if shoot and tank.remaining_reloading_time == 0 and all([ context.memory.good_to_shoot.get(t.id) or t.id == tank.id for t in attackers ]): max_pos = target_v + target_avoid_distance_forward * target_direction min_pos = target_v + target_avoid_distance_backward * target_direction max_pos_fu = fictive_unit(target, max_pos.x, max_pos.y) min_pos_fu = fictive_unit(target, min_pos.x, min_pos.y) debug_data = { "units": [min_pos_fu, max_pos_fu, target], "tanks": attackers } debug_dump( debug_data, "/".join([ str(context.memory.battle_id), str(context.world.tick) + "_" + str(tank.id) + "_multiple" ])) return ((estimate_pos.x, estimate_pos.y), shoot, target_avoid_distance_forward, target_avoid_distance_backward, comment)
def single_attacker(): #estimate_pos = target_v + target_direction * ((target_avoid_distance_forward + target_avoid_distance_backward) / 2) #vulnerable_width = max(90 * target_turret_n_cos, 60 * (1 - target_turret_n_cos)) target_avoid_distance_forward_new = target_avoid_distance_forward * context.memory.tank_precision[ tank.id] target_avoid_distance_backward_new = target_avoid_distance_backward * context.memory.tank_precision[ tank.id] max_pos = target_v + target_avoid_distance_forward_new * target_direction min_pos = target_v + target_avoid_distance_backward_new * target_direction max_pos_fu = fictive_unit(target, max_pos.x, max_pos.y) min_pos_fu = fictive_unit(target, min_pos.x, min_pos.y) #shoot = physics.will_hit(tank, max_pos_fu, 0.9) and physics.will_hit(tank, min_pos_fu, 0.9) shoot_precise = physics.will_hit_precise(tank, max_pos_fu, factor=0.8, side_part=0.8) and \ physics.will_hit_precise(tank, min_pos_fu, factor=0.8, side_part=0.8) and \ physics.will_hit_precise(tank, target, factor=0.8, side_part=0.8) shoot = shoot_precise fabs(target_turret_n_cos) #all_corners = get_unit_corners(max_pos_fu) + get_unit_corners(min_pos_fu) all_corners = get_unit_corners(target) #closest_corner = min(all_corners, key=lambda c: c.distance(tank_v)) # Instead of closest corner try to target middle of colsest side sc1, sc2 = sorted(all_corners, key=lambda c: c.distance(tank_v))[:2] closest_corner = 0.75 * sc1 + 0.25 * sc2 middle_position = (max_pos + min_pos) / 2 w = fabs(target_turret_n_cos)**2 estimate_pos = w * middle_position + (1 - w) * closest_corner comment = 'SINGLE, %s' % w if obstacle_is_attacked(context, estimate_pos): shoot = False comment += ' blocked' if context.debug_mode: if shoot and tank.remaining_reloading_time == 0: debug_data = { "units": [min_pos_fu, max_pos_fu, target], "tanks": [tank] } debug_dump( debug_data, "/".join([ str(context.memory.battle_id), str(context.world.tick) + "_" + str(tank.id) + "_single" ])) return ((estimate_pos.x, estimate_pos.y), shoot, target_avoid_distance_forward, target_avoid_distance_backward, comment)
def obstacle_is_attacked(context, est_pos): world = context.world tank = context.tank physics = context.physics obstacles = chain( filter(DEAD_TANK, world.tanks), filter(ALLY_TANK(tank.id), world.tanks) ) for obstacle in obstacles: next_position = physics.estimate_target_position(obstacle, tank) next_unit = fictive_unit(obstacle, next_position[0], next_position[1]) blocked = ((physics.will_hit(tank, next_unit, DEAD_TANK_OBSTACLE_FACTOR) or physics.will_hit(tank, obstacle, DEAD_TANK_OBSTACLE_FACTOR)) and tank.get_distance_to_unit(obstacle) < tank.get_distance_to(est_pos.x, est_pos.y)) if blocked: return obstacle for bonus in world.bonuses: if (physics.will_hit(tank, bonus, BONUS_FACTOR)): dist_tank_to_bonus = tank.get_distance_to_unit(bonus) dist_tank_to_pos = tank.get_distance_to(est_pos.x, est_pos.y) if dist_tank_to_bonus < dist_tank_to_pos: dist_bonus_to_pos = bonus.get_distance_to(est_pos.x, est_pos.y) free_to_hit = dist_tank_to_bonus > 300 and dist_tank_to_bonus > 2 * dist_bonus_to_pos if not free_to_hit: return bonus bunker_obstacle = world.obstacles[0] if (physics.will_hit(tank, bunker_obstacle, 1.06) and tank.get_distance_to_unit(bunker_obstacle) < tank.get_distance_to(est_pos.x, est_pos.y)): return bunker_obstacle return False
def obstacle_is_attacked(context, est_pos): world = context.world tank = context.tank physics = context.physics obstacles = chain(filter(DEAD_TANK, world.tanks), filter(ALLY_TANK(tank.id), world.tanks)) for obstacle in obstacles: next_position = physics.estimate_target_position(obstacle, tank) next_unit = fictive_unit(obstacle, next_position[0], next_position[1]) blocked = ( (physics.will_hit(tank, next_unit, DEAD_TANK_OBSTACLE_FACTOR) or physics.will_hit(tank, obstacle, DEAD_TANK_OBSTACLE_FACTOR)) and tank.get_distance_to_unit(obstacle) < tank.get_distance_to( est_pos.x, est_pos.y)) if blocked: return obstacle for bonus in world.bonuses: if (physics.will_hit(tank, bonus, BONUS_FACTOR)): dist_tank_to_bonus = tank.get_distance_to_unit(bonus) dist_tank_to_pos = tank.get_distance_to(est_pos.x, est_pos.y) if dist_tank_to_bonus < dist_tank_to_pos: dist_bonus_to_pos = bonus.get_distance_to(est_pos.x, est_pos.y) free_to_hit = dist_tank_to_bonus > 300 and dist_tank_to_bonus > 2 * dist_bonus_to_pos if not free_to_hit: return bonus bunker_obstacle = world.obstacles[0] if (physics.will_hit(tank, bunker_obstacle, 1.06) and tank.get_distance_to_unit(bunker_obstacle) < tank.get_distance_to(est_pos.x, est_pos.y)): return bunker_obstacle return False
def single_attacker(): #estimate_pos = target_v + target_direction * ((target_avoid_distance_forward + target_avoid_distance_backward) / 2) #vulnerable_width = max(90 * target_turret_n_cos, 60 * (1 - target_turret_n_cos)) target_avoid_distance_forward_new = target_avoid_distance_forward * context.memory.tank_precision[tank.id] target_avoid_distance_backward_new = target_avoid_distance_backward * context.memory.tank_precision[tank.id] max_pos = target_v + target_avoid_distance_forward_new * target_direction min_pos = target_v + target_avoid_distance_backward_new * target_direction max_pos_fu = fictive_unit(target, max_pos.x, max_pos.y) min_pos_fu = fictive_unit(target, min_pos.x, min_pos.y) #shoot = physics.will_hit(tank, max_pos_fu, 0.9) and physics.will_hit(tank, min_pos_fu, 0.9) shoot_precise = physics.will_hit_precise(tank, max_pos_fu, factor=0.8, side_part=0.8) and \ physics.will_hit_precise(tank, min_pos_fu, factor=0.8, side_part=0.8) and \ physics.will_hit_precise(tank, target, factor=0.8, side_part=0.8) shoot = shoot_precise fabs(target_turret_n_cos) #all_corners = get_unit_corners(max_pos_fu) + get_unit_corners(min_pos_fu) all_corners = get_unit_corners(target) #closest_corner = min(all_corners, key=lambda c: c.distance(tank_v)) # Instead of closest corner try to target middle of colsest side sc1, sc2 = sorted(all_corners, key=lambda c: c.distance(tank_v))[:2] closest_corner = 0.75 * sc1 + 0.25 * sc2 middle_position = (max_pos + min_pos)/2 w = fabs(target_turret_n_cos)**2 estimate_pos = w*middle_position + (1 - w) * closest_corner comment = 'SINGLE, %s' % w if obstacle_is_attacked(context, estimate_pos): shoot = False comment += ' blocked' if context.debug_mode: if shoot and tank.remaining_reloading_time == 0: debug_data = { "units": [min_pos_fu, max_pos_fu, target], "tanks": [tank] } debug_dump(debug_data, "/".join([str(context.memory.battle_id), str(context.world.tick) + "_" + str(tank.id) + "_single"])) return ((estimate_pos.x, estimate_pos.y), shoot, target_avoid_distance_forward, target_avoid_distance_backward, comment)
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 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 multiple_attackers(attackers): cnt = len(allies_targeting) ind = index_in_sorted(allies_targeting, tank.id) segment = (target_avoid_distance_forward - target_avoid_distance_backward)/(cnt + 1) shift = segment * (ind + 1) + target_avoid_distance_backward #shift * if cnt == 2: if ind == 0: shift = target_avoid_distance_backward + 45 else: shift = target_avoid_distance_forward - 45 estimate_pos = target_v + target_direction * shift shift_fu = fictive_unit(target, estimate_pos.x, estimate_pos.y) shoot = (physics.will_hit_precise(tank, shift_fu, factor=0.8) and all([lambda a: a.remaining_reloading_time < MULTIPLE_MAX_TIME_DIFFERENCE or a.reloading_time - a.remaining_reloading_time < MULTIPLE_MAX_TIME_DIFFERENCE, attackers]) #and target_avoid_distance_forward - target_avoid_distance_backward < 200 ) comment = 'MULTIPLE(%d), shift=%8.2f' % (ind, shift) if obstacle_is_attacked(context, estimate_pos): shoot = False comment += ' blocked' if context.debug_mode: if shoot and tank.remaining_reloading_time == 0 and all([context.memory.good_to_shoot.get(t.id) or t.id == tank.id for t in attackers]): max_pos = target_v + target_avoid_distance_forward * target_direction min_pos = target_v + target_avoid_distance_backward * target_direction max_pos_fu = fictive_unit(target, max_pos.x, max_pos.y) min_pos_fu = fictive_unit(target, min_pos.x, min_pos.y) debug_data = { "units": [min_pos_fu, max_pos_fu, target], "tanks": attackers } debug_dump(debug_data, "/".join([str(context.memory.battle_id), str(context.world.tick) + "_" + str(tank.id) + "_multiple"])) return ((estimate_pos.x, estimate_pos.y), shoot, target_avoid_distance_forward, target_avoid_distance_backward, comment)
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 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
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 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
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)
def debug_value(self, target): tank = self.context.tank physics = self.context.physics b = tank.angle + tank.turret_relative_angle e = Vector(1, 0) q = e.rotate(b) target_v = Vector(target.x, target.y) target_direction = Vector(1, 0).rotate(target.angle) target_speed = Vector(target.speedX, target.speedY) def get_hit_time(): v0 = INITIAL_SHELL_VELOCITY a = SHELL_ACCELERATION d = max(0, tank.get_distance_to_unit(target) - 60) return solve_quadratic(a / 2, v0, -d) def max_move_distance(v0, a, max_v, t): #TODO: this estimation is rough v0 = max(-max_v, min(v0, max_v)) if fabs(v0 + a * t) > max_v: if a > 0: t1 = fabs((max_v - v0) / a) else: t1 = fabs((-max_v - v0) / a) t2 = t - t1 else: t1 = t t2 = 0 if a > 0: return a * t1**2 / 2 + v0 * t1 + max_v * t2 else: return a * t1**2 / 2 + v0 * t1 - max_v * t2 t = get_hit_time() center = Vector(target.x - tank.x, target.y - tank.y) v0 = target_speed.projection(target_direction) target_avoid_distance_forward = max_move_distance( v0, FICTIVE_TARGET_ACCELERATION, MAX_TARGET_SPEED, t) target_avoid_distance_backward = max_move_distance( v0, -FICTIVE_TARGET_ACCELERATION * 0.75, MAX_TARGET_SPEED, t) max_pos = target_v + target_avoid_distance_forward * target_direction min_pos = target_v + target_avoid_distance_backward * target_direction target_turret_n_cos = fabs(cos(fabs(b - target.angle) + PI / 2)) var = fabs( (target_avoid_distance_forward - target_avoid_distance_backward) * target_turret_n_cos) estimate_pos = target_v + target_direction * ( (target_avoid_distance_forward + target_avoid_distance_backward) / 2) vulnerable_width = max(90 * target_turret_n_cos, 60 * (1 - target_turret_n_cos)) shoot = physics.will_hit( tank, fictive_unit( target, max_pos.x, max_pos.y)) and physics.will_hit( tank, fictive_unit(target, min_pos.x, min_pos.y)) return "fw=%s, bw=%s, t=%s, var=%s, wid=%s, degree=%s, shoot=%s" % ( int(target_avoid_distance_forward), int(target_avoid_distance_backward), int(t), int(var), vulnerable_width, fabs(tank.get_turret_angle_to(estimate_pos.x, estimate_pos.y)) / PI * 180, shoot)