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 angle_to(car, target, forward_speed < 0) < 0.1): # 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.05 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 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 step(self, dt): if self.aerialing: self.aerial.target_orientation = look_at( direction(self.car, self.target), vec3(0, 0, -1)) self.aerial.step(dt) self.controls = self.aerial.controls self.finished = self.aerial.finished else: super().step(dt) # simulate aerial from current state simulated_car = self.simulate_flight(self.car) # if the car ended up too far, we're too fast and we need to slow down if ground_distance(self.car, self.aerial.target) + 100 < ground_distance( self.car, simulated_car): # self.controls.throttle = -1 pass # if it ended up near the target, we could take off elif distance(simulated_car, self.aerial.target) < self.MAX_DISTANCE_ERROR: if angle_to(self.car, self.aerial.target) < 0.1 or norm( self.car.velocity) < 1000: if self.DELAY_TAKEOFF: # extrapolate current state a small amount of time future_car = Car(self.car) time = 0.2 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, write_to_flight_path=False) # 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) > 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 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 < 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 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) < 80 and dot(car.forward(), dir_to_target) > 0.7 and norm(car.velocity) > distance(car, self.target) / 3 and norm(car.velocity) > 1300 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(car): if ( distance(opponent.position + opponent.velocity, car) < max(300.0, norm(opponent.velocity)) 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.step(dt) self.controls = self.flick.controls self.finished = self.flick.finished
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 2000 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.drive.target_speed = 1000 self.stop = Stop(car) self.start_time = car.time
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): is_opponent_close = False for opponent in self.info.get_opponents(car): if ground_distance(opponent, car) < ball.position[2] * 2 + 1000: is_opponent_close = True break if not is_opponent_close: return Dribble(car, self.info, target) if align(car.position, ball, target) < 0.1 and abs(ball.position[1] - target[1]) > 3000: return MirrorShot(car, self.info, target) return self.direct_shot(car, target)
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 = KickoffStrategy.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 for drone in drones: if drone is not nearest_drone and drone not in corner_drones: reserved_pads = {self.boost_reservations[d] for d in self.boost_reservations} drone.maneuver = Refuel(drone.car, self.info, self.info.my_goal.center, forbidden_pads=reserved_pads) self.boost_reservations[drone] = drone.maneuver.pad
def step(self, dt): self.travel.step(dt) self.controls = self.travel.controls if ground_distance(self.car, self.travel.target) < 3000: self.controls.boost = False if self.travel.finished: if angle_to(self.car, self.face_target) > 0.3: self.drive.target_pos = self.face_target self.drive.step(dt) self.controls = self.drive.controls self.controls.handbrake = False else: self.stop.step(dt) self.controls = self.stop.controls self.controls.boost = False self.finished = self.travel.driving and self.car.time > self.start_time + self.DURATION
def direct_shot(self, car: Car, target: vec3) -> Maneuver: dodge_shot = DodgeShot(car, self.info, target) ground_shot = GroundShot(car, self.info, target) if car.boost > 40: aerial_strike = AerialStrike(car, self.info, target) fast_aerial = FastAerialStrike(car, self.info, target) if min(aerial_strike.intercept.time, fast_aerial.intercept.time) < dodge_shot.intercept.time: return min([aerial_strike, fast_aerial], key=lambda strike: strike.intercept.time) 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 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, 30, 50) 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 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())] 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.0] 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.3 or ground_distance(best_intercept, our_goal) > 6000 ): strike = self.offense.any_shot(best_intercept.car, their_goal, best_intercept) else: # otherwise try to clear strike = ClearIntoCorner(best_intercept.car, info) self.drone_going_for_ball.maneuver = strike # clear expired boost reservations for drone in drones: if not isinstance(drone.maneuver, Refuel) 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 < 40: reserved_pads = {self.boost_reservations[drone] for drone in self.boost_reservations} drone.maneuver = Refuel(drone.car, info, info.ball.position, forbidden_pads=reserved_pads) self.boost_reservations[drone] = drone.maneuver.pad # reserve chosen boost pad # pick one drone that will stay far back unemployed_drones = [drone for drone in drones if drone.maneuver is None] if unemployed_drones: self.defending_drone = min(unemployed_drones, key=lambda d: ground_distance(d.car, info.my_goal.center)) for drone in unemployed_drones: shadow_distance = 7000 if drone is self.defending_drone else 3000 drone.maneuver = ShadowDefense(self.defending_drone.car, info, info.ball.position, shadow_distance)
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: target = Arena.clamp(target, 200) target[0] = abs_clamp(target[0], 700) if not self.drive_on_walls: if self.car.position[2] > 100: 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(2.5 * phi) # powersliding if abs(phi) > 1.5 and self.car.position[2] < 200 and ground_distance( self.car, target) < 2500: self.controls.handbrake = 1 else: self.controls.handbrake = 0 # 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 else: 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 and vf > 600: self.controls.boost = 0 # finish when close if distance(self.car, self.target_pos) < 100: self.finished = True
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 ClearIntoCorner(car, info) # 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 ClearIntoCorner(car, info) return ShadowDefense(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 ClearIntoCorner(car, info) 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 ShadowDefense(car, info, their_best_hit.ground_pos, shadow_distance)