def utility_score(self, bot) -> float: car = bot.info.my_car if len(bot.info.teammates) == 0: team_committed01 = 0 no_defence01 = 1 else: mates = bot.info.teammates sum_pos = mates[0].pos + mates[0].vel * 0.5 for mate in mates[1:]: sum_pos += mate.pos + mate.vel * 0.5 avg_pos = sum_pos / len(mates) team_committed01 = clip01(norm(avg_pos - bot.info.own_goal.pos) / Field.LENGTH2) no_defence01 = clip01(argmin(mates, lambda mate: norm(mate.pos - bot.info.own_goal.pos))[1] / 800) dist_to_ball01 = clip01(norm(car.pos - bot.info.ball.pos) / Field.LENGTH2) obj_bonus = { Objective.UNKNOWN: 0, Objective.GO_FOR_IT: 0, Objective.FOLLOW_UP: 0.1, Objective.ROTATE_BACK_OR_DEF: 0.3, }[car.objective] return 0.9 * team_committed01 * dist_to_ball01 * no_defence01 + obj_bonus
def exec(self, bot) -> SimpleControllerState: car = bot.data.my_car ball = bot.data.ball teammate = None if len(bot.data.teammates) > 0: teammate = bot.data.teammates[0] if teammate is not None: my_dist = norm(car.pos - ball.pos) teammate_dist = norm(teammate.pos - ball.pos) if my_dist < teammate_dist: return self.go_for_ball(bot) else: return self.approach_defensively(bot) else: land_event = next_ball_landing(bot) if land_event.happens: ball_at_landing = land_event.data["obj"] dist = norm(car.pos - ball_at_landing.pos) self.target = ball_at_landing.pos self.target_vel = dist / land_event.time else: self.target = ball.pos self.target_vel = 1400 return super().exec(bot)
def exec(self, bot): goal_to_ball = bot.info.ball.pos - bot.info.own_goal.pos goal_to_car = bot.info.my_car.pos - bot.info.own_goal.pos car_prj = proj_onto(goal_to_car, goal_to_ball) target = lerp(bot.info.own_goal.pos + car_prj, lerp(bot.info.ball.pos, bot.info.opp_goal.pos, 0.4), 0.08) if bot.do_rendering: bot.renderer.draw_line_3d(bot.info.my_car.pos, target, bot.renderer.purple()) speed = max( (norm(bot.info.my_car.pos - bot.info.ball.pos) - 900) * 0.6, 100) return bot.drive.towards_point( bot, target, target_vel=speed, slide=True, boost_min=0, can_keep_speed=norm(bot.info.my_car.pos - bot.info.ball.pos) > 3000, can_dodge=True, wall_offset_allowed=125)
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
def step(self, dt) -> SimpleControllerState: # car = bot.info.my_car # ball = bot.info.ball # car_to_ball = ball.pos - car.pos # dist = norm(car_to_ball) # ball_to_enemy_goal = bot.info.enemy_goal - ball.pos # own_goal_to_ball = ball.pos - bot.info.own_goal # offence = ball.pos.y * bot.info.team_sign < 0 # dot_enemy = dot(car_to_ball, ball_to_enemy_goal) # dot_own = dot(car_to_ball, own_goal_to_ball) # right_side_of_ball = dot_enemy > 0 if offence else dot_own > 0 # if right_side_of_ball: # self.go_towards_point(bot, ball.pos) # else: # self.go_towards_point(bot, bot.info.own_goal_field) car = self.car car_to_point = self.target - car.pos dist = norm(car_to_point) point_local = dot(self.target - car.pos, car.rot) # Angle to point in local xy plane and other stuff angle = math.atan2(point_local.y, point_local.x) # dist = norm(point_local) # Flip is finished # if self.flip is not None and self.flip.finished: # self.flip = None # self.last_flip_end_time = self.info.time # # Continue flip # elif self.flip is not None: # return self.flip.step(dt) # time_since_last_flip = self.info.time - self.last_flip_end_time # if dist > 2000 and abs(angle) <= 0.02 and time_since_last_flip > 3: # self.flip = Flip(self.bot) # return self.flip.step(dt) # Boost? if norm(car.vel) < 2200 and abs(angle) <= 0.02: self.controls.boost = True self.controls.steer = clip(angle + (2.5 * angle)**3, -1.0, 1.0) self.controls.throttle = 1.0 if self.info.time - self.start_time > 0.25: self.finished = True return self.controls
def exec(self, bot): car = bot.info.my_car ball = bot.info.ball car_to_ball = ball.pos - car.pos ball_to_enemy_goal = bot.info.enemy_goal - ball.pos own_goal_to_ball = ball.pos - bot.info.own_goal dist = norm(car_to_ball) offence = ball.pos.y * bot.info.team_sign < 0 dot_enemy = dot(car_to_ball, ball_to_enemy_goal) dot_own = dot(car_to_ball, own_goal_to_ball) right_side_of_ball = dot_enemy > 0 if offence else dot_own > 0 if right_side_of_ball: # Aim cone dir_to_post_1 = (bot.info.enemy_goal + Vec3(3800, 0, 0)) - bot.info.ball.pos dir_to_post_2 = (bot.info.enemy_goal + Vec3(-3800, 0, 0)) - bot.info.ball.pos cone = AimCone(dir_to_post_1, dir_to_post_2) cone.get_goto_point(bot, car.pos, bot.info.ball.pos) if bot.do_rendering: cone.draw(bot, bot.info.ball.pos) # Chase ball return bot.drive.go_towards_point(bot, xy(ball.pos), 2000, True, True, can_dodge=dist > 2200) else: # Go home return bot.drive.go_towards_point(bot, bot.info.own_goal_field, 2000, True, True)
def utility_score(self, bot) -> float: car = bot.info.my_car ball = bot.info.ball car_to_ball = car.pos - ball.pos bouncing_b = ball.pos.z > 130 or abs(ball.vel.z) > 300 if not bouncing_b: return 0 dist_01 = clip01(1 - norm(car_to_ball) / 3000) head_dir = lerp(Vec3(0, 0, 1), car.forward, 0.1) ang = angle_between(head_dir, car_to_ball) ang_01 = clip01(1 - ang / (math.pi / 2)) obj_bonus = { Objective.UNKNOWN: 0, Objective.GO_FOR_IT: 0.2, Objective.FOLLOW_UP: 0, Objective.ROTATE_BACK_OR_DEF: 0, }[car.objective] return clip01(0.6 * ang_01 + 0.4 * dist_01 # - 0.3 * bot.analyzer.team_mate_has_ball_01 + self.is_dribbling * self.extra_utility_bias + obj_bonus)
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)
def utility_score(self, bot) -> float: if self.temp_utility_desire_boost > 0: self.temp_utility_desire_boost = max(0, self.temp_utility_desire_boost - bot.info.dt) elif self.temp_utility_desire_boost < 0: self.temp_utility_desire_boost = min(0, self.temp_utility_desire_boost + bot.info.dt) car = bot.info.my_car ball_soon = predict.ball_predict(bot, 1) arena_length2 = bot.info.team_sign * Field.LENGTH2 own_half_01 = clip01(remap(arena_length2, -arena_length2, 0.0, 1.1, ball_soon.pos.y)) close_to_ball01 = clip01(1.0 - norm(car.pos - ball_soon.pos) / 3500) ** 0.5 reachable_ball = predict.ball_predict(bot, predict.time_till_reach_ball(bot.info.my_car, bot.info.ball)) self.ball_to_goal_right = bot.info.opp_goal.right_post - reachable_ball.pos self.ball_to_goal_left = bot.info.opp_goal.left_post - reachable_ball.pos self.aim_cone = AimCone(self.ball_to_goal_right, self.ball_to_goal_left) car_to_ball = reachable_ball.pos - bot.info.my_car.pos in_position = self.aim_cone.contains_direction(car_to_ball) # Chase ball right after kickoff. High right after kickoff kickoff_bias01 = max(0, 1 - bot.info.time_since_last_kickoff * 0.3) * float(bot.info.my_car.objective == Objective.UNKNOWN) obj_bonus = { Objective.UNKNOWN: 0, Objective.GO_FOR_IT: 0.2, Objective.FOLLOW_UP: 0, Objective.ROTATE_BACK_OR_DEF: -0.2, }[bot.info.my_car.objective] return clip01(close_to_ball01 * own_half_01 + 0.1 * in_position + self.temp_utility_desire_boost + kickoff_bias01) + obj_bonus
def time_till_reach_ball(car, ball): """ Rough estimate about when we can reach the ball in 2d. """ car_to_ball = xy(ball.pos - car.pos) dist = norm(car_to_ball) - Ball.RADIUS / 2 vel_c_f = proj_onto_size(car.vel, car_to_ball) vel_b_f = proj_onto_size(ball.vel, car_to_ball) vel_c_amp = lerp(vel_c_f, norm(car.vel), 0.58) vel_f = vel_c_amp - vel_b_f dist_long_01 = clip01(dist / 10_000.0) time_normal = dist / max(220, vel_f) time_long = dist / max(norm(car.vel), 1410) time = lerp(time_normal, time_long, dist_long_01) arrive_time = time * 0.95 # Combine slightly with old prediction to negative rapid changes result = lerp(arrive_time, car.last_expected_time_till_reach_ball, 0.22) car.last_expected_time_till_reach_ball = arrive_time return result
def exec(self, bot) -> SimpleControllerState: DODGE_DIST = 250 MIDDLE_OFFSET = 430 # Since ball is at (0,0) we don't we a car_to_ball variable like we do so many other places car = bot.info.my_car dist = norm(car.pos) vel_p = -proj_onto_size(car.vel, car.pos) point = Vec3(0, bot.info.team_sign * (dist / 2.6 - MIDDLE_OFFSET), 0) speed = 2300 opp_dist = norm(bot.info.opponents[0].pos) opp_does_kick = opp_dist < dist + 600 # Opponent is not going for kickoff, so we slow down a bit if not opp_does_kick: speed = 2210 point = Vec3(0, bot.info.team_sign * (dist / 2.05 - MIDDLE_OFFSET), 0) point += Vec3(35 * sign(car.pos.x), 0, 0) # Dodge when close to (0, 0) - but only if the opponent also goes for kickoff. # The dodge itself should happen in about 0.3 seconds if dist - DODGE_DIST < vel_p * 0.3 and opp_does_kick: bot.drive.start_dodge(bot) # Make two dodges when spawning far back elif dist > 3640 and vel_p > 1200 and not opp_does_kick: bot.drive.start_dodge(bot) # Pickup boost when spawning back corner by driving a bit towards the middle boost pad first elif abs(car.pos.x) > 230 and abs(car.pos.y) > 2880: # The pads exact location is (0, 2816), but don't have to be exact point.y = bot.info.team_sign * 2790 self.done = not bot.info.is_kickoff bot.renderer.draw_line_3d(car.pos, point, bot.renderer.white()) return bot.drive.towards_point(bot, point, target_vel=speed, slide=False, boost_min=0, can_dodge=False, can_keep_speed=False)
def closest_enemy(self, pos: Vec3): enemy = None dist = -1 for e in self.opponents: d = norm(e.pos - pos) if enemy is None or d < dist: enemy = e dist = d return enemy, dist
def approach_defensively(self, bot) -> SimpleControllerState: car = bot.data.my_car ball = bot.data.ball goal = Vec3(y=5400 * bot.data.team_sign) self.target = lerp(ball.pos, goal, 0.8) self.target_vel = norm(self.target - car.pos) return super().exec(bot)
def exec(self, bot): controls = SimpleControllerState() dt = bot.info.dt car = bot.info.my_car relative_rotation = dot(transpose(car.rot), self.target) geodesic_local = rotation_to_axis(relative_rotation) # figure out the axis of minimal rotation to target geodesic_world = dot(car.rot, geodesic_local) # get the angular acceleration alpha = Vec3( self.controller(geodesic_world.x, car.ang_vel.x, dt), self.controller(geodesic_world.y, car.ang_vel.y, dt), self.controller(geodesic_world.z, car.ang_vel.z, dt) ) # reduce the corrections for when the solution is nearly converged alpha.x = self.q(abs(geodesic_world.x) + abs(car.ang_vel.x)) * alpha.x alpha.y = self.q(abs(geodesic_world.y) + abs(car.ang_vel.y)) * alpha.y alpha.z = self.q(abs(geodesic_world.z) + abs(car.ang_vel.z)) * alpha.z # set the desired next angular velocity ang_vel_next = car.ang_vel + alpha * dt # determine the controls that produce that angular velocity roll_pitch_yaw = AerialTurnManeuver._aerial_rpy(car.ang_vel, ang_vel_next, car.rot, dt) controls.roll = roll_pitch_yaw.x controls.pitch = roll_pitch_yaw.y controls.yaw = roll_pitch_yaw.z self._timer += dt if ((norm(car.ang_vel) < self.epsilon_ang_vel and norm(geodesic_world) < self.epsilon_rotation) or self._timer >= self.timeout or car.on_ground): self.done = True controls.throttle = 1.0 return controls
def index_of_teammate_at_kickoff_spawn(bot, loc): """ Returns index of teammate at loc, or -1 if there is no teammate """ # RLU Cars does not contain index, so we have to find that ourselves :( for car in bot.info.teammates: dist = norm(car.pos - loc) if dist < 150: return car.index return -1
def pick_pad(self, bot, pads: List[BoostPad]): # Find closest boost pad my_pos = bot.info.my_car.pos shortest_dist = 99999999 for pad in pads: if pad.is_active: dist = norm(my_pos - pad.pos) if dist < shortest_dist: self.closest_pad = pad shortest_dist = dist
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)
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)
def utility(self, bot) -> float: team_sign = bot.info.team_sign ball = bot.info.ball ball_to_goal = bot.info.own_goal - ball.pos too_close = norm(ball_to_goal) < Field.GOAL_WIDTH / 2 + Ball.RADIUS hits_goal_prediction = predict.will_ball_hit_goal(bot) hits_goal = hits_goal_prediction.happens and sign( ball.vel.y) == team_sign and hits_goal_prediction.time < 3 return hits_goal or too_close
def exec(self, bot) -> SimpleControllerState: car = bot.info.my_car # End maneuver when almost there car_to_pad = self.closest_pad.pos - car.pos vel = proj_onto_size(car.vel, car_to_pad) dist = norm(car_to_pad) if dist < 50 + vel * 0.2 or car.boost > 50: self.done = True bot.renderer.draw_line_3d(car.pos, self.closest_pad.pos, bot.renderer.yellow()) return bot.drive.go_towards_point(bot, self.closest_pad.pos, target_vel=2200, slide=True, boost_min=0)
def run(self, bot) -> SimpleControllerState: car = bot.info.my_car ball = bot.info.ball half_way = lerp(bot.info.own_goal.pos, ball.pos, 0.5) dist_ball = norm(car.pos - ball.pos) return bot.drive.towards_point( bot, half_way, target_vel=dist_ball * 0.3, slide=dist_ball > 1500, can_dodge=False )
def exec(self, bot) -> SimpleControllerState: car = bot.data.my_car target_goal = Vec3(y=-5600 * bot.data.team_sign) vel_prj_to_goal = proj_onto_size(car.vel, target_goal - car.pos) enemy, enemy_dist = bot.data.closest_enemy(bot.data.ball.pos) if car.on_ground and enemy_dist < 1100 and vel_prj_to_goal > 1230 and norm( target_goal - car.pos) > 1500: bot.maneuver = DodgeShotManeuver(bot) # Default to just going straight return self.go_towards_goal.exec(bot)
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)
def exec(self, bot) -> SimpleControllerState: car = bot.info.my_car car_to_pad = self.boost_pad_pos - car.pos vel = proj_onto_size(car.vel, car_to_pad) dist = norm(car_to_pad) if dist < vel * 0.3: self.done = True # Drive towards the pad return bot.drive.towards_point(bot, self.boost_pad_pos, target_vel=2300, boost_min=0, can_keep_speed=True)
def exec(self, bot) -> SimpleControllerState: car = bot.info.my_car self.done = norm( car.pos ) < 1100 # End when getting close to ball (approx at boost pad) curve_point = curve_from_arrival_dir(car.pos, self.target_loc, self.target_dir) return bot.drive.towards_point(bot, curve_point, target_vel=1200, slide=True, boost_min=20, can_keep_speed=False)
def draw_debug(renderer, car, ball, bot): renderer.begin_rendering() # draw a line from the car to the ball # renderer.draw_line_3d(car.physics.location, ball.physics.location, renderer.white()) # print the action that the bot is taking # renderer.draw_string_3d(car.physics.location, 2, 2, action_display, renderer.white()) corner_debug = "Car velocity: {}\n".format(norm(car.vel)) corner_debug += "Car pos x: {}\n".format(round(car.pos.x)) corner_debug += "Car pos y: {}\n".format(round(car.pos.y)) corner_debug += "Car boost: {}\n".format(car.boost) corner_debug += "Maneuver: {}\n".format(type(bot.maneuver)) corner_display_y = 900 - (corner_debug.count('\n') * 20) renderer.draw_string_2d(10, corner_display_y, 1, 1, corner_debug, renderer.white())
def get_boost_pad_convenience_score(self, pad): if not pad.is_active: return 0 car_to_pad = pad.pos - self.my_car.pos angle = angle_between(self.my_car.forward, car_to_pad) # Pads behind the car is bad if abs(angle) > 1.3: return 0 dist = norm(car_to_pad) dist_score = 1 - clip((abs(dist) / 2500)**2, 0, 1) angle_score = 1 - clip((abs(angle) / 3), 0, 1) return dist_score * angle_score * (0.8, 1)[pad.is_big]
def __init__(self, bot, pads: List[BoostPad]=None, target_vel: int=2200): super().__init__() self.target_vel = target_vel self.closest_pad = None if pads is None: pads = bot.info.big_boost_pads # Find closest boost pad my_pos = bot.info.my_car.pos shortest_dist = 99999999 for pad in pads: dist = norm(my_pos - pad.pos) if dist < shortest_dist: self.closest_pad = pad shortest_dist = dist
def go_for_ball(self, bot) -> SimpleControllerState: car = bot.data.my_car ball = bot.data.ball land_event = next_ball_landing(bot) if land_event.happens: ball_at_landing = land_event.data["obj"] dist = norm(car.pos - ball_at_landing.pos) self.target = ball_at_landing.pos self.target_vel = dist / land_event.time else: self.target = ball.pos self.target_vel = 1400 return super().exec(bot)
def utility_score(self, bot) -> float: team_sign = bot.info.team_sign ball = bot.info.ball ball_to_goal = bot.info.own_goal.pos - ball.pos too_close = norm(ball_to_goal) < Goal.WIDTH2 + Ball.RADIUS hits_goal_prediction = predict.will_ball_hit_goal(bot) hits_goal = hits_goal_prediction.happens and sign( ball.vel.y) == team_sign and hits_goal_prediction.time < 3 obj_bonus = { Objective.UNKNOWN: 0, Objective.GO_FOR_IT: 0.2, Objective.FOLLOW_UP: 0, Objective.ROTATE_BACK_OR_DEF: 0.1, }[bot.info.my_car.objective] return float(hits_goal or too_close) + obj_bonus