示例#1
0
def get_controls(game_info, sub_state_machine):

    controls = SimpleControllerState()

    persistent = game_info.persistent

    if game_info.me.wheel_contact:
        #If still on ground, jump
        controls.jump = 1
    else:
        #Once we've jumped, dodge towards the ball
        controls = AirDodge(
            car_coordinates_2d(game_info.me,
                               game_info.ball.pos - game_info.me.pos),
            game_info.me.jumped_last_frame).input()

    return controls, persistent
示例#2
0
def offcenter(game_info, old_game_info, opponent_distance, x_sign, persistent):
    controller_input = SimpleControllerState()
    current_state = game_info.me
    old_state = old_game_info.me
    ball_angle = atan2((game_info.ball.pos - current_state.pos).y,
                       (game_info.ball.pos - current_state.pos).x)
    #Offset added to the ball position for our initial steer
    offset = Vec3(x_sign * 1200, 0, 0)

    first_boost = 7
    wobble = Vec3(current_state.omega.x, current_state.omega.y, 0).magnitude()
    epsilon = 0.01

    if abs(current_state.pos.y) > 3400:
        #If we're not near the center-line of the field, boost towards the first small boost
        controller_input = GroundTurn(
            current_state,
            current_state.copy_state(pos=game_info.ball.pos + offset)).input()
        controller_input.boost = 1

    elif abs(current_state.pos.y) > 900 and current_state.wheel_contact:
        controller_input.jump = 1
        controller_input.boost = 1

    elif abs(current_state.pos.y) > 900 and not current_state.double_jumped:
        #If we're far away, fast dodge to speed up.
        dodge_direction = car_coordinates_2d(
            current_state, (game_info.ball.pos - Vec3(x_sign * 2500, 0, 0)) -
            current_state.pos)
        controller_input = CancelledFastDodge(current_state,
                                              dodge_direction).input()
        controller_input.boost = 1

    elif abs(current_state.pos.y) < 250:
        #If both players are close to the ball, dodge into the ball.
        controller_input = AirDodge(Vec3(0, 1, 0),
                                    current_state.jumped_last_frame).input()
        controller_input.boost = 1

    elif current_state.wheel_contact and wobble < epsilon:
        #If we're approaching the ball and stable enough to jump again,
        #jump turn into ball to prep for the dodge
        if current_state.rot.yaw > ball_angle:
            direction = -1
        else:
            direction = 1
        controller_input = JumpTurn(current_state, 0, direction).input()

    elif current_state.wheel_contact:
        controller_input = GroundTurn(
            current_state,
            current_state.copy_state(pos=game_info.ball.pos)).input()
        controller_input.boost = 1

    else:
        vel_angle = atan2(current_state.vel.y, current_state.vel.x)
        target_rot = Orientation(pyr=[0, vel_angle, 0])
        controller_input, persistent = aerial_rotation(target_rot,
                                                       game_info.dt,
                                                       persistent)
        controller_input.boost = 1

    return controller_input, persistent
示例#3
0
def far_back(game_info, old_game_info, opponent_distance, persistent):
    controller_input = SimpleControllerState()
    current_state = game_info.me
    old_state = old_game_info.me
    ball_angle = atan2((game_info.ball.pos - current_state.pos).y,
                       (game_info.ball.pos - current_state.pos).x)

    #Set which boost we want based on team.
    if game_info.team_sign == 1:
        first_boost = 7
    else:
        first_boost = 26

    if abs(current_state.pos.y) > 4000:
        #Boost to start
        controller_input = GroundTurn(
            current_state,
            current_state.copy_state(pos=game_info.ball.pos)).input()

        controller_input.boost = 1

    elif abs(current_state.pos.y) > 3817:
        #Turn to line up the dodge
        controller_input = QuickTurn(1, True).input()

    elif abs(current_state.pos.y) > 2500 and current_state.wheel_contact:
        #Jump to prep for the dodge
        controller_input.jump = 1

    elif abs(current_state.pos.y) > 3400:
        #If we're far away, fast dodge to speed up
        dodge_direction = car_coordinates_2d(
            current_state,
            (game_info.ball.pos - Vec3(0, 0, 0)) - current_state.pos)
        controller_input = CancelledFastDodge(current_state,
                                              dodge_direction).input()

    elif abs(current_state.pos.y) < 400:  # and opponent_distance < 1000:
        #Dodge into the ball.
        controller_input = AirDodge(Vec3(1, 0, 0),
                                    current_state.jumped_last_frame).input()

    elif current_state.wheel_contact and abs(
            current_state.pos.y) < 1000:  # and opponent_distance < 1000:
        #If we're on the ground, close, and the opponent is also close,
        #jump and turn towards the ball to prep for the dodge.
        if current_state.rot.yaw > ball_angle:
            direction = -1
        else:
            direction = 1
        controller_input = JumpTurn(current_state, 0, direction).input()

    elif abs(current_state.pos.y) < 375:
        #If the opponent is far away and we're close to the ball, take a single jump shot
        controller_input.jump = 1

    elif current_state.wheel_contact:
        #Otherwise if we're on the ground, boost and turn towards the ball
        controller_input = GroundTurn(
            current_state,
            current_state.copy_state(pos=game_info.ball.pos)).input()
        controller_input.boost = 1

    else:
        #Otherwise turn towards the ball (this might not actually do anything)
        controller_input = GroundTurn(
            current_state,
            current_state.copy_state(pos=game_info.ball.pos)).input()

    return controller_input, persistent
