Esempio n. 1
0
def shot_valid(agent: MyHivemind,
               shot: Union[AerialShot, JumpShot, Aerial],
               threshold: float = 45) -> bool:
    # Returns True if the ball is still where the shot anticipates it to be
    # First finds the two closest slices in the ball prediction to shot's intercept_time
    # threshold controls the tolerance we allow the ball to be off by
    slices = agent.get_ball_prediction_struct().slices
    soonest = 0
    latest = len(slices) - 1
    while len(slices[soonest:latest + 1]) > 2:
        midpoint = (soonest + latest) // 2
        if slices[midpoint].game_seconds > shot.intercept_time:
            latest = midpoint
        else:
            soonest = midpoint
    # preparing to interpolate between the selected slices
    dt = slices[latest].game_seconds - slices[soonest].game_seconds
    time_from_soonest = shot.intercept_time - slices[soonest].game_seconds
    slopes = (Vector3(slices[latest].physics.location) -
              Vector3(slices[soonest].physics.location)) * (1 / dt)
    # Determining exactly where the ball will be at the given shot's intercept_time
    predicted_ball_location = Vector3(
        slices[soonest].physics.location) + (slopes * time_from_soonest)
    # Comparing predicted location with where the shot expects the ball to be
    return (shot.ball_location -
            predicted_ball_location).magnitude() < threshold
Esempio n. 2
0
def find_hits(drone: CarObject, agent: MyHivemind, targets):
    # find_hits takes a dict of (left,right) target pairs and finds routines that could hit the ball
    # between those target pairs
    # find_hits is only meant for routines that require a defined intercept time/place in the future
    # find_hits should not be called more than once in a given tick,
    # as it has the potential to use an entire tick to calculate

    # Example Useage:
    # targets = {"goal":(opponent_left_post,opponent_right_post), "anywhere_but_my_net":(my_right_post,my_left_post)}
    # hits = find_hits(agent,targets)
    # print(hits)
    # >{"goal":[a ton of jump and aerial routines,in order from soonest to latest],
    # "anywhere_but_my_net":[more routines and stuff]}
    hits = {name: [] for name in targets}
    struct = agent.get_ball_prediction_struct()

    # Begin looking at slices 0.25s into the future
    # The number of slices
    i = 15
    while i < struct.num_slices:
        # Gather some data about the slice
        intercept_time = struct.slices[i].game_seconds
        time_remaining = intercept_time - agent.time
        if time_remaining > 0:
            ball_location = Vector3(struct.slices[i].physics.location)
            ball_velocity = Vector3(
                struct.slices[i].physics.velocity).magnitude()

            if abs(ball_location[1]) > 5250:
                break  # abandon search if ball is scored at/after this point

            # determine the next slice we will look at, based on ball velocity (slower ball needs fewer slices)
            i += 15 - cap(int(ball_velocity // 150), 0, 13)

            car_to_ball = ball_location - drone.location
            # Adding a True to a vector's normalize will have it also return the magnitude of the vector
            direction, distance = car_to_ball.normalize(True)

            # How far the car must turn in order to face the ball, for forward and reverse
            forward_angle = direction.angle(drone.forward)
            backward_angle = math.pi - forward_angle

            # Accounting for the average time it takes to turn and face the ball
            # Backward is slightly longer as typically the car is moving forward and takes time to slow down
            forward_time = time_remaining - (forward_angle * 0.318)
            backward_time = time_remaining - (backward_angle * 0.418)

            # If the car only had to drive in a straight line, we ensure it has enough time to reach the ball
            # (a few assumptions are made)
            forward_flag = forward_time > 0.0 and (
                distance * 1.05 / forward_time) < (
                    2290 if drone.boost > distance / 100 else 1400)
            backward_flag = distance < 1500 and backward_time > 0.0 and (
                distance * 1.05 / backward_time) < 1200

            # Provided everything checks out, we begin to look at the target pairs
            if forward_flag or backward_flag:
                for pair in targets:
                    # First we correct the target coordinates to account for the ball's radius
                    # If swapped == True, the shot isn't possible because the ball wouldn't fit between the targets
                    left, right, swapped = post_correction(
                        ball_location, targets[pair][0], targets[pair][1])
                    if not swapped:
                        # Now we find the best direction to hit the ball in order to land it between the target points
                        left_vector = (left - ball_location).normalize()
                        right_vector = (right - ball_location).normalize()
                        best_shot_vector = direction.clamp(
                            left_vector, right_vector)

                        # Check to make sure our approach is inside the field
                        if in_field(ball_location - (200 * best_shot_vector),
                                    1):
                            # The slope represents how close the car is to the chosen vector, higher = better
                            # A slope of 1.0 would mean the car is 45 degrees off
                            slope = find_slope(best_shot_vector, car_to_ball)
                            if forward_flag:
                                if ball_location[2] <= 300 and slope > 0.0:
                                    hits[pair].append(
                                        JumpShot(ball_location, intercept_time,
                                                 best_shot_vector, slope))
                                if 300 < ball_location[
                                        2] < 600 and slope > 1.0 and (
                                            ball_location[2] -
                                            250) * 0.14 > drone.boost:
                                    hits[pair].append(
                                        AerialShot(ball_location,
                                                   intercept_time,
                                                   best_shot_vector))
                            elif backward_flag and ball_location[
                                    2] <= 280 and slope > 0.25:
                                hits[pair].append(
                                    JumpShot(ball_location, intercept_time,
                                             best_shot_vector, slope, -1))
    return hits