Beispiel #1
0
    def direct_shot(self, car: Car, target: vec3) -> Maneuver:
        dodge_shot = DodgeStrike(car, self.info, target)
        ground_shot = GroundStrike(car, self.info, target)

        if car.boost > 40:  # TODO
            aerial_strike = AerialStrike(car, self.info, target)
            fast_aerial = FastAerialStrike(car, self.info, target)

            better_aerial_strike = min(
                [aerial_strike, fast_aerial],
                key=lambda strike: strike.intercept.time)

            if better_aerial_strike.intercept.time < dodge_shot.intercept.time:
                if ground_distance(better_aerial_strike.intercept,
                                   self.info.their_goal.center) < 5000:
                    return DoubleTouch(better_aerial_strike)
                return better_aerial_strike

        if (dodge_shot.intercept.time < ground_shot.intercept.time - 0.1
                or ground_distance(dodge_shot.intercept, target) < 4000
                or distance(ground_shot.intercept.ball.velocity,
                            car.velocity) < 500):
            if (distance(dodge_shot.intercept.ground_pos, target) < 4000
                    and abs(dodge_shot.intercept.ground_pos[0]) < 3000):
                return CloseShot(car, self.info, target)
            return dodge_shot
        return ground_shot
Beispiel #2
0
def direct_shot(info: GameInfo, car: Car, target: vec3) -> Strike:
    dodge_shot = DodgeStrike(car, info, target)
    ground_shot = GroundStrike(car, info, target)

    if car.boost > 40:  # TODO
        # aerial_strike = AerialStrike(car, info, target)
        fast_aerial = FastAerialStrike(car, info, target)

        better_aerial_strike = min([fast_aerial],
                                   key=lambda strike: strike.intercept.time)

        if (better_aerial_strike.intercept.time < dodge_shot.intercept.time
                and abs(better_aerial_strike.intercept.position[1] -
                        info.their_goal.center[1]) > 500):
            if ground_distance(better_aerial_strike.intercept,
                               info.their_goal.center) < 8000:
                return DoubleTouch(better_aerial_strike)
            return better_aerial_strike

    if (dodge_shot.intercept.time < ground_shot.intercept.time - 0.1
            or ground_distance(dodge_shot.intercept, target) < 2000 or
            distance(ground_shot.intercept.ball.velocity, car.velocity) < 500
            or is_opponent_close(info, 300)):
        if (distance(dodge_shot.intercept.ground_pos, target) < 4000
                and abs(dodge_shot.intercept.ground_pos[0]) < 2000):
            return CloseShot(car, info, target)
        return dodge_shot
    return ground_shot
    def step(self, dt):
        if not self.flicking:
            self.carry.step(dt)
            self.controls = self.carry.controls
            self.finished = self.carry.finished
            car = self.car
            ball = self.info.ball

            # check if it's a good idea to flick
            dir_to_target = ground_direction(car, self.target)
            if (distance(car, ball) < 150 and ground_distance(car, ball) < 100
                    and dot(car.forward(), dir_to_target) > 0.7
                    and norm(car.velocity) > clamp(
                        distance(car, self.target) / 3, 1000, 1700)
                    and dot(dir_to_target, ground_direction(car, ball)) > 0.9):
                self.flicking = True

            # flick if opponent is close
            for opponent in self.info.get_opponents():
                if (distance(opponent.position + opponent.velocity, car) < max(
                        300.0,
                        norm(opponent.velocity) * 0.5)
                        and dot(opponent.velocity,
                                direction(opponent, self.info.ball)) > 0.5):
                    if distance(car.position, self.info.ball.position) < 200:
                        self.flicking = True
                    else:
                        self.finished = True
        else:
            self.flick.target = self.info.ball.position + self.info.ball.velocity * 0.2
            self.flick.step(dt)
            self.controls = self.flick.controls
            self.finished = self.flick.finished
