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 positions(self, tank, world): positions = [] # Current position positions.append(Position(tank.x, tank.y, "CURRENT")) # Forward and back direction tank_v = Vector(tank.x, tank.y) tank_d_v = Vector(1, 0).rotate(tank.angle) forw = tank_v + tank_d_v * self.short_direction back = tank_v - tank_d_v * self.short_direction positions.append(Position(forw.x, forw.y, "FORWARD")) positions.append(Position(back.x, back.y, "BACKWARD")) fforw = tank_v + tank_d_v * self.long_direction fback = tank_v - tank_d_v * self.long_direction positions.append(Position(fforw.x, fforw.y, "FAR FORWARD")) positions.append(Position(fback.x, fback.y, "FAR BACKWARD")) # Low diagonal move DIAGONAL_ANGLE = PI/6 for a in [DIAGONAL_ANGLE, - DIAGONAL_ANGLE, PI - DIAGONAL_ANGLE, DIAGONAL_ANGLE - PI]: pt = tank_v + tank_d_v.rotate(a) * 100 positions.append(Position(pt.x, pt.y, "TURN %.0f" % degrees(a))) # Bonuses positions positions += [Position(b.x, b.y, "BONUS %s" % bonus_name_by_type(b.type)) for b in world.bonuses] return positions
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 will_hit_precise(self, tank, target, ricochet_angle=PI/4, factor=1, side_part=1): """ Returns True if tank will hit rectangular object """ b = tank.angle + tank.turret_relative_angle e = Vector(1, 0) q = e.rotate(b) tank_v = Vector(tank.x, tank.y) c = get_unit_corners(target, factor) hit_v = tank_v + MAX_DISTANCE * q def will_hit_side(p1, p2): p_mid = (p1 + p2)/2 p1_new = p_mid + (p1 - p_mid) * side_part p2_new = p_mid + (p2 - p_mid) * side_part intersecting = segments_are_intersecting(tank_v, hit_v, p1_new, p2_new) angle = q.angle(p2 - p1) safe = ricochet_angle < angle < PI - ricochet_angle return intersecting and safe sides = [(c[0], c[1]), (c[1], c[2]), (c[2], c[3]), (c[3], c[0])] closest_corner = min(c, key=lambda x: x.distance(tank_v)) closer_sides = list(filter(lambda s: s[0] == closest_corner or s[1] == closest_corner, sides)) #result = will_hit_side(c1, c2) or will_hit_side(c2, c3) or will_hit_side(c3, c4) or will_hit_side(c4, c1) result = will_hit_side(closer_sides[0][0], closer_sides[0][1]) or will_hit_side(closer_sides[1][0], closer_sides[1][1]) return result
def positions(self, tank, world): positions = [] # Current position positions.append(Position(tank.x, tank.y, "CURRENT")) # Forward and back direction tank_v = Vector(tank.x, tank.y) tank_d_v = Vector(1, 0).rotate(tank.angle) forw = tank_v + tank_d_v * self.short_direction back = tank_v - tank_d_v * self.short_direction positions.append(Position(forw.x, forw.y, "FORWARD")) positions.append(Position(back.x, back.y, "BACKWARD")) fforw = tank_v + tank_d_v * self.long_direction fback = tank_v - tank_d_v * self.long_direction positions.append(Position(fforw.x, fforw.y, "FAR FORWARD")) positions.append(Position(fback.x, fback.y, "FAR BACKWARD")) # Low diagonal move DIAGONAL_ANGLE = PI / 6 for a in [ DIAGONAL_ANGLE, -DIAGONAL_ANGLE, PI - DIAGONAL_ANGLE, DIAGONAL_ANGLE - PI ]: pt = tank_v + tank_d_v.rotate(a) * 100 positions.append(Position(pt.x, pt.y, "TURN %.0f" % degrees(a))) # Bonuses positions positions += [ Position(b.x, b.y, "BONUS %s" % bonus_name_by_type(b.type)) for b in world.bonuses ] return positions
def positions(self, tank, world): positions = [] # Low diagonal move tank_v = Vector(tank.x, tank.y) tank_d_v = Vector(1, 0).rotate(tank.angle) for a in [self.angle, - self.angle, PI - self.angle, self.angle - PI]: pt = tank_v + tank_d_v.rotate(a) * 100 positions.append(Position(pt.x, pt.y, "TURN %.0f" % degrees(a))) return positions
def draw_tank(tank): draw_unit(tank) b = tank.angle + tank.turret_relative_angle e = Vector(1, 0) q = e.rotate(b) tank_v = Vector(tank.x, tank.y) hit_v = tank_v + MAX_DISTANCE * q pygame.draw.line(window, (255, 0, 0), (tank.x, tank.y), (hit_v.x, hit_v.y))
def positions(self, tank, world): positions = [] # Low diagonal move tank_v = Vector(tank.x, tank.y) tank_d_v = Vector(1, 0).rotate(tank.angle) for a in [self.angle, -self.angle, PI - self.angle, self.angle - PI]: pt = tank_v + tank_d_v.rotate(a) * 100 positions.append(Position(pt.x, pt.y, "TURN %.0f" % degrees(a))) return positions
def will_hit_precise(self, tank, target, ricochet_angle=PI / 4, factor=1, side_part=1): """ Returns True if tank will hit rectangular object """ b = tank.angle + tank.turret_relative_angle e = Vector(1, 0) q = e.rotate(b) tank_v = Vector(tank.x, tank.y) c = get_unit_corners(target, factor) hit_v = tank_v + MAX_DISTANCE * q def will_hit_side(p1, p2): p_mid = (p1 + p2) / 2 p1_new = p_mid + (p1 - p_mid) * side_part p2_new = p_mid + (p2 - p_mid) * side_part intersecting = segments_are_intersecting(tank_v, hit_v, p1_new, p2_new) angle = q.angle(p2 - p1) safe = ricochet_angle < angle < PI - ricochet_angle return intersecting and safe sides = [(c[0], c[1]), (c[1], c[2]), (c[2], c[3]), (c[3], c[0])] closest_corner = min(c, key=lambda x: x.distance(tank_v)) closer_sides = list( filter(lambda s: s[0] == closest_corner or s[1] == closest_corner, sides)) #result = will_hit_side(c1, c2) or will_hit_side(c2, c3) or will_hit_side(c3, c4) or will_hit_side(c4, c1) result = will_hit_side(closer_sides[0][0], closer_sides[0][1]) or will_hit_side( closer_sides[1][0], closer_sides[1][1]) return result
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)