class Refuel(Maneuver): def __init__(self, car: Car, info: GameInfo, target: vec3): super().__init__(car) self.info = info pos = (target + car.pos + info.my_goal.center * 2) / 4 self.pad = self.nearest_boostpad(car, info, pos) self.travel = Travel(car, self.pad.pos, waste_boost=True) @staticmethod def nearest_boostpad(car: Car, info: GameInfo, pos: vec3): best_pad = None best_dist = 9999 for pad in info.boost_pads: dist = distance(pos, pad.pos) if (pad.is_active or pad.timer < estimate_time( car, pad.pos, estimate_max_car_speed(car)) ) and dist < best_dist: best_pad = pad best_dist = dist return best_pad def step(self, dt): if norm(self.car.vel) > 1400: if distance(self.car, self.pad) < norm(self.car.vel) * 0.3: self.travel.action.target_speed = 1000 self.travel.step(dt) self.controls = self.travel.controls self.finished = (not self.pad.is_active \ and not self.pad.timer < estimate_time(self.car, self.pad.pos, estimate_max_car_speed(self.car))) \ or self.car.boost > 99 def render(self, draw: DrawingTool): self.travel.render(draw)
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 furthest_point( face_target, points) self.target = Arena.clamp(target_pos, 500) self.travel = Travel(car, self.target) self.travel.finish_distance = 800 if near_goal else 1500 self.drive = Drive(car) self.start_time = car.time self.wait = Stop(car)
def __init__(self, car: Car, info: GameInfo, target: vec3): super().__init__(car) self.info = info pos = (target + car.pos + info.my_goal.center * 2) / 4 self.pad = self.nearest_boostpad(car, info, pos) self.travel = Travel(car, self.pad.pos, waste_boost=True)
class Arrive(Maneuver): def __init__(self, car: Car, target=vec3(0, 0, 0), time=0, target_direction: vec3 = None): super().__init__(car) self.target_direction = target_direction self.target = target self.time = time self.drive = Drive(car, target) self.travel = Travel(car) self.lerp_t = 0.6 def step(self, dt): target = self.target car = self.car if self.target_direction is not None: car_vel = norm(car.vel) target_direction = normalize(self.target_direction) shift = clamp( distance(car.pos, target) * self.lerp_t, 0, car_vel * 1.5) if shift < turn_radius(clamp(car_vel, 1400, 2000) * 1.1): shift = 0 translated_target = target - target_direction * shift translated_time = self.time - distance( translated_target, target) / max(1, clamp(car_vel, 500, 2300)) else: translated_target = target translated_time = self.time self.drive.target_pos = translated_target dist_to_target = distance(car.pos, translated_target) target_speed = clamp( dist_to_target / max(0.001, translated_time - car.time), 0, 2300) self.drive.target_speed = target_speed if car.boost < 1 and dist_to_target > 3000: self.travel.target = target self.travel.step(dt) self.controls = self.travel.controls else: self.drive.step(dt) self.controls = self.drive.controls self.finished = self.car.time >= self.time return self.finished def render(self, draw: DrawingTool): self.drive.render(draw) if self.target_direction is not None: draw.color(draw.lime) draw.triangle(self.target - self.target_direction * 250, self.target_direction)
def __init__(self, car: Car): super().__init__(car) self.target_direction: vec3 = None self.target: vec3 = None self.time: float = 0 self.drive = Drive(car) self.travel = Travel(car) self.lerp_t = 0.6 self.allow_dodges_and_wavedashes: bool = True self.additional_shift = 0
class ShadowDefense(Maneuver): DURATION = 0.5 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 interruptible(self) -> bool: return self.travel.interruptible() 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 render(self, draw: DrawingTool): self.travel.render(draw)
class ShadowDefense(Maneuver): def __init__(self, car: Car, info: GameInfo, target: vec3, distance_from_target: float): super().__init__(car) self.info = info self.target = target ball = info.ball dist = min(distance_from_target, ground_distance(target, self.info.my_goal.center) - 50) target_pos = ground(target) + ground_direction(target, self.info.my_goal.center) * dist side_shift = distance_from_target / 4 if ground_distance(car, info.my_goal.center) > 2500 else 400 points = [target_pos + vec3(side_shift, 0, 0), target_pos - vec3(side_shift, 0, 0)] target_pos = nearest_point(car.pos, points) self.target = Arena.clamp(target_pos, 700) self.travel = Travel(car, self.target) self.drive = Drive(car) self.start_time = car.time self.wait = Stop(car) def step(self, dt): ball = self.info.ball if ( distance(self.car, ball) < 1000 and align(self.car.pos, ball, self.info.my_goal.center) > 0.2 ): shift = normalize(cross(direction(ball, self.car), vec3(0, 0, 1))) * 1000 self.travel.target = nearest_point(self.car.pos, [ball.pos + shift, ball.pos - shift]) else: self.travel.target = self.target self.travel.step(dt) self.controls = self.travel.controls if self.travel.finished: if angle_to(self.car, self.target) > 0.2 and norm(self.car.vel) < 600: self.drive.target_pos = self.target self.drive.step(dt) self.drive.target_speed = 500 self.drive.controls.handbrake = False self.controls = self.drive.controls else: self.wait.step(dt) self.controls = self.wait.controls self.finished = self.travel._driving and self.car.time > self.start_time + 0.5 def render(self, draw: DrawingTool): self.travel.render(draw)
def __init__(self, car: Car, target=vec3(0, 0, 0), time=0, target_direction: vec3 = None): super().__init__(car) self.target_direction = target_direction self.target = target self.time = time self.drive = Drive(car, target) self.travel = Travel(car) self.lerp_t = 0.6
def __init__(self, car: Car, info: GameInfo, target: vec3, forbidden_pads: Set[Pad] = set()): super().__init__(car) self.info = info pos = (target + car.position * 2 + info.my_goal.center * 2) / 5 pads = set(info.large_boost_pads) - forbidden_pads self.pad = self.best_boostpad_to_pickup(car, pads, pos) self.pad_was_active = self.pad and self.pad.is_active self.travel = Travel( car, self.pad.position if self.pad else info.my_goal.center, waste_boost=True)
def __init__(self, car: Car, info: GameInfo, target: vec3, distance_from_target: float): super().__init__(car) self.info = info self.target = target ball = info.ball dist = min(distance_from_target, ground_distance(target, self.info.my_goal.center) - 50) target_pos = ground(target) + ground_direction(target, self.info.my_goal.center) * dist side_shift = distance_from_target / 4 if ground_distance(car, info.my_goal.center) > 2500 else 400 points = [target_pos + vec3(side_shift, 0, 0), target_pos - vec3(side_shift, 0, 0)] target_pos = nearest_point(car.pos, points) self.target = Arena.clamp(target_pos, 700) self.travel = Travel(car, self.target) self.drive = Drive(car) self.start_time = car.time self.wait = Stop(car)
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 __init__(self, car: Car): super().__init__(car) self.drive = Drive(car) self.travel = Travel(car) self.action = self.drive self.target_direction: Optional[None] = None self.target: vec3 = None self.arrival_time: float = 0 self.backwards: bool = False self.lerp_t = 0.57 self.allow_dodges_and_wavedashes: bool = True self.additional_shift = 0
class PickupBoostPad(Maneuver): """Pickup a boostpad. Abort when picked up by someone else.""" def __init__(self, car: Car, pad: BoostPad): super().__init__(car) self.pad = pad self.pad_was_active = self.pad.state == BoostPadState.Available self.travel = Travel(car, self.pad.position, waste_boost=True) def interruptible(self) -> bool: return self.travel.interruptible() def step(self, dt): # slow down when we're about to pick up the boost, so we can turn faster afterwards if distance(self.car, self.pad) < norm(self.car.velocity) * 0.2: self.travel.drive.target_speed = 1400 self.travel.step(dt) self.controls = self.travel.controls # finish when someone picks up the pad if self.pad_was_active and self.pad.state == BoostPadState.Unavailable: self.finished = True self.pad_was_active = self.pad.state == BoostPadState.Available # finish when we picked the boost up but the previous condition somehow wasn't true if self.car.boost > 99 or distance(self.car, self.pad) < 100: self.finished = True def render(self, draw: DrawingTool): self.travel.render(draw) # draw timer if boost isn't available yet if self.pad and not self.pad.state == BoostPadState.Available: draw.color(draw.yellow) draw.string(self.pad.position + vec3(0, 0, 100), f"spawns in: {self.pad.timer:.1f}s")
class Refuel(Maneuver): """ Choose a large boost pad and go pick it up. """ def __init__(self, car: Car, info: GameInfo, target: vec3, forbidden_pads: Set[Pad] = set()): super().__init__(car) self.info = info pos = (target + car.position * 2 + info.my_goal.center * 2) / 5 # TODO: make this better pads = set(info.large_boost_pads) - forbidden_pads self.pad = self.best_boostpad_to_pickup(car, pads, pos) self.pad_was_active = self.pad and self.pad.is_active self.travel = Travel( car, self.pad.position if self.pad else info.my_goal.center, waste_boost=True) @staticmethod def best_boostpad_to_pickup(car: Car, pads: Set[Pad], pos: vec3) -> Optional[Pad]: best_pad = None best_dist = math.inf for pad in pads: dist = distance(pos, pad.position) time_estimate = estimate_time(car, pad.position, estimate_max_car_speed(car)) if dist < best_dist and (pad.is_active or pad.timer < time_estimate): best_pad = pad best_dist = dist return best_pad def interruptible(self) -> bool: return self.travel.interruptible() def step(self, dt): if self.pad is None: self.finished = True return # slow down when we're about to pick up the boost, so we can turn faster afterwards if distance(self.car, self.pad) < norm(self.car.velocity) * 0.2: self.travel.drive.target_speed = 1400 self.travel.step(dt) self.controls = self.travel.controls # finish when someone picks up the pad if not self.pad.is_active and self.pad_was_active: self.finished = True self.pad_was_active = self.pad.is_active # finish when we picked the boost up but the previous condition somehow wasn't true if self.car.boost > 99 or distance(self.car, self.pad) < 100: self.finished = True def render(self, draw: DrawingTool): self.travel.render(draw) if self.pad and not self.pad.is_active: draw.color(draw.yellow) draw.string(self.pad.position + vec3(0, 0, 100), int(self.pad.timer * 100) / 100)
class Arrive(Maneuver): ''' Arrive at a target position at a certain time (game seconds). You can also specify `target_direction`, and it will try to arrive at an angle. However this does work well only if the car is already roughly facing the specified direction, and only if it's far enough. ''' def __init__(self, car: Car): super().__init__(car) self.target_direction: vec3 = None self.target: vec3 = None self.time: float = 0 self.drive = Drive(car) self.travel = Travel(car) self.lerp_t = 0.6 self.allow_dodges_and_wavedashes: bool = True self.additional_shift = 0 def step(self, dt): target = self.target car = self.car if self.target_direction is not None: car_vel = norm(car.velocity) target_direction = normalize(self.target_direction) shift = clamp( distance(car.position, target) * self.lerp_t, 0, car_vel * 1.5) if shift - self.additional_shift < turn_radius( clamp(car_vel, 1400, 2000) * 1.1): shift = 0 else: shift += self.additional_shift translated_target = target - target_direction * shift translated_time = self.time - distance( translated_target, target) * 0.7 / max( 1, clamp(car_vel, 500, 2300)) else: translated_target = target translated_time = self.time self.drive.target_pos = translated_target dist_to_target = distance(car.position, translated_target) target_speed = clamp( dist_to_target / max(0.001, translated_time - car.time), 0, 2300) self.drive.target_speed = target_speed if (self.allow_dodges_and_wavedashes and car.boost < 5 and dist_to_target > clamp(norm(car.velocity) + 600, 1400, 2300) and norm(car.velocity) < target_speed - 600 or not self.travel.driving): self.travel.target = target self.travel.step(dt) self.controls = self.travel.controls else: self.drive.step(dt) self.controls = self.drive.controls self.finished = self.car.time >= self.time return self.finished def render(self, draw: DrawingTool): self.drive.render(draw) if self.target_direction is not None: draw.color(draw.lime) draw.triangle(self.target - self.target_direction * 250, self.target_direction)
class GeneralDefense(Maneuver): """ First, attempt to rotate on the far side, and when far away enough from the target (usually the ball), turn around to face it. If already far enough and facing the target, just stop and wait. Also try to pickup boost pads along the way. This state expires after a short amount of time, so we can look if there's something better to do. If not, it can be simply instantiated again. """ DURATION = 0.5 BOOST_LOOK_RADIUS = 1200 BOOST_LOOK_ANGLE = 0.5 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 interruptible(self) -> bool: return self.travel.interruptible() 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 self.controls.boost = False self.finished = self.travel.driving and self.car.time > self.start_time + self.DURATION def render(self, draw: DrawingTool): self.travel.render(draw) # render target pad if self.pad: draw.color(draw.blue) draw.circle(self.pad.position, 50)
def __init__(self, car: Car, pad: BoostPad): super().__init__(car) self.pad = pad self.pad_was_active = self.pad.state == BoostPadState.Available self.travel = Travel(car, self.pad.position, waste_boost=True)
class ShadowDefense(Maneuver): 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 furthest_point( face_target, points) self.target = Arena.clamp(target_pos, 500) self.travel = Travel(car, self.target) self.travel.finish_distance = 800 if near_goal else 1500 self.drive = Drive(car) self.start_time = car.time self.wait = Stop(car) def step(self, dt): ball = self.info.ball # if ( # distance(self.car, ball) < 1000 # and align(self.car.position, ball, self.info.my_goal.center) > 0.2 # ): # shift = normalize(cross(direction(ball, self.car), vec3(0, 0, 1))) * 1000 # self.travel.target = nearest_point(self.car.position, [ball.position + shift, ball.position - shift]) # else: # self.travel.target = self.target 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.drive.target_speed = 700 self.drive.controls.handbrake = False self.controls = self.drive.controls else: self.wait.step(dt) self.controls = self.wait.controls self.finished = self.travel.driving and self.car.time > self.start_time + 0.5 def render(self, draw: DrawingTool): self.travel.render(draw)