def choose_maneuver(info: GameInfo, my_car: Car):
    ball = info.ball
    teammates = info.get_teammates(my_car)
    my_team = [my_car] + teammates
    their_goal = ground(info.their_goal.center)
    my_goal = ground(info.my_goal.center)

    # recovery
    if not my_car.on_ground:
        return Recovery(my_car)

    # kickoff
    if ball.position[0] == 0 and ball.position[1] == 0:

        # if I'm nearest to the ball, go for kickoff
        if min(my_team, key=lambda car: distance(car, ball)) is my_car:
            return kickoffs.choose_kickoff(info, my_car)

    if my_car.boost < 20:
        return Refuel(my_car, info)

    info.predict_ball()

    my_intercept = Intercept(my_car, info.ball_predictions)
    teammates_intercepts = [
        Intercept(mate, info.ball_predictions) for mate in teammates
    ]
    our_intercepts = teammates_intercepts + [my_intercept]

    good_intercepts = [
        i for i in our_intercepts
        if align(i.car.position, i.ball, their_goal) > 0.0
    ]
    if good_intercepts:
        best_intercept = min(good_intercepts,
                             key=lambda intercept: intercept.time)
    else:
        best_intercept = min(our_intercepts,
                             key=lambda i: distance(i.car, my_goal))

    if best_intercept is my_intercept:
        # if not completely out of position, go for a shot
        if (align(my_intercept.car.position, my_intercept.ball, their_goal) > 0
                or ground_distance(my_intercept, my_goal) > 6000):
            return offense.any_shot(info, my_intercept.car, their_goal,
                                    my_intercept)

        # otherwise try to clear
        else:
            return defense.any_clear(info, my_intercept.car)

    # if I'm nearest to goal, stay far back
    if min(my_team, key=lambda car: distance(car, my_goal)) is my_car:
        return GeneralDefense(my_car, info, my_intercept.position, 7000)

    # otherwise get into position
    return GeneralDefense(my_car, info, my_intercept.position, 4000)
    def set_kickoff_maneuvers(self, drones: List[Drone]):
        nearest_drone = min(
            drones,
            key=lambda drone: ground_distance(drone.car, self.info.ball))
        nearest_drone.maneuver = kickoffs.choose_kickoff(
            self.info, nearest_drone.car)
        self.drone_going_for_ball = nearest_drone

        self.boost_reservations.clear()
        corner_drones = [
            drone for drone in drones if abs(drone.car.position[0]) > 2000
        ]
        if len(corner_drones) > 1:
            other_corner_drone = next(drone for drone in corner_drones
                                      if drone is not nearest_drone)
            nearest_pad = min(
                self.info.large_boost_pads,
                key=lambda pad: distance(other_corner_drone.car, pad))
            other_corner_drone.maneuver = HalfFlipPickup(
                other_corner_drone.car, nearest_pad)
            self.boost_reservations[other_corner_drone] = nearest_pad

        self.defending_drone = max(
            drones,
            key=lambda drone: ground_distance(drone.car, self.info.ball))
        self.defending_drone.maneuver = DriveBackwardsToGoal(
            self.defending_drone.car, self.info)

        for drone in drones:
            if drone not in corner_drones + [self.defending_drone
                                             ] + [self.drone_going_for_ball]:
                self.send_drone_for_boost(drone)
Beispiel #6
0
    def step(self, dt):
        car = self.car

        if self.phase == 1:
            if norm(car.velocity) > 1400:
                self.phase = 2
                self.action = AirDodge(car, 0.05, car.position + car.velocity)

        if self.phase == 2:
            self.action.controls.boost = self.action.state_timer < 0.1

            if car.on_ground and self.action.finished:
                self.action = self.drive
                self.phase = 3

        if self.phase == 3:
            if distance(car, vec3(0, 0, 93)) < norm(car.velocity) * 0.4:
                self.phase = 4
                self.action = AirDodge(car, 0.05, self.info.ball.position)

                self.counter_fake_kickoff()
        
        if self.phase == 4:
            if self.action.finished:
                self.finished = True

        super().step(dt)
Beispiel #7
0
    def detect_collisions(self,
                          time_limit=0.5,
                          dt=1 / 60) -> List[Tuple[int, int, float]]:
        """Returns a list of tuples, where the first two elements are
        indices of cars and the last is time from now until the collision.
        """
        time_steps = int(time_limit / dt)
        predictions = [
            self.predict_car_drive(i, time_limit=time_limit, dt=dt)
            for i in range(self.num_cars)
        ]
        collisions = []
        for i in range(self.num_cars):
            for j in range(self.num_cars):
                if i >= j:
                    continue

                for step in range(time_steps):
                    pos1 = predictions[i][step]
                    pos2 = predictions[j][step]
                    if distance(pos1, pos2) < self.COLLISION_THRESHOLD:
                        collisions.append((i, j, step * dt))
                        break

        return collisions