示例#4
0
def diagonal(game_info, old_game_info, opponent_distance, x_sign, persistent):
    controller_input = SimpleControllerState()
    current_state = game_info.me
    old_state = old_game_info.me
    ball_angle = atan2((game_info.ball.pos - current_state.pos).y,
                       (game_info.ball.pos - current_state.pos).x)
    offset = Vec3(x_sign * 1500, -465, 0)
    if current_state.rot.yaw > ball_angle:
        direction = -1
    else:
        direction = 1
    wobble = Vec3(current_state.omega.x, current_state.omega.y, 0).magnitude()
    epsilon = 0.3

    #Set which boost we want based on team and side.
    if x_sign == -1:
        first_boost = 11
    else:
        first_boost = 10

    if abs(current_state.pos.y) > 2440:
        #If we haven't taken the small boost yet, drive towards it
        controller_input = GroundTurn(
            current_state,
            current_state.copy_state(pos=game_info.ball.pos + offset)).input()
        controller_input.boost = 1

    elif abs(current_state.pos.y) > 1100 and current_state.wheel_contact:
        controller_input.jump = 1
        controller_input.boost = 1

    elif abs(current_state.pos.y) > 500:
        #If we've taken the boost but are still far away, fast dodge to speed up
        dodge_direction = car_coordinates_2d(
            current_state, (game_info.ball.pos - Vec3(x_sign * 1000, 0, 0)) -
            current_state.pos)
        print(atan2(dodge_direction.y, dodge_direction.x))
        controller_input = CancelledFastDodge(current_state,
                                              dodge_direction).input()
        controller_input.steer = direction

    elif abs(current_state.pos.y) < 350:
        #If both players are close to the ball, dodge into the ball.
        controller_input = AirDodge(
            car_coordinates_2d(current_state,
                               game_info.ball.pos - current_state.pos),
            current_state.jumped_last_frame).input()

    elif current_state.wheel_contact:
        #If we're approaching the ball and the opponent is close,
        #jump turn into ball to prep for the dodge

        controller_input.steer = direction

    elif current_state.wheel_contact and abs(current_state.pos.y) < 520:
        controller_input.jump
    elif current_state.wheel_contact and wobble < epsilon:
        controller_input.handbrake = 1
    else:
        vel_angle = atan2(current_state.vel.y, current_state.vel.x)
        target_rot = Orientation(pyr=[0, vel_angle - x_sign * pi / 4, 0])
        controller_input, persistent = aerial_rotation(target_rot,
                                                       game_info.dt,
                                                       persistent)
        controller_input.boost = 1
        controller_input.steer = x_sign

    return controller_input, persistent
