Ejemplo n.º 1
0
    def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
        self.info.read_packet(packet)

        halfpi = math.pi / 2
        car = self.info.my_car
        ball = self.info.ball

        car_state = CarState()
        if ARTIFICIAL_UNLIMITED_BOOST:
            car_state.boost_amount = 100

        if ball.pos.x == 0 and ball.pos.y == 0:
            # Kickoff
            self.last_turn_time = time.time()
            euler = rotation_to_euler(looking_in_dir(xy(ball.pos - car.pos)))
            car_state.physics = Physics(
                rotation=Rotator(pitch=euler.x, roll=0, yaw=euler.y))

        elif self.last_turn_time + TURN_COOLDOWN < time.time():

            turns = [
                Turn(car.forward, None),
                Turn(car.left, car.up * halfpi),
                Turn(-car.left, car.up * -halfpi)
            ] if not VERTICAL_TURNS else [
                Turn(car.forward, None),
                Turn(car.left, car.up * halfpi),
                Turn(-car.left, car.up * -halfpi),
                Turn(car.up * 0.25, car.left * -halfpi),
                Turn(-car.up, car.left * halfpi),
            ]

            # In practise, this offset has little impact
            ball_pos_with_offset = ball.pos + normalize(
                self.info.opp_goal.pos - ball.pos) * -60
            delta_n = normalize(ball_pos_with_offset - car.pos)

            turn, _ = argmax(turns, lambda turn: dot(turn.dir, delta_n))

            if turn.axis is not None:
                self.last_turn_time = time.time()
                mat = axis_to_rotation(turn.axis)
                new_vel = dot(mat, car.vel)
                new_rot = dot(mat, car.rot)
                euler = rotation_to_euler(new_rot)
                car_state.physics = Physics(velocity=Vector3(
                    new_vel[0], new_vel[1], new_vel[2]),
                                            rotation=Rotator(pitch=euler.x,
                                                             roll=0,
                                                             yaw=euler.y))

        game_state = GameState(cars={self.index: car_state})
        self.set_game_state(game_state)

        return self.controls
Ejemplo n.º 2
0
def draw_circle(bot, center: Vec3, normal: Vec3, radius: float, pieces: int):
    # Construct the arm that will be rotated
    arm = normalize(cross(normal, center)) * radius
    angle = 2 * math.pi / pieces
    rotation_mat = axis_to_rotation(angle * normalize(normal))
    points = [center + arm]

    for i in range(pieces):
        arm = dot(rotation_mat, arm)
        points.append(center + arm)

    bot.renderer.draw_polyline_3d(points, bot.renderer.orange())
Ejemplo n.º 3
0
 def get_closest_dir_in_cone(self, direction, span_offset: float = 0):
     if self.contains_direction(direction, span_offset):
         return normalize(direction)
     else:
         ang_to_right = abs(angle_between(direction, self.right_dir))
         ang_to_left = abs(angle_between(direction, self.left_dir))
         return self.right_dir if ang_to_right < ang_to_left else self.left_dir
Ejemplo n.º 4
0
 def __init__(self, right_most, left_most):
     # Right angle and direction
     if isinstance(right_most, float):
         self.right_ang = fix_ang(right_most)
         self.right_dir = Vec3(math.cos(right_most), math.sin(right_most),
                               0)
     elif isinstance(right_most, Vec3):
         self.right_ang = math.atan2(right_most.y, right_most.x)
         self.right_dir = normalize(right_most)
     # Left angle and direction
     if isinstance(left_most, float):
         self.left_ang = fix_ang(left_most)
         self.left_dir = Vec3(math.cos(left_most), math.sin(left_most), 0)
     elif isinstance(left_most, Vec3):
         self.left_ang = math.atan2(left_most.y, left_most.x)
         self.left_dir = normalize(left_most)