Beispiel #8
0
def any_shot(info: GameInfo,
             car: Car,
             target: vec3,
             intercept: Intercept,
             allow_dribble=False) -> Maneuver:
    ball = intercept.ball

    if (allow_dribble
            and (ball.position[2] > 100 or abs(ball.velocity[2]) > 250
                 or distance(car, info.ball) < 300)
            and abs(ball.velocity[2]) < 700
            and ground_distance(car, ball) < 1500
            and ground_distance(ball, info.my_goal.center) > 1000
            and ground_distance(ball, info.their_goal.center) > 1000
            and not is_opponent_close(info, info.ball.position[2] * 2 + 1000)):
        return CarryAndFlick(car, info, target)

    direct = direct_shot(info, car, target)

    if not isinstance(direct,
                      GroundStrike) and intercept.time < car.time + 4.0:
        alignment = align(car.position, ball, target)
        if alignment < -0.3 and abs(ball.position[1] - target[1]) > 3000:
            return MirrorStrike(car, info, target)

    return direct
Beispiel #9
0
    def step(self, dt):
        # slow down when we're about to pick up the boost, so we can turn faster afterwards
        if distance(self.car, self.pad) < norm(self.car.velocity) * 0.2:
            self.travel.drive.target_speed = 1400

        self.travel.step(dt)
        self.controls = self.travel.controls

        # finish when someone picks up the pad
        if self.pad_was_active and self.pad.state == BoostPadState.Unavailable:
            self.finished = True
        self.pad_was_active = self.pad.state == BoostPadState.Available

        # finish when we picked the boost up but the previous condition somehow wasn't true
        if self.car.boost > 99 or distance(self.car, self.pad) < 100:
            self.finished = True
Beispiel #10
0
    def step(self, dt):
        ball = Ball(self.ball)
        car = self.car

        # simulate ball until it gets near the floor
        while (ball.position[2] > 120 or ball.velocity[2] > 0) and ball.time < car.time + 10:
            ball.step(1/60)

        ball_local = local(car, ground(ball.position))
        target = local(car, self.target)

        shift = ground(direction(ball_local, target))
        shift[1] *= 1.8
        shift = normalize(shift)
        
        max_turn = clamp(norm(car.velocity) / 800, 0, 1)
        max_shift = normalize(vec3(1 - max_turn, max_turn * sign(shift[1]), 0))

        if abs(shift[1]) > abs(max_shift[1]) or shift[0] < 0:
            shift = max_shift
        shift *= clamp(car.boost, 40, 60)

        shift[1] *= clamp(norm(car.velocity)/1000, 1, 2)

        self._shift_direction = normalize(world(car, shift) - car.position)

        target = world(car, ball_local - shift)
        speed = distance(car.position, target) / max(0.001, ball.time - car.time)

        self.drive.target_speed = speed
        self.drive.target_pos = target

        self.drive.step(dt)
        self.controls = self.drive.controls
        self.finished = self.ball.position[2] < 100 or ground_distance(self.ball, self.car) > 2000
Beispiel #11
0
    def counter_fake_kickoff(self):
        if any(
                distance(self.info.ball, opponent) < 1500
                for opponent in self.info.get_opponents()):
            return

        self.phase = "anti-fake-kickoff"
        self.action = self.drive
Beispiel #12
0
def estimate_time(car: Car, target, speed, dd=1) -> float:
    travel = distance(car, target) / speed
    turning = angle_between(car.forward() * dd, direction(
        car, target)) / math.pi * 2
    if turning < 1:
        turning **= 2
    acceleration = (speed * dd -
                    dot(car.velocity, car.forward())) / 2100 * 0.2 * dd / max(
                        car.boost / 20, 1)
    return travel + acceleration + turning * 0.7
Beispiel #13
0
    def __init__(self, car: Car, target: vec3 = vec3(0, 0, 0), waste_boost=False):
        super().__init__(car)

        self.target = Arena.clamp(ground(target), 100)
        self.waste_boost = waste_boost
        self.finish_distance = 500

        self._time_on_ground = 0
        self.driving = True

        # decide whether to start driving backwards and halfflip later
        forward_estimate = estimate_time(car, self.target)
        backwards_estimate = estimate_time(car, self.target, -1) + 0.5
        backwards = (
                dot(car.velocity, car.forward()) < 500
                and backwards_estimate < forward_estimate
                and (distance(car, self.target) > 3000 or distance(car, self.target) < 300)
                and car.position[2] < 200
        )

        self.drive = Drive(car, self.target, 2300, backwards)
        self.action = self.drive
