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, ball: Ball, target: vec3): super().__init__(car) self.ball = ball self.target = ground(target) self.drive = Drive(car) self._shift_direction = vec3(0, 0, 0)
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, info: GameInfo, target=None): self.drive = Drive(car) self.reorient = Reorient(car) self.jumping = False self.time_for_jump = float("inf") self.timer = 0.0 super().__init__(car, info, target)
def __init__(self, car: Car, info: GameInfo, target=None): self.drive = Drive(car) self.aerial_turn = AerialTurn(car) self.jumping = False self.time_for_jump = float("inf") self.timer = 0.0 super().__init__(car, info, target)
class Carry(Maneuver): """ Carry the ball on roof towards a target. Finishes if the ball hits the floor. """ def __init__(self, car: Car, ball: Ball, target: vec3): super().__init__(car) self.ball = ball self.target = ground(target) self.drive = Drive(car) self._shift_direction = vec3(0, 0, 0) 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 render(self, draw: DrawingTool): draw.color(draw.pink) draw.triangle(self.car.position + self._shift_direction * 50, self._shift_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
def __init__(self, car: Car, info: GameInfo): super().__init__(car) self.info = info self.drive = Drive(car) self.drive.target_speed = 2300 self.dodge = Dodge(car) self.dodge.duration = 0.1 self.dodge.direction = normalize(info.their_goal) self.phase = 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): 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 Carry(Maneuver): def __init__(self, car: Car, ball: Ball, target: vec3): super().__init__(car) self.ball = ball self.target = ground(target) self.drive = Drive(car) self._shift_direction = vec3(0, 0, 0) def step(self, dt): ball = Ball(self.ball) car = self.car while (ball.pos[2] > 120 or ball.vel[2] > 0) and ball.t < car.time + 10: ball.step(1 / 60) ball_local = local(car, ground(ball.pos)) target = local(car, self.target) shift = ground(direction(ball_local, target)) shift[1] *= 1.8 shift = normalize(shift) max_turn = clamp(norm(car.vel) / 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 *= 45 shift[1] *= clamp(norm(car.vel) / 1000, 1, 2) self._shift_direction = normalize(world(car, shift) - car.pos) target = world(car, ball_local - shift) speed = distance(car.pos, target) / max(0.001, ball.t - 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.pos[2] < 100 or ground_distance( self.ball, self.car) > 1500 def render(self, draw: DrawingTool): draw.color(draw.pink) draw.triangle(self.car.pos + self._shift_direction * 50, self._shift_direction)
class DiagonalKickoff(Maneuver): '''Dodge forward once to get there faster.''' def __init__(self, car: Car, info: GameInfo): super().__init__(car) self.info = info self.drive = Drive(car) self.drive.target_speed = 2300 self.dodge = Dodge(car) self.dodge.duration = 0.1 self.dodge.direction = normalize(info.their_goal) self.phase = 0 def step(self, dt): if self.phase == 0: self.controls.throttle = 1 self.controls.boost = 1 if ground_distance(self.car, self.info.ball) < 2950: print("entering phase 1") self.phase = 1 if self.phase == 1: self.dodge.step(dt) self.controls = self.dodge.controls if self.dodge.finished and self.car.on_ground: print("entering phase 2") self.phase = 2 self.dodge = Dodge(self.car) self.dodge.duration = 0.18 self.dodge.direction = direction(self.car.position, self.info.ball.position) if self.phase == 2: self.drive.step(dt) self.controls = self.drive.controls if distance(self.car, self.info.ball) < 850: print("entering phase 3") self.phase = 3 if self.phase == 3: self.dodge.step(dt) self.controls = self.dodge.controls self.finished = self.info.ball.position[0] != 0 and self.dodge.finished def render(self, draw: DrawingTool): pass
def __init__(self, car: Car, target: vec3 = vec3(0, 0, 0), waste_boost=False): super().__init__(car) self.target = Arena.clamp(ground(target), 100) self.waste_boost = waste_boost self.finish_distance = 500 self._time_on_ground = 0 self._driving = True self.dodge_duration = 1.6 self.halflip_duration = 2 self.wavedash_duration = 1.3 # decide whether to start driving backwards and halfflip later forward_est = estimate_time(car, target, estimate_max_car_speed(car)) backwards_est = estimate_time(car, target, 1400, -1) + 0.5 backwards = backwards_est < forward_est \ and (distance(car, target) > 3000 or distance(car, target) < 300) \ and car.pos[2] < 200 self.drive = Drive(car, self.target, 2300, backwards) self.action = self.drive
def __init__(self, car: Car, info: GameInfo): super().__init__(car) self.info: GameInfo = info self.drive = Drive(car, target_speed=2300) self.action: Maneuver = self.drive self.phase = 1
def __init__(self, car: Car, pad: Pad): super().__init__(car) self.drive = Drive(car, target_pos=pad.position, target_speed=2300, backwards=True) self.phase = 1 self.action = self.drive
def __init__(self, car: Car, info: GameInfo): super().__init__(car) self.info = info target_pos = vec3(0, sgn(info.my_goal.center[1]) * 100, 0) self.drive = Drive(car, target_pos, 2300) self.action: Maneuver = self.drive self.phase = 1
class DriveBackwardsToGoal(Maneuver): """ Simply drive backwards until we are in the center of our goal. This is useful if we are the nearest car to our goal on kickoff, in case we lose a kickoff horribly, we are in a good position to save. Note: This isn't an actual kickoff maneuver (it doesn't go for the ball), so it doesn't inherit from Kickoff. """ def __init__(self, car: Car, info: GameInfo): super().__init__(car) self.drive = Drive(car) self.drive.backwards = True self.drive.target_pos = info.my_goal.center self.drive.target_speed = 1300 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): 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 __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, target: vec3 = vec3(0, 0, 0), waste_boost=False): super().__init__(car) self.target = Arena.clamp(ground(target), 100) self.waste_boost = waste_boost self.finish_distance = 500 self._time_on_ground = 0 self.driving = True # decide whether to start driving backwards and halfflip later forward_estimate = estimate_time(car, self.target) backwards_estimate = estimate_time(car, self.target, -1) + 0.5 backwards = ( dot(car.velocity, car.forward()) < 500 and backwards_estimate < forward_estimate and (distance(car, self.target) > 3000 or distance(car, self.target) < 300) and car.position[2] < 200 ) self.drive = Drive(car, self.target, 2300, backwards) self.action = self.drive
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)
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.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 def interruptible(self) -> bool: return self.action.interruptible() 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 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 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)
class DoubleJumpStrike(Strike): def intercept_predicate(self, car, ball): return 250 < ball.position[2] < 550 def __init__(self, car: Car, info: GameInfo, target=None): self.drive = Drive(car) self.reorient = Reorient(car) self.jumping = False self.time_for_jump = float("inf") self.timer = 0.0 super().__init__(car, info, target) def configure(self, intercept: Intercept): super().configure(intercept) self.drive.target_pos = ground(intercept.position) self.time_for_jump = self.double_jump_time_needed( intercept.position[2]) def interruptible(self) -> bool: return not self.jumping and super().interruptible() 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 @staticmethod def double_jump_time_needed(height): """Return the time needed for the double jump to reach a given height""" # polynomial approximation a = 1.872348977E-8 * height * height * height b = -1.126747937E-5 * height * height c = 3.560647225E-3 * height d = -7.446058499E-3 return a + b + c + d
def __init__(self, car: Car, info: GameInfo): super().__init__(car) self.drive = Drive(car) self.drive.backwards = True self.drive.target_pos = info.my_goal.center self.drive.target_speed = 1300