Ejemplo n.º 5
0
    def exec(self, bot) -> SimpleControllerState:
        ct = bot.info.time - self._start_time
        controls = SimpleControllerState()
        controls.throttle = 1

        car = bot.info.my_car

        # Target is allowed to be a function that takes bot as a parameter. Check what it is
        if callable(self.target):
            target = self.target(bot)
        else:
            target = self.target

        # To boost or not to boost, that is the question
        car_to_target = target - car.pos
        vel_p = proj_onto_size(car.vel, car_to_target)
        angle = angle_between(car_to_target, car.forward)
        controls.boost = self.boost and angle < self._boost_ang_req and vel_p < self._max_speed

        # States of dodge (note reversed order)
        # Land on ground
        if ct >= self._t_finishing:
            self._almost_finished = True
            if car.on_ground:
                self.done = True
            else:
                bot.maneuver = RecoveryManeuver(bot)
                self.done = True
            return controls
        elif ct >= self._t_second_unjump:
            # Stop pressing jump and rotate and wait for flip is done
            pass
        elif ct >= self._t_aim:
            if ct >= self._t_second_jump:
                controls.jump = 1

            # Direction, yaw, pitch, roll
            if self.target is None:
                controls.roll = 0
                controls.pitch = -1
                controls.yaw = 0
            else:
                target_local = dot(car_to_target, car.rot)
                target_local.z = 0

                direction = normalize(target_local)

                controls.roll = 0
                controls.pitch = -direction.x
                controls.yaw = sign(car.rot.get(2, 2)) * direction.y

        # Stop pressing jump
        elif ct >= self._t_first_unjump:
            pass

        # First jump
        else:
            controls.jump = 1

        return controls
Ejemplo n.º 6
0
    def update(self, bot):
        ball = bot.info.ball

        # Find closest foe to ball
        self.opp_closest_to_ball, self.opp_closest_to_ball_dist = argmin(bot.info.opponents, lambda opp: norm(opp.pos - ball.pos))

        # Possession and on/off-site
        self.car_with_possession = None
        self.ally_with_possession = None
        self.opp_with_possession = None
        for car in bot.info.cars:

            # On site
            own_goal = bot.info.goals[car.team]
            ball_to_goal = own_goal.pos - ball.pos
            car_to_ball = ball.pos - car.pos
            car.onsite = dot(ball_to_goal, car_to_ball) < 0.1

            # Reach ball time
            car.reach_ball_time = predict.time_till_reach_ball(car, ball)
            reach01 = clip01((5 - car.reach_ball_time) / 5)

            # Possession
            point_in_front = car.pos + car.vel * 0.6
            ball_point_dist = norm(ball.pos - point_in_front)
            dist01 = 1500 / (1500 + ball_point_dist)  # Halves every 1500 uu of dist
            car_to_ball = bot.info.ball.pos - car.pos
            car_to_ball_unit = normalize(car_to_ball)
            in_front01 = dot(car.forward, car_to_ball_unit)
            car.possession = dist01 * in_front01 * reach01 * 3
            if self.car_with_possession is None or car.possession > self.car_with_possession.possession:
                self.car_with_possession = car
            if car.team == bot.team and (self.ally_with_possession is None or car.possession > self.ally_with_possession.possession):
                self.ally_with_possession = car
            if car.team != bot.team and (self.opp_with_possession is None or car.possession > self.opp_with_possession.possession):
                self.opp_with_possession = car

        # Objectives
        for car in bot.info.cars:
            car.last_objective = car.objective
            car.objective = Objective.UNKNOWN
        thirdman_index, _ = argmin(bot.info.team_cars, lambda ally: norm(ally.pos - bot.info.own_goal.pos))
        attacker, attacker_score = argmax(bot.info.team_cars,
                                          lambda ally: ((0.09 if ally.last_objective == Objective.GO_FOR_IT else 0)
                                                        + ally.boost / 490
                                                        - (0.21 if ally.index == thirdman_index else 0)
                                                        - (0.4 if not ally.onsite else 0)
                                                        + ally.possession * (10_000 - ally.team_sign * ally.pos.y) / 20_000)**2)
        attacker.objective = Objective.GO_FOR_IT
        follower_expected_pos = (ball.pos + bot.info.own_goal.pos) * 0.5
        follower, follower_score = argmin([ally for ally in bot.info.team_cars if ally.objective == Objective.UNKNOWN],
                                          lambda ally: (-500 if ally.last_objective == Objective.FOLLOW_UP else 0)
                                                        - ally.boost * 2
                                                        + (1100 if ally.index == thirdman_index else 0)
                                                        + (200 if not ally.onsite else 0)
                                                        + norm(ally.pos - follower_expected_pos))
        follower.objective = Objective.FOLLOW_UP
        for car in bot.info.team_cars:
            if car.objective == Objective.UNKNOWN:
                car.objective = Objective.ROTATE_BACK_OR_DEF
