def any_shot(info: GameInfo, car: Car, target: vec3, intercept: Intercept, allow_dribble=False) -> Maneuver: ball = intercept.ball if (allow_dribble and (ball.position[2] > 100 or abs(ball.velocity[2]) > 250 or distance(car, info.ball) < 300) and abs(ball.velocity[2]) < 700 and ground_distance(car, ball) < 1500 and ground_distance(ball, info.my_goal.center) > 1000 and ground_distance(ball, info.their_goal.center) > 1000 and not is_opponent_close(info, info.ball.position[2] * 2 + 1000)): return CarryAndFlick(car, info, target) direct = direct_shot(info, car, target) if not isinstance(direct, GroundStrike) and intercept.time < car.time + 4.0: alignment = align(car.position, ball, target) if alignment < -0.3 and abs(ball.position[1] - target[1]) > 3000: return MirrorStrike(car, info, target) return direct
def set_kickoff_maneuvers(self, drones: List[Drone]): nearest_drone = min( drones, key=lambda drone: ground_distance(drone.car, self.info.ball)) nearest_drone.maneuver = kickoffs.choose_kickoff( self.info, nearest_drone.car) self.drone_going_for_ball = nearest_drone self.boost_reservations.clear() corner_drones = [ drone for drone in drones if abs(drone.car.position[0]) > 2000 ] if len(corner_drones) > 1: other_corner_drone = next(drone for drone in corner_drones if drone is not nearest_drone) nearest_pad = min( self.info.large_boost_pads, key=lambda pad: distance(other_corner_drone.car, pad)) other_corner_drone.maneuver = HalfFlipPickup( other_corner_drone.car, nearest_pad) self.boost_reservations[other_corner_drone] = nearest_pad self.defending_drone = max( drones, key=lambda drone: ground_distance(drone.car, self.info.ball)) self.defending_drone.maneuver = DriveBackwardsToGoal( self.defending_drone.car, self.info) for drone in drones: if drone not in corner_drones + [self.defending_drone ] + [self.drone_going_for_ball]: self.send_drone_for_boost(drone)
def __init__(self, car: Car, info: GameInfo, face_target: vec3, distance_from_target: float): super().__init__(car) self.info = info self.face_target = face_target dist = min(distance_from_target, ground_distance(face_target, self.info.my_goal.center) - 50) target_pos = ground(face_target) + ground_direction( face_target, self.info.my_goal.center) * dist near_goal = ground_distance(car, info.my_goal.center) < 3000 side_shift = 400 if near_goal else 2500 points = [ target_pos + vec3(side_shift, 0, 0), target_pos - vec3(side_shift, 0, 0) ] target_pos = nearest_point(face_target, points) if near_goal else farthest_point( face_target, points) target_pos = Arena.clamp(target_pos, 500) self.travel = Travel(car, target_pos) self.travel.finish_distance = 800 if near_goal else 1500 self.drive = Drive(car) self.stop = Stop(car) self.start_time = car.time self.pad = None
def direct_shot(info: GameInfo, car: Car, target: vec3) -> Strike: dodge_shot = DodgeStrike(car, info, target) ground_shot = GroundStrike(car, info, target) if car.boost > 40: # TODO # aerial_strike = AerialStrike(car, info, target) fast_aerial = FastAerialStrike(car, info, target) better_aerial_strike = min([fast_aerial], key=lambda strike: strike.intercept.time) if (better_aerial_strike.intercept.time < dodge_shot.intercept.time and abs(better_aerial_strike.intercept.position[1] - info.their_goal.center[1]) > 500): if ground_distance(better_aerial_strike.intercept, info.their_goal.center) < 8000: return DoubleTouch(better_aerial_strike) return better_aerial_strike if (dodge_shot.intercept.time < ground_shot.intercept.time - 0.1 or ground_distance(dodge_shot.intercept, target) < 2000 or distance(ground_shot.intercept.ball.velocity, car.velocity) < 500 or is_opponent_close(info, 300)): if (distance(dodge_shot.intercept.ground_pos, target) < 4000 and abs(dodge_shot.intercept.ground_pos[0]) < 2000): return CloseShot(car, info, target) return dodge_shot return ground_shot
def direct_shot(self, car: Car, target: vec3) -> Maneuver: dodge_shot = DodgeStrike(car, self.info, target) ground_shot = GroundStrike(car, self.info, target) if car.boost > 40: # TODO aerial_strike = AerialStrike(car, self.info, target) fast_aerial = FastAerialStrike(car, self.info, target) better_aerial_strike = min( [aerial_strike, fast_aerial], key=lambda strike: strike.intercept.time) if better_aerial_strike.intercept.time < dodge_shot.intercept.time: if ground_distance(better_aerial_strike.intercept, self.info.their_goal.center) < 5000: return DoubleTouch(better_aerial_strike) return better_aerial_strike if (dodge_shot.intercept.time < ground_shot.intercept.time - 0.1 or ground_distance(dodge_shot.intercept, target) < 4000 or distance(ground_shot.intercept.ball.velocity, car.velocity) < 500): if (distance(dodge_shot.intercept.ground_pos, target) < 4000 and abs(dodge_shot.intercept.ground_pos[0]) < 3000): return CloseShot(car, self.info, target) return dodge_shot return ground_shot
def choose_maneuver(info: GameInfo, my_car: Car): ball = info.ball teammates = info.get_teammates(my_car) my_team = [my_car] + teammates their_goal = ground(info.their_goal.center) my_goal = ground(info.my_goal.center) # recovery if not my_car.on_ground: return Recovery(my_car) # kickoff if ball.position[0] == 0 and ball.position[1] == 0: # if I'm nearest (or tied) to the ball, go for kickoff if distance(my_car, ball) == min(distance(car, ball) for car in my_team): return kickoffs.choose_kickoff(info, my_car) if my_car.boost < 20: best_boostpad = choose_boostpad_to_pickup(info, my_car) if best_boostpad is not None: return PickupBoostPad(my_car, best_boostpad) info.predict_ball() my_intercept = Intercept(my_car, info.ball_predictions) teammates_intercepts = [Intercept(mate, info.ball_predictions) for mate in teammates] our_intercepts = teammates_intercepts + [my_intercept] good_intercepts = [i for i in our_intercepts if align(i.car.position, i.ball, their_goal) > 0.0] if good_intercepts: best_intercept = min(good_intercepts, key=lambda intercept: intercept.time) else: best_intercept = min(our_intercepts, key=lambda i: distance(i.car, my_goal)) if ground_distance(my_car, my_goal) < 2000: best_intercept = my_intercept if best_intercept is my_intercept: # if not completely out of position, go for a shot if ( align(my_intercept.car.position, my_intercept.ball, their_goal) > 0 or ground_distance(my_intercept, my_goal) > 6000 ): return offense.any_shot(info, my_intercept.car, their_goal, my_intercept) # otherwise try to clear else: return defense.any_clear(info, my_intercept.car) # if I'm nearest to goal, stay far back if min(my_team, key=lambda car: distance(car, my_goal)) is my_car: return GeneralDefense(my_car, info, my_intercept.position, 7000) # otherwise get into position return GeneralDefense(my_car, info, my_intercept.position, 4000)
def step(self, dt): target = self.target car = self.car if self.target_direction is not None: car_speed = norm(car.velocity) target_direction = normalize(self.target_direction) # in order to arrive in a direction, we need to shift the target in the opposite direction # the magnitude of the shift is based on how far are we from the target shift = clamp( ground_distance(car.position, target) * self.lerp_t, 0, car_speed * 1.5) # if we're too close to the target, aim for the actual target so we don't miss it if shift - self.additional_shift < Drive.turn_radius( clamp(car_speed, 1400, 2000) * 1.1): shift = 0 else: shift += self.additional_shift shifted_target = target - target_direction * shift time_shift = ground_distance(shifted_target, target) * 0.7 / clamp( car_speed, 500, 2300) shifted_arrival_time = self.arrival_time - time_shift else: shifted_target = target shifted_arrival_time = self.arrival_time self.drive.target_pos = shifted_target self.travel.target = shifted_target dist_to_target = ground_distance(car.position, shifted_target) time_left = nonzero(shifted_arrival_time - car.time) target_speed = clamp(dist_to_target / time_left, 0, 2300) self.drive.target_speed = target_speed self.drive.backwards = self.backwards # dodges and wavedashes can mess up correctly arriving, so we use them only if we really need them if ((self.allow_dodges_and_wavedashes and norm(car.velocity) < target_speed - 600 and car.boost < 20 and not self.backwards) or not self.travel.driving # a dodge/wavedash is in progress ): self.action = self.travel else: self.action = self.drive self.action.step(dt) self.controls = self.action.controls self.finished = self.car.time >= self.arrival_time
def step(self, dt): if not self.flicking: self.carry.step(dt) self.controls = self.carry.controls self.finished = self.carry.finished car = self.car ball = self.info.ball # check if it's a good idea to flick dir_to_target = ground_direction(car, self.target) if (distance(car, ball) < 150 and ground_distance(car, ball) < 100 and dot(car.forward(), dir_to_target) > 0.7 and norm(car.velocity) > clamp( distance(car, self.target) / 3, 1000, 1700) and dot(dir_to_target, ground_direction(car, ball)) > 0.9): self.flicking = True # flick if opponent is close for opponent in self.info.get_opponents(): if (distance(opponent.position + opponent.velocity, car) < max( 300.0, norm(opponent.velocity) * 0.5) and dot(opponent.velocity, direction(opponent, self.info.ball)) > 0.5): if distance(car.position, self.info.ball.position) < 200: self.flicking = True else: self.finished = True else: self.flick.target = self.info.ball.position + self.info.ball.velocity * 0.2 self.flick.step(dt) self.controls = self.flick.controls self.finished = self.flick.finished
def step(self, dt: float): car = self.car if self.phase == 1: if norm(car.velocity) > 800: self.action = SpeedFlip( car, right_handed=local(car, self.info.ball.position)[1] < 0) self.phase = 2 self._speed_flip_start_time = car.time if self.phase == 2: if self.action.finished and self.car.on_ground: self.action = self.drive self.drive.target_pos = vec3(0, 0, 0) self.phase = 3 if self.phase == 3: if ground_distance(self.car, vec3(0, 0, 0)) < 500: self.action = AirDodge(car, 0.1, vec3(0, 0, 0)) self.phase = 4 if self.phase == 4: if self.action.finished: self.finished = True # abort when taking too long if car.time > self._speed_flip_start_time + 3.0: self.finished = True super().step(dt)
def estimate_time(car: Car, target, dd=1) -> float: turning_radius = 1 / Drive.max_turning_curvature(norm(car.velocity) + 500) turning = angle_between(car.forward() * dd, direction( car, target)) * turning_radius / 1800 if turning < 0.5: turning = 0 dist = ground_distance(car, target) - 200 if dist < 0: return turning speed = dot(car.velocity, car.forward()) time = 0 result = None if car.boost > 0 and dd > 0: boost_time = car.boost / 33.33 result = BOOST.simulate_until_limit(speed, distance_limit=dist, time_limit=boost_time) dist -= result.distance_traveled time += result.time_passed speed = result.speed_reached if dist > 0 and speed < 1410: result = THROTTLE.simulate_until_limit(speed, distance_limit=dist) dist -= result.distance_traveled time += result.time_passed speed = result.speed_reached if result is None or not result.distance_limit_reached: time += dist / speed return time * 1.05 + turning
def step(self, dt): ball = Ball(self.ball) car = self.car # simulate ball until it gets near the floor while (ball.position[2] > 120 or ball.velocity[2] > 0) and ball.time < car.time + 10: ball.step(1/60) ball_local = local(car, ground(ball.position)) target = local(car, self.target) shift = ground(direction(ball_local, target)) shift[1] *= 1.8 shift = normalize(shift) max_turn = clamp(norm(car.velocity) / 800, 0, 1) max_shift = normalize(vec3(1 - max_turn, max_turn * sign(shift[1]), 0)) if abs(shift[1]) > abs(max_shift[1]) or shift[0] < 0: shift = max_shift shift *= clamp(car.boost, 40, 60) shift[1] *= clamp(norm(car.velocity)/1000, 1, 2) self._shift_direction = normalize(world(car, shift) - car.position) target = world(car, ball_local - shift) speed = distance(car.position, target) / max(0.001, ball.time - car.time) self.drive.target_speed = speed self.drive.target_pos = target self.drive.step(dt) self.controls = self.drive.controls self.finished = self.ball.position[2] < 100 or ground_distance(self.ball, self.car) > 2000
def any_shot(self, car: Car, target: vec3, intercept: Intercept) -> Maneuver: ball = intercept.ball if (self.allow_dribbles and (100 < ball.position[2] or abs(ball.velocity[2]) > 300) and abs(ball.velocity[2]) < 1500 and ground_distance(car, ball) < 1500 and ground_distance(ball, self.info.my_goal.center) > 1000): if not self.is_opponent_close(car, ball): return CarryAndFlick(car, self.info, target) alignment = align(car.position, ball, target) if alignment < 0.1 and abs(ball.position[1] - target[1]) > 3000: return MirrorStrike(car, self.info, target) # if 250 < ball.position[2] < 550 and self.is_opponent_close(car, ball): # return DoubleJumpStrike(car, self.info, target) return self.direct_shot(car, target)
def step(self, dt): if self.jumping: self.controls = Input() # first jump for full 0.2 sec if self.timer <= 0.2: self.controls.jump = True # single tick between jumps elif self.timer <= 0.2 + dt * JUMP_FALSE_TICKS: self.controls.jump = False # second jump else: self.controls.jump = True self.jumping = False self.timer += dt else: self.finished = self.intercept.time < self.info.time if self.car.on_ground: # manage speed before jump distance_to_target = ground_distance(self.car.position, self.intercept.position) if distance_to_target < MIN_DIST_BEFORE_SPEED_CONTROL: target_speed = distance_to_target / self.time_for_jump self.drive.target_speed = -target_speed if self._should_strike_backwards else target_speed self.drive.step(dt) self.controls = self.drive.controls else: super().step(dt) # decide when to jump ground_vel = ground(self.car.velocity) direction_to_target = ground_direction(self.car.position, self.intercept.position) alignment = dot(normalize(ground_vel), direction_to_target) # check alignment if alignment >= MIN_ALIGNMENT: # check that speed is correct speed_in_direction = dot(ground_vel, direction_to_target) time_to_target = distance_to_target / speed_in_direction if self.time_for_jump - ALLOWED_TIME_ERROR <= time_to_target <= self.time_for_jump + ALLOWED_TIME_ERROR: self.jumping = True # after jump (when the car is in the air) else: # face the ball for some additional height self.reorient.target_orientation = look_at( direction(self.car.position, self.info.ball), vec3(0, 0, 1)) self.reorient.step(dt) self.controls = self.reorient.controls
def __init__(self, car: Car, info: GameInfo, face_target: vec3, distance_from_target: float, force_nearest=False): super().__init__(car) self.info = info self.face_target = face_target dist = min(distance_from_target, ground_distance(face_target, self.info.my_goal.center) - 50) target_pos = ground(face_target) + ground_direction( face_target, self.info.my_goal.center) * dist near_goal = abs(car.position[1] - info.my_goal.center[1]) < 3000 side_shift = 400 if near_goal else 1800 points = target_pos + vec3(side_shift, 0, 0), target_pos - vec3( side_shift, 0, 0) if abs(self.car.position.x) > 3000: force_nearest = True target_pos = nearest_point( face_target, points) if near_goal or force_nearest else farthest_point( face_target, points) if abs(face_target[0]) < 1000 or ground_distance(car, face_target) < 1000: target_pos = nearest_point(car.position, points) target_pos = Arena.clamp(target_pos, 500) self.travel = Travel(car, target_pos) self.travel.finish_distance = 800 if near_goal else 1500 self.drive = Drive(car) self.stop = Stop(car) self.start_time = car.time self.pad = None
def step(self, dt): # update finished state even if we are not using the controls self.travel.step(dt) if self.travel.finished: # turn around to face the target direction if angle_to(self.car, self.face_target) > 0.3: self.drive.target_pos = self.face_target self.drive.target_speed = 1000 self.drive.step(dt) self.controls = self.drive.controls self.controls.handbrake = False else: self.stop.step(dt) self.controls = self.stop.controls else: self.pad = None # collect boost pads on the way (greedy algorithm, assumes first found is best) if self.car.boost < 90 and self.travel.interruptible(): to_target = ground_direction(self.car, self.travel.target) for pad in self.info.large_boost_pads + self.info.small_boost_pads: to_pad = ground_direction(self.car, pad) if (pad.is_active and distance(self.car, pad) < self.BOOST_LOOK_RADIUS and angle_between(to_target, to_pad) < self.BOOST_LOOK_ANGLE): self.pad = pad self.drive.target_pos = pad.position self.drive.target_speed = 2200 self.drive.step(dt) self.controls = self.drive.controls break # go to the actual target if self.pad is None: self.controls = self.travel.controls # don't waste boost during downtime if self.car.boost < 100 and ground_distance(self.car, self.travel.target) < 4000: self.controls.boost = False self.finished = self.travel.driving and self.car.time > self.start_time + self.DURATION
def step(self, dt): target = self.target_pos # don't try driving outside the arena target = Arena.clamp(target, 100) # smoothly escape goal if abs(self.car.position[1]) > Arena.size[1] - 50 and abs( self.car.position.x) < 1000: target = Arena.clamp(target, 200) target[0] = abs_clamp(target[0], 700) if not self.drive_on_walls: seam_radius = 100 if abs( self.car.position[1]) > Arena.size[1] - 100 else 200 if self.car.position[2] > seam_radius: target = ground(self.car) local_target = local(self.car, target) if self.backwards: local_target[0] *= -1 local_target[1] *= -1 # steering phi = math.atan2(local_target[1], local_target[0]) self.controls.steer = clamp11(3.0 * phi) # powersliding self.controls.handbrake = 0 if (abs(phi) > 1.5 and self.car.position[2] < 300 and (ground_distance(self.car, target) < 3500 or abs(self.car.position[0]) > 3500) and dot(normalize(self.car.velocity), self.car.forward()) > 0.85): self.controls.handbrake = 1 # forward velocity vf = dot(self.car.velocity, self.car.forward()) if self.backwards: vf *= -1 # speed controller if vf < self.target_speed: self.controls.throttle = 1.0 if self.target_speed > 1400 and vf < 2250 and self.target_speed - vf > 50: self.controls.boost = 1 else: self.controls.boost = 0 else: if (vf - self.target_speed) > 400: # 75 self.controls.throttle = -1.0 elif (vf - self.target_speed) > 100: if self.car.up()[2] > 0.85: self.controls.throttle = 0.0 else: self.controls.throttle = 0.01 self.controls.boost = 0 # backwards driving if self.backwards: self.controls.throttle *= -1 self.controls.steer *= -1 self.controls.boost = 0 self.controls.handbrake = 0 # don't boost if not facing target if abs(phi) > 0.3: self.controls.boost = 0 # finish when close if distance(self.car, self.target_pos) < 100: self.finished = True
def step(self, dt): car = self.car target = ground(self.target) car_speed = norm(car.velocity) time_left = (ground_distance(car, target) - self.finish_distance) / max(car_speed + 500, 1400) forward_speed = dot(car.forward(), car.velocity) if self.driving and car.on_ground: self.action.target_pos = target self._time_on_ground += dt # check if it's a good idea to dodge, wavedash or halfflip if ( self._time_on_ground > 0.2 and car.position[2] < 200 and car_speed < 2000 and angle_to(car, target, backwards=forward_speed < 0) < 0.1 and car.gravity[2] < -500 # don't dodge in low gravity ): # if going forward, use a dodge or a wavedash if forward_speed > 0: use_boost_instead = self.waste_boost and car.boost > 20 if car_speed > 1200 and not use_boost_instead: if time_left > self.DODGE_DURATION: dodge = Dodge(car) dodge.duration = 0.07 dodge.direction = vec2(direction(car, target)) self.action = dodge self.driving = False elif time_left > self.WAVEDASH_DURATION: wavedash = Wavedash(car) wavedash.direction = vec2(direction(car, target)) self.action = wavedash self.driving = False # if going backwards, use a halfflip elif time_left > self.HALFFLIP_DURATION and car_speed > 800: self.action = HalfFlip(car, self.waste_boost and time_left > 3) self.driving = False self.action.step(dt) self.controls = self.action.controls # make sure we're not boosting airborne if self.driving and not car.on_ground: self.controls.boost = False # make sure we're not stuck turtling if not car.on_ground: self.controls.throttle = 1 # a dodge/wavedash/halfflip has finished if self.action.finished and not self.driving: self.driving = True self._time_on_ground = 0 self.action = self.drive self.drive.backwards = False if ground_distance(car, target) < self.finish_distance and self.driving: self.finished = True
def choose_maneuver(info: GameInfo, my_car: Car): ball = info.ball their_goal = ground(info.their_goal.center) my_goal = ground(info.my_goal.center) opponents = info.get_opponents() # recovery if not my_car.on_ground: return Recovery(my_car) # kickoff if ball.position[0] == 0 and ball.position[1] == 0: return kickoffs.choose_kickoff(info, my_car) info.predict_ball() my_intercept = Intercept(my_car, info.ball_predictions) their_intercepts = [ Intercept(opponent, info.ball_predictions) for opponent in opponents ] their_intercept = min(their_intercepts, key=lambda i: i.time) opponent = their_intercept.car banned_boostpads = { pad for pad in info.large_boost_pads if abs(pad.position[1] - their_goal[1]) < abs(my_intercept.position[1] - their_goal[1]) or abs(pad.position[0] - my_car.position[0]) > 6000 } # if ball is in a dangerous position, clear it if (ground_distance(my_intercept, my_goal) < 3000 and (abs(my_intercept.position[0]) < 2000 or abs(my_intercept.position[1]) < 4500) and my_car.position[2] < 300): if align(my_car.position, my_intercept.ball, their_goal) > 0.5: return offense.any_shot(info, my_intercept.car, their_goal, my_intercept, allow_dribble=True) return defense.any_clear(info, my_intercept.car) # if I'm low on boost and the ball is not near my goal, go for boost if my_car.boost < 10 and ground_distance(my_intercept, their_goal) > 3000: refuel = Refuel(my_car, info, forbidden_pads=banned_boostpads) if refuel.pad: return refuel ball_in_their_half = abs(my_intercept.position[1] - their_goal[1]) < 3000 shadow_distance = 4000 if ball_in_their_half else 6000 # if they can hit the ball sooner than me and they aren't out of position, wait in defense if (their_intercept.time < my_intercept.time and align(opponent.position, their_intercept.ball, my_goal) > -0.1 + opponent.boost / 100 and ground_distance(opponent, their_intercept) > 300 and dot(opponent.velocity, ground_direction(their_intercept, my_goal)) > 0): return GeneralDefense(my_car, info, my_intercept.position, shadow_distance, force_nearest=ball_in_their_half) # if not completely out of position, go for a shot if (align(my_car.position, my_intercept.ball, their_goal) > -0.5 or ground_distance(my_intercept, their_goal) < 2000 or ground_distance(opponent, their_intercept) < 300): if my_car.position[2] < 300: shot = offense.any_shot(info, my_intercept.car, their_goal, my_intercept, allow_dribble=True) if (not isinstance(shot, Strike) or shot.intercept.time < their_intercept.time or abs(shot.intercept.position[0]) < 3500): return shot if my_car.boost < 30: refuel = Refuel(my_car, info, forbidden_pads=banned_boostpads) if refuel.pad: return refuel # fallback return GeneralDefense(my_car, info, my_intercept.position, shadow_distance, force_nearest=ball_in_their_half)
def set_maneuvers(self, drones: List[Drone]): info = self.info their_goal = ground(info.their_goal.center) our_goal = ground(info.my_goal.center) if self.drone_going_for_ball is not None and self.drone_going_for_ball.maneuver is None: self.drone_going_for_ball = None if self.defending_drone is not None and self.defending_drone.maneuver is None: self.defending_drone = None # recovery for drone in drones: if drone.maneuver is None and not drone.car.on_ground: drone.maneuver = Recovery(drone.car) # decide which drone is gonna commit if self.drone_going_for_ball is None: ready_drones = [ drone for drone in drones if not drone.car.demolished and ( drone.maneuver is None or drone.maneuver.interruptible()) and drone.car.position[2] < 300 ] if not ready_drones: return info.predict_ball() our_intercepts = [ Intercept(drone.car, info.ball_predictions) for drone in ready_drones ] good_intercepts = [ i for i in our_intercepts if align(i.car.position, i.ball, their_goal) > 0.3 and ground_distance(i.car, i) > 2000 ] if good_intercepts: best_intercept = min(good_intercepts, key=lambda intercept: intercept.time) else: best_intercept = min( our_intercepts, key=lambda i: ground_distance(i.car, our_goal)) # find out which drone does the intercept belong to self.drone_going_for_ball = next( drone for drone in ready_drones if drone.car == best_intercept.car) # if not completely out of position, go for a shot if (align(best_intercept.car.position, best_intercept.ball, their_goal) > 0 or ground_distance(best_intercept, our_goal) > 6000): strike = offense.any_shot(info, best_intercept.car, their_goal, best_intercept) else: # otherwise try to clear strike = defense.any_clear(info, best_intercept.car) self.drone_going_for_ball.maneuver = strike if self.drone_going_for_ball is self.defending_drone: self.defending_drone = None # clear expired boost reservations for drone in drones: if not isinstance( drone.maneuver, PickupBoostPad) and drone in self.boost_reservations: del self.boost_reservations[drone] # drones that need boost go for boost for drone in drones: if drone.maneuver is None: if drone.car.boost < 30: self.send_drone_for_boost(drone) # pick one drone that will stay far back unemployed_drones = [ drone for drone in drones if drone.maneuver is None ] if unemployed_drones and self.defending_drone is None: self.defending_drone = min( unemployed_drones, key=lambda d: ground_distance(d.car, info.my_goal.center)) self.defending_drone.maneuver = GeneralDefense( self.defending_drone.car, info, info.ball.position, 7000) unemployed_drones.remove(self.defending_drone) for drone in unemployed_drones: drone.maneuver = GeneralDefense(drone.car, info, info.ball.position, 4000)
def is_opponent_close(info: GameInfo, dist: float) -> bool: for opponent in info.get_opponents(): if ground_distance(opponent.position + opponent.velocity * 0.5, info.ball) < dist: return True return False
def choose_maneuver(self, car: Car): info = self.info offense = self.offense ball = info.ball teammates = info.get_teammates(car) opponents = info.get_opponents(car) their_goal = ground(info.their_goal.center) my_goal = ground(info.my_goal.center) my_hit = Intercept(car, info.ball_predictions) their_best_hit = self.best_intercept(opponents) opponent = their_best_hit.car # recovery if not car.on_ground: return Recovery(car) # kickoff should_go = all( distance(mate, ball) > distance(car, ball) for mate in teammates) if should_go and ball.position[0] == 0 and ball.position[1] == 0: return KickoffStrategy.choose_kickoff(info, car) # don't save our own shots if info.about_to_score: if info.time_of_goal < their_best_hit.time - 2: return Stop(car) # save if info.about_to_be_scored_on: if align(car.position, my_hit.ball, their_goal) > 0.0: return offense.direct_shot(car, their_goal) return self.defense.any_clear(car) # fallback if align(car.position, my_hit.ball, my_goal) > 0.2: if (ground_distance(my_hit, my_goal) < 4000 and abs(car.position[1]) < abs(my_hit.position[1])): return self.defense.any_clear(car) return GeneralDefense(car, info, my_hit.ground_pos, 6000) # clear if (ground_distance(my_hit, my_goal) < 3500 and abs(my_hit.position[0]) < 3000 and ground_distance(car, my_goal) < 2500): if align(car.position, my_hit.ball, their_goal) > 0: return offense.direct_shot(car, their_goal) return self.defense.any_clear(car) if distance(their_best_hit, their_goal) < distance( their_best_hit, my_goal): opponents_align = -align(opponent.position, their_best_hit.ball, their_goal) else: opponents_align = align(opponent.position, their_best_hit.ball, my_goal) # 1v1 if not teammates: # I can get to ball faster than them if my_hit.time < their_best_hit.time - 0.8: strike = offense.any_shot(car, their_goal, my_hit) if not isinstance(strike, Strike): return strike if strike.intercept.time < their_best_hit.time - 0.8 \ and (not info.about_to_score or strike.intercept.time < info.time_of_goal - 1): if strike.intercept.time - car.time > 4 and car.boost < 30 \ and distance(strike.intercept.ground_pos, their_goal) > 3000 and distance(their_best_hit.ground_pos, my_goal) > 5000: return Refuel(car, info, my_hit.ground_pos) if abs(strike.intercept.ground_pos[0] ) > Arena.size[0] - 800 and car.boost < 30: return Refuel(car, info, my_hit.ground_pos) if abs(strike.intercept.ball.position[1] - their_goal[1]) > 300 or ground_distance( strike.intercept, their_goal) < 900: return strike # they are out of position if (opponents_align < -0.1 and my_hit.time < their_best_hit.time - opponents_align * 1.5): strike = offense.any_shot(car, their_goal, my_hit) if not isinstance(strike, Strike) or strike.intercept.is_viable \ and (not info.about_to_score or strike.intercept.time < info.time_of_goal - 0.5): if (car.boost < 40 and (distance(my_hit, their_goal) > 5000 or abs(my_hit.position[0]) > Arena.size[0] - 1500) and distance(opponent, their_best_hit) > 3000): return Refuel(car, info, my_hit.ground_pos) if not isinstance(strike, Strike) or abs( strike.intercept.ball.position[1] - their_goal[1]) > 300 or ground_distance( strike.intercept, their_goal) < 900: return strike if distance(their_best_hit.ball, my_goal) > 7000 and \ (distance(their_best_hit, opponent) > 3000 or align(opponent.position, their_best_hit.ball, my_goal) < 0) and car.boost < 30: return Refuel(car, info, my_hit.ground_pos) if car.boost < 35 and distance(their_best_hit, opponent) > 3000: refuel = Refuel(car, info, my_hit.ground_pos) if estimate_time(car, refuel.pad.position, 1400) < 1.5: return refuel if opponents_align < 0: return offense.any_shot(car, their_goal, my_hit) # teamplay else: if car.boost < 40: return Refuel(car, info, my_goal) else: return offense.any_shot(car, their_goal, my_hit) shadow_distance = 4000 + opponents_align * 1500 shadow_distance = max(shadow_distance, 3000) return GeneralDefense(car, info, their_best_hit.ground_pos, shadow_distance)
def step(self, dt: float): self.drive.step(dt) self.controls = self.drive.controls if ground_distance(self.car, self.drive.target_pos) < 100: self.finished = True
def step(self, dt): time_left = self.aerial.arrival_time - self.car.time if self.aerialing: to_ball = direction(self.car, self.info.ball) # freestyling if self.car.position[2] > 200: # if time_left > 0.7: # rotation = axis_to_rotation(self.car.forward() * 0.5) # self.aerial.up = dot(rotation, self.car.up()) # else: self.aerial.up = vec3(0, 0, -1) + xy(to_ball) self.aerial.target_orientation = look_at(to_ball, vec3(0, 0, -3) + to_ball) self.aerial.step(dt) self.controls = self.aerial.controls self.finished = self.aerial.finished and time_left < -0.3 else: super().step(dt) # simulate aerial from current state simulated_car = self.simulate_flight(self.car, self.aerial, self._flight_path) speed_towards_target = dot( self.car.velocity, ground_direction(self.car, self.aerial.target_position)) speed_needed = ground_distance( self.car, self.aerial.target_position) / time_left # too fast, slow down if speed_towards_target > speed_needed and angle_to( self.car, self.aerial.target_position) < 0.1: self.controls.throttle = -1 # if it ended up near the target, we could take off elif distance( simulated_car, self.aerial.target_position) < self.MAX_DISTANCE_ERROR: if angle_to(self.car, self.aerial.target_position) < 0.1 or norm( self.car.velocity) < 1000: if self.DELAY_TAKEOFF and ground_distance( self.car, self.aerial.target_position) > 1000: # extrapolate current state a small amount of time future_car = Car(self.car) time = 0.5 future_car.time += time displacement = future_car.velocity * time if norm(future_car.velocity) > 500\ else normalize(future_car.velocity) * 500 * time future_car.position += displacement # simulate aerial fot the extrapolated car again future_simulated_car = self.simulate_flight( future_car, self.aerial) # if the aerial is also successful, that means we should continue driving instead of taking off # this makes sure that we go for the most late possible aerials, which are the most effective if distance(future_simulated_car, self.aerial. target_position) > self.MAX_DISTANCE_ERROR: self.aerialing = True else: self.too_early = True else: self.aerialing = True else: # self.controls.boost = True self.controls.throttle = 1
def is_opponent_close(self, car, ball) -> bool: for opponent in self.info.get_opponents(car): if ground_distance(opponent, ball) < ball.position[2] * 2 + 1000: return True return False