Beispiel #14
0
    def find_second_touch(self):
        self.info.predict_ball(duration=4.0)
        for i in range(0, len(self.info.ball_predictions), 5):
            ball = self.info.ball_predictions[i]
            if ball.position[2] < 500: break
            self.aerial.target = ball.position - direction(
                ball, self.aerial_strike.target) * 80
            self.aerial.arrival_time = ball.time
            final_car = AerialStrike.simulate_flight(self.car, self.aerial,
                                                     self._flight_path)
            if distance(final_car, self.aerial.target) < 50:
                return

        self.finished = True
Beispiel #15
0
    def best_boostpad_to_pickup(car: Car, pads: Set[Pad],
                                pos: vec3) -> Optional[Pad]:
        best_pad = None
        best_dist = math.inf

        for pad in pads:
            dist = distance(pos, pad.position)
            time_estimate = estimate_time(car, pad.position,
                                          estimate_max_car_speed(car))

            if dist < best_dist and (pad.is_active
                                     or pad.timer < time_estimate):
                best_pad = pad
                best_dist = dist

        return best_pad
    def step(self, dt):
        # update finished state even if we are not using the controls
        self.travel.step(dt)

        if self.travel.finished:
            # turn around to face the target direction
            if angle_to(self.car, self.face_target) > 0.3:
                self.drive.target_pos = self.face_target
                self.drive.target_speed = 1000
                self.drive.step(dt)
                self.controls = self.drive.controls
                self.controls.handbrake = False
            else:
                self.stop.step(dt)
                self.controls = self.stop.controls

        else:
            self.pad = None

            # collect boost pads on the way (greedy algorithm, assumes first found is best)
            if self.car.boost < 90 and self.travel.interruptible():
                to_target = ground_direction(self.car, self.travel.target)

                for pad in self.info.large_boost_pads + self.info.small_boost_pads:
                    to_pad = ground_direction(self.car, pad)

                    if (pad.is_active and
                            distance(self.car, pad) < self.BOOST_LOOK_RADIUS
                            and angle_between(to_target,
                                              to_pad) < self.BOOST_LOOK_ANGLE):
                        self.pad = pad
                        self.drive.target_pos = pad.position
                        self.drive.target_speed = 2200
                        self.drive.step(dt)
                        self.controls = self.drive.controls
                        break

            # go to the actual target
            if self.pad is None:
                self.controls = self.travel.controls

        # don't waste boost during downtime
        if self.car.boost < 100 and ground_distance(self.car,
                                                    self.travel.target) < 4000:
            self.controls.boost = False

        self.finished = self.travel.driving and self.car.time > self.start_time + self.DURATION
def choose_boostpad_to_pickup(info: GameInfo, car: Car, forbidden_pads: Set[BoostPad] = None) -> Optional[BoostPad]:
    if forbidden_pads is None:
        forbidden_pads = set()

    # consider pads which are available or going to spawn before we can reach them
    active_pads = {pad for pad in info.large_boost_pads if pad.state == BoostPadState.Available}
    soon_active_pads = {pad for pad in info.large_boost_pads if estimate_time(car, pad.position) * 0.7 > pad.timer}

    valid_pads = active_pads | soon_active_pads - forbidden_pads
    if not valid_pads:
        return None

    # a good candidate should be somewhere between us, our goal, and the ball
    # the easiest way to do that is to just take a weighted average of those positions
    pos = (info.ball.position + car.position * 2 + info.my_goal.center) / 4

    # and pick the closest valid pad to that position
    return min(valid_pads, key=lambda pad: distance(pad.position, pos))