Ejemplo n.º 7
0
    def find_landing_orientation(car: Car, num_points: int) -> Mat33:
        """
        dummy = DummyObject(car)
        trajectory = [Vec3(dummy.pos)]

        for i in range(0, num_points):
            fall(dummy, 0.0333)  # Apply physics and let car fall through the air
            trajectory.append(Vec3(dummy.pos))
            up = dummy.pitch_surface_normal()
            if norm(up) > 0.0 and i > 10:
                up = normalize(up)
                forward = normalize(dummy.vel - dot(dummy.vel, up) * up)
                left = cross(up, forward)

                return Mat33.from_columns(forward, left, up)

        return Mat33(car.rot)
        """

        forward = normalize(xy(
            car.vel)) if norm(xy(car.vel)) != 0 else car.forward
        up = Vec3(z=1)
        left = cross(up, forward)

        return Mat33.from_columns(forward, left, up)
Ejemplo n.º 8
0
    def find_landing_orientation(car: Car) -> Mat33:

        # FIXME: If we knew the arena's mesh we could test if we are landing or a wall or something

        forward = normalize(xy(
            car.vel)) if norm(xy(car.vel)) > 20 else car.forward
        up = Vec3(z=1)
        left = cross(up, forward)

        return Mat33.from_columns(forward, left, up)
Ejemplo n.º 9
0
    def exec(self, bot) -> SimpleControllerState:
        self.is_dribbling = True

        car = bot.info.my_car
        ball = bot.info.ball
        ball_landing = predict.next_ball_landing(bot)
        ball_to_goal = bot.info.enemy_goal - ball.pos

        # Decide on target pos and speed
        target = ball_landing.data["obj"].pos - self.offset_bias * normalize(
            ball_to_goal)
        dist = norm(target - bot.info.my_car.pos)
        speed = 1400 if ball_landing.time == 0 else dist / ball_landing.time

        # Do a flick?
        car_to_ball = ball.pos - car.pos
        dist = norm(car_to_ball)
        if dist <= self.required_distance_to_ball_for_flick:
            self.flick_timer += 0.016666
            if self.flick_timer > self.wait_before_flick:
                bot.maneuver = DodgeManeuver(
                    bot, bot.info.enemy_goal)  # use flick_init_jump_duration?
        else:
            self.flick_timer = 0

            # dodge on far distances
            if dist > 2450 and speed > 1410:
                ctt_n = normalize(target - car.pos)
                vtt = dot(bot.info.my_car.vel, ctt_n) / dot(ctt_n, ctt_n)
                if vtt > 750:
                    bot.maneuver = DodgeManeuver(bot, target)

        if bot.do_rendering:
            bot.renderer.draw_line_3d(car.pos, target, bot.renderer.pink())

        return bot.drive.go_towards_point(bot,
                                          target,
                                          target_vel=speed,
                                          slide=False,
                                          can_keep_speed=False,
                                          can_dodge=True,
                                          wall_offset_allowed=0)
Ejemplo n.º 10
0
    def stay_at(self, bot, point: Vec3, looking_at: Vec3):

        OKAY_DIST = 100

        car = bot.info.my_car
        car_to_point = point - car.pos
        car_to_point_dir = normalize(point - car.pos)
        dist = norm(car_to_point)

        if dist > OKAY_DIST:
            return self.towards_point(bot, point, int(dist * 2))
        else:
            look_dir = normalize(looking_at - car.pos)
            facing_correctly = dot(car.forward, look_dir) > 0.9
            if facing_correctly:
                return SimpleControllerState()
            else:
                ctrls = SimpleControllerState()
                ctrls.throttle = 0.7 * sign(dot(car.forward, car_to_point_dir))
                ctrls.steer = -ctrls.throttle * sign(
                    dot(car.left, car_to_point_dir))

                return ctrls
