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 _aerial_rpy(ang_vel_start, ang_vel_next, rot, dt): """ :param ang_vel_start: beginning step angular velocity (world coordinates) :param ang_vel_next: next step angular velocity (world coordinates) :param rot: orientation matrix :param dt: time step :return: Vec3 with roll pitch yaw controls """ # car's moment of inertia (spherical symmetry) J = 10.5 # aerial control torque coefficients T = Vec3(-400.0, -130.0, 95.0) # aerial damping torque coefficients H = Vec3(-50.0, -30.0, -20.0) # get angular velocities in local coordinates w0_local = dot(ang_vel_start, rot) w1_local = dot(ang_vel_next, rot) # PWL equation coefficients a = [T[i] * dt / J for i in range(0, 3)] b = [-w0_local[i] * H[i] * dt / J for i in range(0, 3)] c = [w1_local[i] - (1 + H[i] * dt / J) * w0_local[i] for i in range(0, 3)] # RL treats roll damping differently b[0] = 0 return Vec3( solve_PWL(a[0], b[0], c[0]), solve_PWL(a[1], b[1], c[1]), solve_PWL(a[2], b[2], c[2]) )
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 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
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
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)
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 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 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())
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 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
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
def towards_point(self, bot, point: Vec3, target_vel=1430, slide=False, boost_min=101, can_keep_speed=True, can_dodge=True, wall_offset_allowed=125) -> SimpleControllerState: REQUIRED_ANG_FOR_SLIDE = 1.65 REQUIRED_VELF_FOR_DODGE = 1100 car = bot.info.my_car # Dodge is done if self.dodge is not None and self.dodge.done: self.dodge = None self.last_dodge_end_time = bot.info.time # Continue dodge elif self.dodge is not None: self.dodge.target = point return self.dodge.exec(bot) # Begin recovery if not car.on_ground: bot.maneuver = RecoveryManeuver(bot) return self.controls # Get down from wall by choosing a point close to ground if not is_near_wall(point, wall_offset_allowed) and angle_between( car.up, Vec3(0, 0, 1)) > math.pi * 0.31: point = lerp(xy(car.pos), xy(point), 0.5) # If the car is in a goal, avoid goal posts self._avoid_goal_post(bot, point) car_to_point = point - car.pos # The vector from the car to the point in local coordinates: # point_local.x: how far in front of my car # point_local.y: how far to the left of my car # point_local.z: how far above my car point_local = dot(point - 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) vel_f = proj_onto_size(car.vel, car.forward) vel_towards_point = proj_onto_size(car.vel, car_to_point) # Start dodge if can_dodge and abs(angle) <= 0.02 and vel_towards_point > REQUIRED_VELF_FOR_DODGE\ and dist > vel_towards_point + 500 + 900 and bot.info.time > self.last_dodge_end_time + self.dodge_cooldown: self.dodge = DodgeManeuver(bot, point) # Start half-flip elif can_dodge and abs(angle) >= 3 and vel_towards_point < 50\ and dist > -vel_towards_point + 500 + 900 and bot.info.time > self.last_dodge_end_time + self.dodge_cooldown: self.dodge = HalfFlipManeuver(bot, boost=car.boost > boost_min + 10) # Is point right behind? Maybe reverse instead if -100 < point_local.x < 0 and abs(point_local.y) < 50: #bot.print("Reversing?") pass # Is in turn radius deadzone? tr = turn_radius(abs(vel_f + 50)) # small bias tr_side = sign(angle) tr_center_local = Vec3(0, tr * tr_side, 10) point_is_in_turn_radius_deadzone = norm(point_local - tr_center_local) < tr # Draw turn radius deadzone if car.on_ground and bot.do_rendering: tr_center_world = car.pos + dot(car.rot, tr_center_local) tr_center_world_2 = car.pos + dot(car.rot, -1 * tr_center_local) rendering.draw_circle(bot, tr_center_world, car.up, tr, 22) rendering.draw_circle(bot, tr_center_world_2, car.up, tr, 22) if point_is_in_turn_radius_deadzone: # Hard turn self.controls.steer = sign(angle) self.controls.boost = False self.controls.throttle = 0 if vel_f > 150 else 0.1 if point_local.x < 110 and point_local.y < 400 and norm( car.vel) < 300: # Brake or go backwards when the point is really close but not in front of us self.controls.throttle = clip(-0.25 + point_local.x / -110.0, 0, -1) self.controls.steer = -0.5 * sign(angle) else: # Should drop speed or just keep up the speed? if can_keep_speed and target_vel < vel_towards_point: target_vel = vel_towards_point else: # Small lerp adjustment target_vel = lerp(vel_towards_point, target_vel, 1.1) # Turn and maybe slide self.controls.steer = clip(angle + (2.5 * angle)**3, -1.0, 1.0) if slide and abs(angle) > REQUIRED_ANG_FOR_SLIDE: self.controls.handbrake = True self.controls.steer = sign(angle) else: self.controls.handbrake = False # Overshoot target vel for quick adjustment target_vel = lerp(vel_towards_point, target_vel, 1.2) # Find appropriate throttle/boost if vel_towards_point < target_vel: self.controls.throttle = 1 if boost_min < car.boost and vel_towards_point + 80 < target_vel and target_vel > 1400 \ and not self.controls.handbrake and is_heading_towards(angle, dist): self.controls.boost = True else: self.controls.boost = False else: vel_delta = target_vel - vel_towards_point self.controls.throttle = clip(0.2 + vel_delta / 500, 0, -1) self.controls.boost = False if self.controls.handbrake: self.controls.throttle = min(0.4, self.controls.throttle) # Saved if something outside calls start_dodge() in the meantime self.last_point = point return self.controls
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
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)