def estimate_time_to_position(self, x, y, tank): dist = tank.get_distance_to(x, y) vt = Vector(tank.speedX, tank.speedY) tank_v = Vector(tank.x, tank.y) pt_v = Vector(x, y) d = pt_v - tank_v if d.is_zero(): return 0 tank_d_v = Vector(1, 0).rotate(tank.angle) if vt.is_zero() or d.is_zero(): vd_angle = 0 else: vd_angle = vt.angle(d) if tank_d_v.is_zero() or d.is_zero(): d_angle = 0 else: d_angle = tank_d_v.angle(d) # Short distances fix if dist < 100: if fabs(d_angle) < LOW_ANGLE: v0 = vt.projection(d) return solve_quadratic(FICTIVE_ACCELERATION/2, v0, -dist) #return dist/6 elif PI - fabs(d_angle) < LOW_ANGLE: v0 = vt.projection(d) return solve_quadratic(FICTIVE_ACCELERATION/2 * 0.75, v0, -dist) #return dist/6 /0.75 if d.is_zero(): values = (0, 0, 0, 0, 0, 0, 0, 1) else: values = ( d_angle, dist, vd_angle, vt.length(), tank.angular_speed, tank.crew_health/tank.crew_max_health, d_angle ** 2, 1 ) return sum([x * y for (x, y) in zip(values, TIME_ESTIMATION_COEF)])
def estimate_time_to_position(self, x, y, tank): dist = tank.get_distance_to(x, y) vt = Vector(tank.speedX, tank.speedY) tank_v = Vector(tank.x, tank.y) pt_v = Vector(x, y) d = pt_v - tank_v if d.is_zero(): return 0 tank_d_v = Vector(1, 0).rotate(tank.angle) if vt.is_zero() or d.is_zero(): vd_angle = 0 else: vd_angle = vt.angle(d) if tank_d_v.is_zero() or d.is_zero(): d_angle = 0 else: d_angle = tank_d_v.angle(d) # Short distances fix if dist < 100: if fabs(d_angle) < LOW_ANGLE: v0 = vt.projection(d) return solve_quadratic(FICTIVE_ACCELERATION / 2, v0, -dist) #return dist/6 elif PI - fabs(d_angle) < LOW_ANGLE: v0 = vt.projection(d) return solve_quadratic(FICTIVE_ACCELERATION / 2 * 0.75, v0, -dist) #return dist/6 /0.75 if d.is_zero(): values = (0, 0, 0, 0, 0, 0, 0, 1) else: values = (d_angle, dist, vd_angle, vt.length(), tank.angular_speed, tank.crew_health / tank.crew_max_health, d_angle**2, 1) return sum([x * y for (x, y) in zip(values, TIME_ESTIMATION_COEF)])
def value(self, target): tank = self.context.tank tank_v = Vector(tank.x, tank.y) target_v = Vector(target.x, target.y) #b = tank.angle + tank.turret_relative_angle b = (target_v - tank_v).angle(Vector(1, 0)) 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): 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() v0 = target_speed.projection(target_direction) target_health_fraction = target.crew_health / target.crew_max_health efficency = (1 + target_health_fraction) / 2 target_avoid_distance_forward = max_move_distance( v0, FICTIVE_TARGET_ACCELERATION * efficency, MAX_TARGET_SPEED * efficency, t) target_avoid_distance_backward = max_move_distance( v0, -FICTIVE_TARGET_ACCELERATION * 0.75 * efficency, MAX_TARGET_SPEED * efficency, t) 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) return (1 - var / 200) * self.max_value
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)
def get_target_data(context): # New Decision Maker tank = context.tank target = context.cur_target physics = context.physics b = tank.angle + tank.turret_relative_angle #e = Vector(1, 0) #q = e.rotate(b) tank_v = Vector(tank.x, tank.y) 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) t = get_hit_time() #center = Vector(target.x - tank.x, target.y - tank.y) v0 = target_speed.projection(target_direction) target_health_fraction = target.crew_health/target.crew_max_health efficency = (1 + target_health_fraction)/2 target_avoid_distance_forward = physics.max_move_distance(v0, FICTIVE_TARGET_ACCELERATION * efficency, MAX_TARGET_SPEED * efficency, t) target_avoid_distance_backward = physics.max_move_distance(v0, -FICTIVE_TARGET_ACCELERATION * BACKWARDS_FICTIVE_MULTIPLIER * efficency, MAX_TARGET_SPEED * efficency, 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) allies_targeting = inverse_dict(context.memory.target_id, target.id) 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 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) context.memory.good_to_shoot[tank.id] = False try_single = single_attacker() if len(allies_targeting) == 2 and try_single[1] == False: if tank.id in allies_targeting: attackers = [] for t in context.world.tanks: if t.id in allies_targeting: attackers.append(t) if all([lambda a: a.remaining_reloading_time < 70 or a.reloading_time - a.remaining_reloading_time < MULTIPLE_MAX_TIME_DIFFERENCE, attackers]): try_multiple = multiple_attackers(attackers) if try_multiple[1]: if tank.remaining_reloading_time < 3: context.memory.good_to_shoot[tank.id] = True ok = all([context.memory.good_to_shoot.get(t.id) for t in attackers]) if not ok: try_multiple = (try_multiple[0], False) return try_multiple return try_single
def get_target_data(context): # New Decision Maker tank = context.tank target = context.cur_target physics = context.physics b = tank.angle + tank.turret_relative_angle #e = Vector(1, 0) #q = e.rotate(b) tank_v = Vector(tank.x, tank.y) 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) t = get_hit_time() #center = Vector(target.x - tank.x, target.y - tank.y) v0 = target_speed.projection(target_direction) target_health_fraction = target.crew_health / target.crew_max_health efficency = (1 + target_health_fraction) / 2 target_avoid_distance_forward = physics.max_move_distance( v0, FICTIVE_TARGET_ACCELERATION * efficency, MAX_TARGET_SPEED * efficency, t) target_avoid_distance_backward = physics.max_move_distance( v0, -FICTIVE_TARGET_ACCELERATION * BACKWARDS_FICTIVE_MULTIPLIER * efficency, MAX_TARGET_SPEED * efficency, 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) allies_targeting = inverse_dict(context.memory.target_id, target.id) 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 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) context.memory.good_to_shoot[tank.id] = False try_single = single_attacker() if len(allies_targeting) == 2 and try_single[1] == False: if tank.id in allies_targeting: attackers = [] for t in context.world.tanks: if t.id in allies_targeting: attackers.append(t) if all([ lambda a: a.remaining_reloading_time < 70 or a. reloading_time - a.remaining_reloading_time < MULTIPLE_MAX_TIME_DIFFERENCE, attackers ]): try_multiple = multiple_attackers(attackers) if try_multiple[1]: if tank.remaining_reloading_time < 3: context.memory.good_to_shoot[tank.id] = True ok = all([ context.memory.good_to_shoot.get(t.id) for t in attackers ]) if not ok: try_multiple = (try_multiple[0], False) return try_multiple return try_single