Ejemplo n.º 11
0
def curve_from_arrival_dir(src: Vec3, target: Vec3, arrival_direction: Vec3, w=1):
    """
    Returns a point that is equally far from src and target on the line going through target with the given direction
    """
    dir = normalize(arrival_direction)
    tx = target.x
    ty = target.y
    sx = src.x
    sy = src.y
    dx = dir.x
    dy = dir.y

    t = - (tx * tx - 2 * tx * sx + ty * ty - 2 * ty * sy + sx * sx + sy * sy) / (2 * (tx * dx + ty * dy - sx * dx - sy * dy))
    t = clip(t, -1700, 1700)

    return target + w * t * dir
Ejemplo n.º 12
0
    def towards(self,
                bot,
                target: Vec3,
                time: float,
                allowed_uncertainty: float = 0.3,
                dodge_hit: bool = True):

        ball_soon = ball_predict(bot, time)
        ball_soon_to_target_dir = normalize(target - ball_soon.pos)
        right = dot(axis_to_rotation(Vec3(z=allowed_uncertainty)),
                    ball_soon_to_target_dir)
        left = dot(axis_to_rotation(Vec3(z=-allowed_uncertainty)),
                   ball_soon_to_target_dir)
        aim_cone = AimCone(right, left)

        aim_cone.draw(bot, ball_soon.pos, r=0, g=0)

        return self.with_aiming(bot, aim_cone, time, dodge_hit)
Ejemplo n.º 13
0
    def run(self, bot) -> SimpleControllerState:
        self.is_dribbling = True

        car = bot.info.my_car
        ball = bot.info.ball
        ball_landing = predict.next_ball_landing(bot)
        ball_to_goal = bot.info.opp_goal.pos - ball.pos

        # Decide on target pos and speed
        target = ball_landing.data["obj"].pos - self.offset_bias * normalize(
            ball_to_goal)
        dist = norm(target - bot.info.my_car.pos)
        speed = 1400 if ball_landing.time == 0 else dist / ball_landing.time

        # Do a flick?
        car_to_ball = ball.pos - car.pos
        dist = norm(car_to_ball)
        enemy, enemy_dist = bot.info.closest_enemy(ball.pos)
        if dist <= self.required_distance_to_ball_for_flick:
            self.flick_timer += bot.info.dt
            if self.flick_timer > self.wait_before_flick and enemy_dist < 900:
                bot.maneuver = DodgeManeuver(
                    bot,
                    bot.info.opp_goal.pos)  # use flick_init_jump_duration?
        else:
            self.flick_timer = 0

        if bot.do_rendering:
            bot.renderer.draw_line_3d(car.pos, target, bot.renderer.pink())

        return bot.drive.towards_point(bot,
                                       target,
                                       target_vel=speed,
                                       slide=False,
                                       can_keep_speed=False,
                                       can_dodge=True,
                                       wall_offset_allowed=0)
Ejemplo n.º 14
0
    def exec(self, bot) -> SimpleControllerState:
        ctrl = super().exec(bot)

        ct = time.time() - self._start_time

        if ct >= self._t_release_ball:
            ctrl.use_item = True

        if self._t_second_jump >= ct >= 0:
            # Rotate away from target
            car = bot.data.my_car
            target = Vec3(y=-5440 * bot.data.team_sign)
            ball_to_target = target - bot.data.ball.pos

            target_local = dot(ball_to_target, car.rot)
            target_local.z = 0

            direction = normalize(-target_local)

            ctrl.roll = 0
            ctrl.pitch = -direction.x
            ctrl.yaw = sign(car.rot.get(2, 2)) * direction.y

        return ctrl