Beispiel #18
0
    def step(self, dt):
        target = self.target_pos

        # don't try driving outside the arena
        target = Arena.clamp(target, 100)

        # smoothly escape goal
        if abs(self.car.position[1]) > Arena.size[1] - 50 and abs(
                self.car.position.x) < 1000:
            target = Arena.clamp(target, 200)
            target[0] = abs_clamp(target[0], 700)

        if not self.drive_on_walls:
            seam_radius = 100 if abs(
                self.car.position[1]) > Arena.size[1] - 100 else 200
            if self.car.position[2] > seam_radius:
                target = ground(self.car)

        local_target = local(self.car, target)

        if self.backwards:
            local_target[0] *= -1
            local_target[1] *= -1

        # steering
        phi = math.atan2(local_target[1], local_target[0])
        self.controls.steer = clamp11(3.0 * phi)

        # powersliding
        self.controls.handbrake = 0
        if (abs(phi) > 1.5 and self.car.position[2] < 300
                and (ground_distance(self.car, target) < 3500
                     or abs(self.car.position[0]) > 3500) and
                dot(normalize(self.car.velocity), self.car.forward()) > 0.85):
            self.controls.handbrake = 1

        # forward velocity
        vf = dot(self.car.velocity, self.car.forward())
        if self.backwards:
            vf *= -1

        # speed controller
        if vf < self.target_speed:
            self.controls.throttle = 1.0
            if self.target_speed > 1400 and vf < 2250 and self.target_speed - vf > 50:
                self.controls.boost = 1
            else:
                self.controls.boost = 0
        else:
            if (vf - self.target_speed) > 400:  # 75
                self.controls.throttle = -1.0
            elif (vf - self.target_speed) > 100:
                if self.car.up()[2] > 0.85:
                    self.controls.throttle = 0.0
                else:
                    self.controls.throttle = 0.01
            self.controls.boost = 0

        # backwards driving
        if self.backwards:
            self.controls.throttle *= -1
            self.controls.steer *= -1
            self.controls.boost = 0
            self.controls.handbrake = 0

        # don't boost if not facing target
        if abs(phi) > 0.3:
            self.controls.boost = 0

        # finish when close
        if distance(self.car, self.target_pos) < 100:
            self.finished = True
Beispiel #19
0
    def choose_maneuver(self, car: Car):
        info = self.info
        offense = self.offense
        ball = info.ball

        teammates = info.get_teammates(car)
        opponents = info.get_opponents(car)

        their_goal = ground(info.their_goal.center)
        my_goal = ground(info.my_goal.center)

        my_hit = Intercept(car, info.ball_predictions)
        their_best_hit = self.best_intercept(opponents)
        opponent = their_best_hit.car

        # recovery
        if not car.on_ground:
            return Recovery(car)

        # kickoff
        should_go = all(
            distance(mate, ball) > distance(car, ball) for mate in teammates)
        if should_go and ball.position[0] == 0 and ball.position[1] == 0:
            return KickoffStrategy.choose_kickoff(info, car)

        # don't save our own shots
        if info.about_to_score:
            if info.time_of_goal < their_best_hit.time - 2:
                return Stop(car)

        # save
        if info.about_to_be_scored_on:

            if align(car.position, my_hit.ball, their_goal) > 0.0:

                return offense.direct_shot(car, their_goal)

            return self.defense.any_clear(car)

        # fallback
        if align(car.position, my_hit.ball, my_goal) > 0.2:
            if (ground_distance(my_hit, my_goal) < 4000
                    and abs(car.position[1]) < abs(my_hit.position[1])):
                return self.defense.any_clear(car)
            return GeneralDefense(car, info, my_hit.ground_pos, 6000)

        # clear
        if (ground_distance(my_hit, my_goal) < 3500
                and abs(my_hit.position[0]) < 3000
                and ground_distance(car, my_goal) < 2500):
            if align(car.position, my_hit.ball, their_goal) > 0:
                return offense.direct_shot(car, their_goal)
            return self.defense.any_clear(car)

        if distance(their_best_hit, their_goal) < distance(
                their_best_hit, my_goal):
            opponents_align = -align(opponent.position, their_best_hit.ball,
                                     their_goal)
        else:
            opponents_align = align(opponent.position, their_best_hit.ball,
                                    my_goal)

        # 1v1
        if not teammates:

            # I can get to ball faster than them
            if my_hit.time < their_best_hit.time - 0.8:
                strike = offense.any_shot(car, their_goal, my_hit)

                if not isinstance(strike, Strike):
                    return strike

                if strike.intercept.time < their_best_hit.time - 0.8 \
                and (not info.about_to_score or strike.intercept.time < info.time_of_goal - 1):

                    if strike.intercept.time - car.time > 4 and car.boost < 30 \
                    and distance(strike.intercept.ground_pos, their_goal) > 3000 and distance(their_best_hit.ground_pos, my_goal) > 5000:

                        return Refuel(car, info, my_hit.ground_pos)

                    if abs(strike.intercept.ground_pos[0]
                           ) > Arena.size[0] - 800 and car.boost < 30:

                        return Refuel(car, info, my_hit.ground_pos)

                    if abs(strike.intercept.ball.position[1] -
                           their_goal[1]) > 300 or ground_distance(
                               strike.intercept, their_goal) < 900:
                        return strike

            # they are out of position
            if (opponents_align < -0.1 and
                    my_hit.time < their_best_hit.time - opponents_align * 1.5):

                strike = offense.any_shot(car, their_goal, my_hit)

                if not isinstance(strike, Strike) or strike.intercept.is_viable \
                and (not info.about_to_score or strike.intercept.time < info.time_of_goal - 0.5):

                    if (car.boost < 40 and
                        (distance(my_hit, their_goal) > 5000
                         or abs(my_hit.position[0]) > Arena.size[0] - 1500)
                            and distance(opponent, their_best_hit) > 3000):
                        return Refuel(car, info, my_hit.ground_pos)

                    if not isinstance(strike, Strike) or abs(
                            strike.intercept.ball.position[1] -
                            their_goal[1]) > 300 or ground_distance(
                                strike.intercept, their_goal) < 900:
                        return strike

            if distance(their_best_hit.ball, my_goal) > 7000 and \
                (distance(their_best_hit, opponent) > 3000 or align(opponent.position, their_best_hit.ball, my_goal) < 0) and car.boost < 30:
                return Refuel(car, info, my_hit.ground_pos)

            if car.boost < 35 and distance(their_best_hit, opponent) > 3000:
                refuel = Refuel(car, info, my_hit.ground_pos)
                if estimate_time(car, refuel.pad.position, 1400) < 1.5:
                    return refuel

            if opponents_align < 0:
                return offense.any_shot(car, their_goal, my_hit)

        # teamplay
        else:
            if car.boost < 40:
                return Refuel(car, info, my_goal)
            else:
                return offense.any_shot(car, their_goal, my_hit)

        shadow_distance = 4000 + opponents_align * 1500
        shadow_distance = max(shadow_distance, 3000)
        return GeneralDefense(car, info, their_best_hit.ground_pos,
                              shadow_distance)