示例#5
0
def Cowculate(plan, game_info, ball_prediction, persistent):
    '''
    The main control function for BAC, Cowculate() returns the final input.
    It takes a GameState object, a plan, and returns a controller_input object.
    Cowculate will be the framework of all decision making, and will be the highest level of abstraction.
    '''

    controller_input = SimpleControllerState()
    current_state = game_info.me

    #############################################################################

    if plan.layers[0] == "Boost":
        '''
        Decide how to get the boost we want to go for.
        '''

        #TODO: Optimize these, or phase them out for RLU or similar.
        wobble = Vec3(current_state.omega.x, current_state.omega.y,
                      0).magnitude()
        epsilon = 0.3

        target_boost = None
        if type(plan.layers[1]) == int:
            target_boost = game_info.boosts[plan.layers[1]]
        elif plan.layers[1] == "Pads":
            #This will skip the target boost loop and follow the path
            pass

        if target_boost != None:
            angle_to_boost = atan2((target_boost.pos - current_state.pos).y,
                                   (target_boost.pos - current_state.pos).x)
            facing_boost = angles_are_close(angle_to_boost,
                                            current_state.rot.yaw, pi / 12)
            grounded_facing_boost = facing_boost and current_state.wheel_contact

            #Turn towards boostI
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=target_boost.pos)).input()

            if 1000 < current_state.vel.magnitude(
            ) < 2250 and facing_boost and wobble < epsilon and (
                    current_state.pos - target_boost.pos
            ).magnitude() > 1000 and abs(current_state.omega.z) < epsilon:
                #If slow, not wobbling from a previous dodge, facing towards the boost,
                #and not already at the boost, dodge for speed
                controller_input = FrontDodge(current_state).input()

            elif current_state.vel.magnitude() < 2300 and (
                    grounded_facing_boost
                    or current_state.rot.pitch < -pi / 12):
                controller_input.boost = 1

        elif plan.path == None:
            #Copied the "go to net" code because I don't plan on really improving this for now.
            #This section will be greatly improved once I have ground recovery code in place.
            #TODO: Reocvery code into picking a path more intelligently.
            center_of_net = Vec3(0, -5120, 0)

            #Turn towards the center of our net
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=center_of_net)).input()

            #Variables to check if we want to flip for speed.
            displacement_from_net = center_of_net - current_state.pos
            distance_to_net = displacement_from_net.magnitude()
            angle_to_net = atan2(displacement_from_net.y,
                                 displacement_from_net.x)
            facing_net = angles_are_close(angle_to_net, current_state.rot.yaw,
                                          pi / 12)
            speed = current_state.vel.magnitude()

            if distance_to_net > 1500 * (
                (speed + 500) / 1410) and 1000 < speed < 2000 and facing_net:
                controller_input = FrontDodge(current_state).input()
            elif current_state.boost > 60 and facing_net and current_state.wheel_contact and speed < 2300:
                controller_input.boost = 1

            #If we start to go up the wall on the way, turn back down.
            if current_state.wheel_contact:
                if current_state.rot.roll > 0.15:
                    controller_input.steer = 1
                elif current_state.rot.roll < -0.15:
                    controller_input.steer = -1

        else:
            controller_input = plan.path.input()

    #############################################################################

    elif plan.layers[0] == "Goal":
        '''
        Decide how to go to or wait in net
        '''

        #Useful locations
        center_of_net = Vec3(0, -5120, 0)
        if game_info.ball.pos.x > 0:
            far_post = Vec3(-1150, -5120 + 300, 0)
            far_boost = game_info.boosts[3].pos
        else:
            far_post = Vec3(1150, -5120 + 300, 0)
            far_boost = game_info.boosts[4].pos

        if plan.layers[1] == "Go to net":
            #Turn towards the center of our net
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=center_of_net)).input()

            #Variables to check if we want to flip for speed.
            displacement_from_net = center_of_net - current_state.pos
            distance_to_net = displacement_from_net.magnitude()
            angle_to_net = atan2(displacement_from_net.y,
                                 displacement_from_net.x)
            facing_net = angles_are_close(angle_to_net, current_state.rot.yaw,
                                          pi / 12)
            speed = current_state.vel.magnitude()

            if distance_to_net > 1500 and 1000 < speed < 2000 and facing_net:
                controller_input = FrontDodge(current_state).input()
            elif current_state.boost > 60 and facing_net and current_state.wheel_contact and speed < 2300:
                controller_input.boost = 1

            #If we start to go up the wall on the way, turn back down.
            if current_state.wheel_contact:
                if current_state.rot.roll > 0.15:
                    controller_input.steer = 1
                elif current_state.rot.roll < -0.15:
                    controller_input.steer = -1

        elif plan.layers[1] == "Wait in net":
            if plan.layers[2] == "Prep for Aerial":
                controller_input = SimpleControllerState()

            else:
                ball_angle = atan2((game_info.ball.pos - current_state.pos).y,
                                   (game_info.ball.pos - current_state.pos).x)

                #Go to net, stop in the middle, then turn in place to face the ball.
                #TODO: Improve NavigateTo or replace completely
                rot = Orientation(pyr=[
                    current_state.rot.pitch, ball_angle, current_state.rot.roll
                ])
                target_state = current_state.copy_state(pos=center_of_net,
                                                        rot=rot)
                controller_input = NavigateTo(current_state,
                                              target_state).input()

            #############################################################################

    elif plan.layers[0] == "Ball":
        '''
        Calculate how to go for the ball as decided in planning.
        '''

        if plan.layers[1] == "Challenge":
            #TODO: Intelligent challenges.
            if current_state.wheel_contact:
                #If still on ground, jump
                controller_input.jump = 1
            else:
                #Once we've jumped, dodge towards the ball
                controller_input = AirDodge(
                    car_coordinates_2d(current_state,
                                       game_info.ball.pos - current_state.pos),
                    current_state.jumped_last_frame).input()
                '''
        elif plan.layers[1] == "Save" and plan.layers[2] == "Backwards":
                controller_input = GroundTurn(current_state,
                                              current_state.copy_state(pos = game_info.ball.pos),
                                              can_reverse = True).input()
                '''

        elif plan.layers[2] == "Aerial":

            controller_input, persistent = aerial(game_info.dt,
                                                  game_info.team_sign,
                                                  persistent)

        else:
            #TODO: Replace all of this with shooting/clearing/better code.
            #Need pathing to get to a reasonable spot, and intelligent dodges to place the ball properly.

            for i in range(100):

                #Adjust for Ball/Car radii
                ball_vel = ball_prediction.slices[i].vel

                try:
                    #Evil magic numbers.  TODO: Actual timing and arrival prediction.
                    target_pos = ball_prediction.slices[
                        i].pos + ball_vel.normalize().scalar_multiply(150)
                except ZeroDivisionError:
                    target_pos = ball_prediction.slices[i].pos

                car_target_vector = target_pos - current_state.pos
                turn_angle = abs(
                    current_state.rot.yaw -
                    atan2(car_target_vector.y, car_target_vector.x))
                time_estimate = linear_time_to_reach(
                    game_info,
                    target_pos) - (ball_vel.magnitude() / 50) * (turn_angle)
                if ball_prediction.slices[i].time < time_estimate + 1 / 30:
                    break

            if plan.layers[1] == "Shot":
                #If the opponent isn't close to the ball, reposition to shoot towards net
                #Find the center of the opponent's net
                center_of_net = Vec3(0, 5120, game_info.ball.pos.z)

                #If the ball is left, go right, and vice versa.
                if game_info.ball.pos.x > 0:
                    shooting_correction = (
                        60 * (5120 - abs(game_info.ball.pos.y))) / (
                            (game_info.ball.pos - center_of_net).magnitude())
                else:
                    shooting_correction = -(
                        60 * (5120 - abs(game_info.ball.pos.y))) / (
                            (game_info.ball.pos - center_of_net).magnitude())

                    target_pos = Vec3(target_pos.x + shooting_correction,
                                      target_pos.y, target_pos.z)

            #Make sure we don't try to go to a point outside the map
            target_pos = Vec3(cap_magnitude(target_pos.x, 4096),
                              cap_magnitude(target_pos.y, 5120), target_pos.z)
            #Turn towards the target
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=target_pos)).input()

            #If we're not supersonic, and we're facing roughly towards the ball, boost.
            ball_angle = atan2((game_info.ball.pos - current_state.pos).y,
                               (game_info.ball.pos - current_state.pos).x)
            if current_state.vel.magnitude(
            ) < 2250 and current_state.wheel_contact and angles_are_close(
                    current_state.rot.yaw, ball_angle, pi / 4):
                controller_input.boost = 1

        #############################################################################

    elif plan.layers[0] == "Recover":
        if plan.layers[1] == "Air":
            #If we're in the air, and not trying to hit the ball, recover.

            controller_input, persistent = aerial_rotation(
                game_info.dt, persistent)

        elif plan.layers[1] == "Ground":
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=Vec3(0, -5120, 0))).input()
            #TODO: Work on have_steering_control, powersliding, etc.

    return controller_input, persistent