Ejemplo n.º 15
0
    def exec(self, bot) -> SimpleControllerState:

        car = bot.info.my_car
        ball = bot.info.ball

        shoot_controls = bot.shoot.with_aiming(
            bot, self.aim_cone, predict.time_till_reach_ball(car, ball))
        if bot.do_rendering:
            self.aim_cone.draw(bot, bot.shoot.ball_when_hit.pos, b=0)

        hit_pos = bot.shoot.ball_when_hit.pos
        dist = norm(car.pos - hit_pos)
        closest_enemy, enemy_dist = bot.info.closest_enemy(
            0.5 * (hit_pos + ball.pos))

        if not bot.shoot.can_shoot and is_closer_to_goal_than(
                car.pos, hit_pos, bot.info.team):
            # Can't shoot but or at least on the right side: Chase

            goal_to_ball = normalize(hit_pos - bot.info.enemy_goal)
            offset_ball = hit_pos + goal_to_ball * Ball.RADIUS * 0.9

            if bot.do_rendering:
                bot.renderer.draw_line_3d(car.pos, offset_ball,
                                          bot.renderer.yellow())

            return bot.drive.go_towards_point(bot,
                                              offset_ball,
                                              target_vel=2200,
                                              slide=False,
                                              boost_min=0)

        elif not bot.shoot.aim_is_ok and hit_pos.y * -bot.info.team_sign > 4350 and abs(
                hit_pos.x) > 900 and not dist < 450:
            # hit_pos is an enemy corner and we are not close: Avoid enemy corners and just wait

            enemy_to_ball = normalize(hit_pos - closest_enemy.pos)
            wait_point = hit_pos + enemy_to_ball * enemy_dist  # a point 50% closer to the center of the field
            wait_point = lerp(wait_point,
                              ball.pos + Vec3(0, bot.info.team_sign * 3000, 0),
                              0.5)

            if bot.do_rendering:
                bot.renderer.draw_line_3d(car.pos, wait_point,
                                          bot.renderer.yellow())

            return bot.drive.go_towards_point(bot,
                                              wait_point,
                                              norm(car.pos - wait_point),
                                              slide=False,
                                              can_keep_speed=True,
                                              can_dodge=False)

        elif not bot.shoot.can_shoot:
            if car.boost == 0:

                collect_center = ball.pos.y * bot.info.team_sign <= 0
                collect_small = closest_enemy.pos.y * bot.info.team_sign <= 0
                pads = filter_pads(bot,
                                   bot.info.big_boost_pads,
                                   big_only=not collect_small,
                                   enemy_side=False,
                                   center=collect_center)
                bot.maneuver = CollectClosestBoostManeuver(bot, pads)
            # return home
            return bot.drive.go_home(bot)

        else:
            # Shoot !
            if bot.shoot.using_curve and bot.do_rendering:
                rendering.draw_bezier(
                    bot, [car.pos, bot.shoot.curve_point, hit_pos])
            return shoot_controls