Beispiel #20
0
    def step(self, dt):
        time_left = self.aerial.arrival_time - self.car.time

        if self.aerialing:
            to_ball = direction(self.car, self.info.ball)

            # freestyling
            if self.car.position[2] > 200:
                # if time_left > 0.7:
                #     rotation = axis_to_rotation(self.car.forward() * 0.5)
                #     self.aerial.up = dot(rotation, self.car.up())
                # else:
                self.aerial.up = vec3(0, 0, -1) + xy(to_ball)

            self.aerial.target_orientation = look_at(to_ball,
                                                     vec3(0, 0, -3) + to_ball)
            self.aerial.step(dt)

            self.controls = self.aerial.controls
            self.finished = self.aerial.finished and time_left < -0.3

        else:
            super().step(dt)

            # simulate aerial from current state
            simulated_car = self.simulate_flight(self.car, self.aerial,
                                                 self._flight_path)

            speed_towards_target = dot(
                self.car.velocity,
                ground_direction(self.car, self.aerial.target_position))
            speed_needed = ground_distance(
                self.car, self.aerial.target_position) / time_left

            # too fast, slow down
            if speed_towards_target > speed_needed and angle_to(
                    self.car, self.aerial.target_position) < 0.1:
                self.controls.throttle = -1

            # if it ended up near the target, we could take off
            elif distance(
                    simulated_car,
                    self.aerial.target_position) < self.MAX_DISTANCE_ERROR:
                if angle_to(self.car,
                            self.aerial.target_position) < 0.1 or norm(
                                self.car.velocity) < 1000:

                    if self.DELAY_TAKEOFF and ground_distance(
                            self.car, self.aerial.target_position) > 1000:
                        # extrapolate current state a small amount of time
                        future_car = Car(self.car)
                        time = 0.5
                        future_car.time += time
                        displacement = future_car.velocity * time if norm(future_car.velocity) > 500\
                            else normalize(future_car.velocity) * 500 * time
                        future_car.position += displacement

                        # simulate aerial fot the extrapolated car again
                        future_simulated_car = self.simulate_flight(
                            future_car, self.aerial)

                        # if the aerial is also successful, that means we should continue driving instead of taking off
                        # this makes sure that we go for the most late possible aerials, which are the most effective
                        if distance(future_simulated_car, self.aerial.
                                    target_position) > self.MAX_DISTANCE_ERROR:
                            self.aerialing = True
                        else:
                            self.too_early = True
                    else:
                        self.aerialing = True

            else:
                # self.controls.boost = True
                self.controls.throttle = 1