示例#6
0
def Cowculate(plan, game_info, ball_prediction, persistent):
    '''
    The main control function for BAC, Cowculate() returns the final input.
    It takes a GameState object, a plan, and returns a controller_input object.
    Cowculate will be the framework of all decision making, and will be the highest level of abstraction.
    '''

    controller_input = SimpleControllerState()
    current_state = game_info.me

    #############################################################################

    if plan.layers[0] == "Boost":
        '''
        Decide how to get the boost we want to go for.
        '''

        #TODO: Optimize these, or phase them out for RLU or similar.
        wobble = Vec3(current_state.omega.x, current_state.omega.y,
                      0).magnitude()
        epsilon = 0.3

        target_boost = None
        if type(plan.layers[1]) == int:
            target_boost = game_info.boosts[plan.layers[1]]
        elif plan.layers[1] == "Pads":
            #This will skip the target boost loop and follow the path
            pass

        if target_boost != None:
            angle_to_boost = atan2((target_boost.pos - current_state.pos).y,
                                   (target_boost.pos - current_state.pos).x)
            facing_boost = angles_are_close(angle_to_boost,
                                            current_state.rot.yaw, pi / 12)
            grounded_facing_boost = facing_boost and current_state.wheel_contact

            #Turn towards boost
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=target_boost.pos)).input()

            if 1000 < current_state.vel.magnitude(
            ) < 2250 and facing_boost and wobble < epsilon and (
                    current_state.pos - target_boost.pos
            ).magnitude() > 1000 and abs(current_state.omega.z) < epsilon:
                #If slow, not wobbling from a previous dodge, facing towards the boost,
                #and not already at the boost, dodge for speed
                controller_input = FrontDodge(current_state).input()

            elif current_state.vel.magnitude() < 2300 and (
                    grounded_facing_boost
                    or current_state.rot.pitch < -pi / 12):
                controller_input.boost = 1

        elif plan.path == None:
            #Copied the "go to net" code because I don't plan on really improving this for now.
            #This section will be greatly improved once I have ground recovery code in place.
            #TODO: Reocvery code into picking a path more intelligently.
            center_of_net = Vec3(0, -5120, 0)

            #Turn towards the center of our net
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=center_of_net)).input()

            #Variables to check if we want to flip for speed.
            displacement_from_net = center_of_net - current_state.pos
            distance_to_net = displacement_from_net.magnitude()
            angle_to_net = atan2(displacement_from_net.y,
                                 displacement_from_net.x)
            facing_net = angles_are_close(angle_to_net, current_state.rot.yaw,
                                          pi / 12)
            speed = current_state.vel.magnitude()

            if distance_to_net > 1500 * (
                (speed + 500) / 1410) and 1000 < speed < 2000 and facing_net:
                controller_input = FrontDodge(current_state).input()
            elif current_state.boost > 60 and facing_net and current_state.wheel_contact and speed < 2300:
                controller_input.boost = 1

            #If we start to go up the wall on the way, turn back down.
            if current_state.wheel_contact:
                if current_state.rot.roll > 0.15:
                    controller_input.steer = 1
                elif current_state.rot.roll < -0.15:
                    controller_input.steer = -1

        else:
            controller_input = plan.path.input()

    #############################################################################

    elif plan.layers[0] == "Goal":
        '''
        Decide how to go to or wait in net
        '''

        #Useful locations
        center_of_net = Vec3(0, -5120, 0)
        if game_info.ball.pos.x > 0:
            far_post = Vec3(-1150, -5120 + 300, 0)
            far_boost = game_info.boosts[3].pos
        else:
            far_post = Vec3(1150, -5120 + 300, 0)
            far_boost = game_info.boosts[4].pos

        if plan.layers[1] == "Go to net":
            #Turn towards the center of our net
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=center_of_net)).input()

            #Variables to check if we want to flip for speed.
            displacement_from_net = center_of_net - current_state.pos
            distance_to_net = displacement_from_net.magnitude()
            angle_to_net = atan2(displacement_from_net.y,
                                 displacement_from_net.x)
            facing_net = angles_are_close(angle_to_net, current_state.rot.yaw,
                                          pi / 12)
            speed = current_state.vel.magnitude()

            if distance_to_net > 1500 and 1000 < speed < 2000 and facing_net:
                controller_input = FrontDodge(current_state).input()
            elif current_state.boost > 60 and facing_net and current_state.wheel_contact and speed < 2300:
                controller_input.boost = 1

            #If we start to go up the wall on the way, turn back down.
            if current_state.wheel_contact:
                if current_state.rot.roll > 0.15:
                    controller_input.steer = 1
                elif current_state.rot.roll < -0.15:
                    controller_input.steer = -1

        elif plan.layers[1] == "Wait in net":
            if plan.layers[2] == "Prep for Aerial":
                controller_input = SimpleControllerState()

            else:
                ball_angle = atan2((game_info.ball.pos - current_state.pos).y,
                                   (game_info.ball.pos - current_state.pos).x)

                #Go to net, stop in the middle, then turn in place to face the ball.
                #TODO: Improve NavigateTo or replace completely
                rot = Orientation(pyr=[
                    current_state.rot.pitch, ball_angle, current_state.rot.roll
                ])
                target_state = current_state.copy_state(pos=center_of_net,
                                                        rot=rot)
                controller_input = NavigateTo(current_state,
                                              target_state).input()

    #############################################################################

    elif plan.layers[0] == "Ball":
        '''
        Calculate how to go for the ball as decided in planning.
        '''

        if plan.layers[1] == "Challenge":
            #TODO: Intelligent challenges.
            if current_state.wheel_contact:
                #If still on ground, jump
                controller_input.jump = 1
            else:
                #Once we've jumped, dodge towards the ball
                controller_input = AirDodge(
                    car_coordinates_2d(current_state,
                                       game_info.ball.pos - current_state.pos),
                    current_state.jumped_last_frame).input()

        elif plan.layers[1] == "Save":
            if persistent.path_follower.action != None:
                #Follow the ArcLineArc path
                persistent.path_follower.action.step(game_info.dt)
                controller_input = persistent.path_follower.action.controls
            #else:
            #    controller_input = jump_turn() #TODO: When facing walls, all paths go about of bounds.  Turn around first.

        elif plan.layers[2] == "Aerial":

            controller_input, persistent = aerial(game_info.dt,
                                                  game_info.team_sign,
                                                  persistent)

        elif (plan.layers[1] == "Shot"
              or plan.layers[1] == "Clear") and plan.layers[2] == "Path":
            if persistent.path_follower.action != None:
                #Follow the ArcLineArc path
                persistent.path_follower.action.step(game_info.dt)
                controller_input = persistent.path_follower.action.controls
            #else:
            #    controller_input = jump_turn() #TODO: When facing walls, all paths go about of bounds.  Turn around first.

        elif (plan.layers[1] == "Shot"
              or plan.layers[1] == "Clear") and plan.layers[2] == "Hit ball":
            if persistent.doddge.action == None:
                persistent.dodge.check = True
                dodge_simulation_results = moving_ball_dodge_contact(game_info)
                persistent.dodge.action = RLU_Dodge(
                    game_info.utils_game.my_car)
                persistent.dodge.action.duration = dodge_simulation_results[0]
                persistent.dodge.action.delay = dodge_simulation_results[1]
                persistent.dodge.action.target = Vec3_to_vec3(
                    game_info.ball.pos, game_info.team_sign)
                persistent.dodge.action.preorientation = roll_away_from_target(
                    persistent.dodge.action.target, pi / 4, game_info)
            else:
                persistent.dodge.check = True
                persistent.dodge.action.step(game_info.dt)
                output = persistent.dodge.action.controls
                output.boost = 1

        elif (plan.layers[1] == "Shot"
              or plan.layers[1] == "Clear") and plan.layers[2] == "Chase":
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=game_info.ball.pos)).input()

    #############################################################################

    elif plan.layers[0] == "Recover":
        if plan.layers[1] == "Air":
            #If we're in the air, and not trying to hit the ball, recover.

            controller_input, persistent = aerial_rotation(
                game_info.dt, persistent)

        elif plan.layers[1] == "Ground":
            controller_input = GroundTurn(
                current_state,
                current_state.copy_state(pos=Vec3(0, -5120, 0))).input()
            #TODO: Work on have_steering_control, powersliding, etc.

    return controller_input, persistent