Ejemplo n.º 16
0
    def with_aiming(self,
                    bot,
                    aim_cone: AimCone,
                    time: float,
                    dodge_hit: bool = True):

        #       aim: |           |           |           |
        #  ball      |   bad     |    ok     |   good    |
        # z pos:     |           |           |           |
        # -----------+-----------+-----------+-----------+
        #  too high  |   give    |   give    |   wait/   |
        #            |    up     |    up     |  improve  |
        # -----------+ - - - - - + - - - - - + - - - - - +
        #   medium   |   give    |  improve  |  aerial   |
        #            |    up     |    aim    |           |
        # -----------+ - - - - - + - - - - - + - - - - - +
        #   soon on  |  improve  |  slow     |   small   |
        #   ground   |    aim    |  curve    |   jump    |
        # -----------+ - - - - - + - - - - - + - - - - - +
        #  on ground |  improve  |  fast     |  fast     |
        #            |   aim??   |  curve    |  straight |
        # -----------+ - - - - - + - - - - - + - - - - - +

        # FIXME if the ball is not on the ground we treat it as 'soon on ground' in all other cases

        self.controls = SimpleControllerState()
        self.aim_is_ok = False
        self.waits_for_fall = False
        self.ball_is_flying = False
        self.can_shoot = False
        self.using_curve = False
        self.curve_point = None
        self.ball_when_hit = None
        car = bot.info.my_car

        ball_soon = ball_predict(bot, time)
        car_to_ball_soon = ball_soon.pos - car.pos
        dot_facing_score = dot(normalize(car_to_ball_soon),
                               normalize(car.forward))
        vel_towards_ball_soon = proj_onto_size(car.vel, car_to_ball_soon)
        is_facing = 0 < dot_facing_score

        if ball_soon.pos.z < 110 or (ball_soon.pos.z < 475 and ball_soon.vel.z
                                     <= 0) or True:  #FIXME Always true

            # The ball is on the ground or soon on the ground

            if 275 < ball_soon.pos.z < 475 and aim_cone.contains_direction(
                    car_to_ball_soon):
                # Can we hit it if we make a small jump?
                vel_f = proj_onto_size(car.vel, xy(car_to_ball_soon))
                car_expected_pos = car.pos + car.vel * time
                ball_soon_flat = xy(ball_soon.pos)
                diff = norm(car_expected_pos - ball_soon_flat)
                ball_in_front = dot(ball_soon.pos - car_expected_pos,
                                    car.vel) > 0

                if bot.do_rendering:
                    bot.renderer.draw_line_3d(car.pos, car_expected_pos,
                                              bot.renderer.lime())
                    bot.renderer.draw_rect_3d(car_expected_pos, 12, 12, True,
                                              bot.renderer.lime())

                if vel_f > 400:
                    if diff < 150 and ball_in_front:
                        bot.maneuver = SmallJumpManeuver(
                            bot, lambda b: b.info.ball.pos)

            if 110 < ball_soon.pos.z:  # and ball_soon.vel.z <= 0:
                # The ball is slightly in the air, lets wait just a bit more
                self.waits_for_fall = True
                ball_landing = next_ball_landing(bot, ball_soon, size=100)
                time = time + ball_landing.time
                ball_soon = ball_predict(bot, time)
                car_to_ball_soon = ball_soon.pos - car.pos

            self.ball_when_hit = ball_soon

            # The ball is on the ground, are we in position for a shot?
            if aim_cone.contains_direction(car_to_ball_soon) and is_facing:

                # Straight shot

                self.aim_is_ok = True
                self.can_shoot = True

                if norm(car_to_ball_soon) < 240 + Ball.RADIUS and aim_cone.contains_direction(car_to_ball_soon)\
                        and vel_towards_ball_soon > 300:
                    bot.drive.start_dodge(bot)

                offset_point = xy(
                    ball_soon.pos) - 50 * aim_cone.get_center_dir()
                speed = self._determine_speed(norm(car_to_ball_soon), time)
                self.controls = bot.drive.towards_point(bot,
                                                        offset_point,
                                                        target_vel=speed,
                                                        slide=True,
                                                        boost_min=0,
                                                        can_keep_speed=False)
                return self.controls

            elif aim_cone.contains_direction(car_to_ball_soon, math.pi / 5):

                # Curve shot

                self.aim_is_ok = True
                self.using_curve = True
                self.can_shoot = True

                offset_point = xy(
                    ball_soon.pos) - 50 * aim_cone.get_center_dir()
                closest_dir = aim_cone.get_closest_dir_in_cone(
                    car_to_ball_soon)
                self.curve_point = curve_from_arrival_dir(
                    car.pos, offset_point, closest_dir)

                self.curve_point.x = clip(self.curve_point.x, -Field.WIDTH / 2,
                                          Field.WIDTH / 2)
                self.curve_point.y = clip(self.curve_point.y,
                                          -Field.LENGTH / 2, Field.LENGTH / 2)

                if dodge_hit and norm(car_to_ball_soon) < 240 + Ball.RADIUS and angle_between(car.forward, car_to_ball_soon) < 0.5\
                        and aim_cone.contains_direction(car_to_ball_soon) and vel_towards_ball_soon > 300:
                    bot.drive.start_dodge(bot)

                speed = self._determine_speed(norm(car_to_ball_soon), time)
                self.controls = bot.drive.towards_point(bot,
                                                        self.curve_point,
                                                        target_vel=speed,
                                                        slide=True,
                                                        boost_min=0,
                                                        can_keep_speed=False)
                return self.controls

            else:

                # We are NOT in position!
                self.aim_is_ok = False

                pass

        else:

            if aim_cone.contains_direction(car_to_ball_soon):
                self.waits_for_fall = True
                self.aim_is_ok = True
                #self.can_shoot = False
                pass  # Allow small aerial (wait if ball is too high)

            elif aim_cone.contains_direction(car_to_ball_soon, math.pi / 4):
                self.ball_is_flying = True
                pass  # Aim is ok, but ball is in the air
Ejemplo n.º 17
0
    def run(self, bot) -> SimpleControllerState:

        car = bot.info.my_car
        ball = bot.info.ball

        my_hit_time = predict.time_till_reach_ball(car, ball)
        shoot_controls = bot.shoot.with_aiming(bot, self.aim_cone, my_hit_time)

        hit_pos = bot.shoot.ball_when_hit.pos
        dist = norm(car.pos - hit_pos)
        closest_enemy, enemy_dist = bot.info.closest_enemy(0.5 * (hit_pos + ball.pos))

        if not bot.shoot.can_shoot and is_closer_to_goal_than(car.pos, hit_pos, bot.info.team):
            # Can't shoot but or at least on the right side: Chase

            goal_to_ball = normalize(hit_pos - bot.info.opp_goal.pos)
            offset_ball = hit_pos + goal_to_ball * Ball.RADIUS * 0.9
            enemy_hit_time = predict.time_till_reach_ball(closest_enemy, ball)
            enemy_hit_pos = predict.ball_predict(bot, enemy_hit_time).pos
            if enemy_hit_time < 1.5 * my_hit_time:
                self.temp_utility_desire_boost -= bot.info.dt
                if bot.do_rendering:
                    bot.renderer.draw_line_3d(closest_enemy.pos, enemy_hit_pos, bot.renderer.red())
                return bot.drive.home(bot)

            if bot.do_rendering:
                bot.renderer.draw_line_3d(car.pos, offset_ball, bot.renderer.yellow())

            return bot.drive.towards_point(bot, offset_ball, target_vel=2200, slide=False, boost_min=0)

        elif len(bot.info.teammates) == 0 and not bot.shoot.aim_is_ok and hit_pos.y * -bot.info.team_sign > 4250 and abs(hit_pos.x) > 900 and not dist < 420:
            # hit_pos is an enemy corner and we are not close: Avoid enemy corners in 1s and just wait

            enemy_to_ball = normalize(hit_pos - closest_enemy.pos)
            wait_point = hit_pos + enemy_to_ball * enemy_dist  # a point 50% closer to the center of the field
            wait_point = lerp(wait_point, ball.pos + Vec3(0, bot.info.team_sign * 3000, 0), 0.5)

            if bot.do_rendering:
                bot.renderer.draw_line_3d(car.pos, wait_point, bot.renderer.yellow())

            return bot.drive.towards_point(bot, wait_point, norm(car.pos - wait_point), slide=False, can_keep_speed=True, can_dodge=False)

        elif bot.shoot.can_shoot:

            # Shoot !
            if bot.do_rendering:
                self.aim_cone.draw(bot, bot.shoot.ball_when_hit.pos, r=0, b=0)
                if bot.shoot.using_curve:
                    rendering.draw_bezier(bot, [car.pos, bot.shoot.curve_point, hit_pos])
            return shoot_controls

        else:
            # We can't shoot at goal reliably
            # How about a shot to the corners then?
            corners = [
                Vec3(-Field.WIDTH2, -bot.info.team_sign * Field.LENGTH2, 0),
                Vec3(Field.WIDTH2, -bot.info.team_sign * Field.LENGTH2, 0),
            ]
            for corner in corners:
                ctrls = bot.shoot.towards(bot, corner, bot.info.my_car.reach_ball_time)
                if bot.shoot.can_shoot:
                    self.aim_cone.draw(bot, bot.shoot.ball_when_hit.pos, b=0)
                    if bot.shoot.using_curve:
                        rendering.draw_bezier(bot, [car.pos, bot.shoot.curve_point, hit_pos])
                    return ctrls

            enemy_to_ball = normalize(xy(ball.pos - closest_enemy.pos))
            ball_to_my_goal = normalize(xy(bot.info.own_goal.pos - ball.pos))
            dot_threat = dot(enemy_to_ball, ball_to_my_goal)  # 1 = enemy is in position, -1 = enemy is NOT in position

            if car.boost <= 10 and ball.pos.y * bot.info.team_sign < 0 and dot_threat < 0.15:
                collect_center = ball.pos.y * bot.info.team_sign <= 0
                collect_small = closest_enemy.pos.y * bot.info.team_sign <= 0 or enemy_dist < 900
                pads = filter_pads(bot, bot.info.big_boost_pads, big_only=not collect_small, enemy_side=False,
                                   center=collect_center)
                bot.maneuver = CollectClosestBoostManeuver(bot, pads)

            # return home-ish
            return bot.drive.stay_at(bot, lerp(bot.info.own_goal.pos, ball.pos, 0.2), ball.pos)