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)
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, 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 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)
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)
def choose_maneuver(self): info = self.info offense = self.offense ball = info.ball car = info.my_car my_score, their_score = self.get_team_scores() their_goal = ground(info.their_goal.center) my_goal = ground(info.my_goal.center) my_hit = Intercept(car, info.ball_predictions) their_best_hit, opponent = self.best_intercept(info.opponents, 500) if my_score > their_score + 2: self.aggresivity = 5 else: self.aggresivity = 0 if their_score > my_score: if self.packet.game_info.game_time_remaining < 60: self.aggresivity = 5 if self.packet.game_info.game_time_remaining < 30: self.aggresivity = 30 should_commit = True if info.teammates: best_team_intercept, _ = self.best_intercept(info.teammates, 500) if best_team_intercept.time < my_hit.time - 0.05: should_commit = False if not car.on_ground: return self.when_airborne() # kickoff if should_commit and ball.pos[0] == 0 and ball.pos[1] == 0: return Kickoff(car, info) # dont 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.pos, my_hit.ball, their_goal) > -0.2: any_shot = offense.any_shot(car, their_goal, my_hit) if (not isinstance(any_shot, Strike) or their_best_hit.time < any_shot.intercept.time + 0.5) \ and align(car.pos, my_hit.ball, their_goal) < 0.6: return DodgeStrike(car, info, their_goal) return any_shot return self.clear_into_corner(my_hit) # fallback if align(car.pos, my_hit.ball, my_goal) > 0.2: if ground_distance(my_hit, my_goal) < 4000 and should_commit and abs( car.pos[1]) < abs(my_hit.pos[1]): return self.clear_into_corner(my_hit) return ShadowDefense(car, info, my_hit.ground_pos, 6000) # clear if (should_commit and ground_distance(my_hit, my_goal) < 3500 and abs(my_hit.pos[0]) < 3000 and ground_distance(car, my_goal) < 2500): if align(car.pos, my_hit.ball, their_goal) > -0.1: any_shot = offense.any_shot(car, their_goal, my_hit) if (not isinstance(any_shot, Strike) or their_best_hit.time < any_shot.intercept.time + 0.5) \ and align(car.pos, my_hit.ball, their_goal) < 0.6: return DodgeStrike(car, info, their_goal) return any_shot return self.clear_into_corner(my_hit) # double tap if should_commit and car.pos[2] > 1000: double_tap = offense.double_tap(car, their_goal) if double_tap is not None: return double_tap # 1v1 if not info.teammates: if distance(their_best_hit.ground_pos, their_goal) < distance( their_best_hit.ground_pos, my_goal): opponents_align = -align(opponent.pos, their_best_hit.ball, their_goal) else: opponents_align = align(opponent.pos, their_best_hit.ball, my_goal) # I can get to ball faster than them if should_commit and 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.pos[1] - their_goal[1]) > 300 or ground_distance( strike.intercept, their_goal) < 900: return strike # they are out of position if (should_commit and opponents_align < -0.1 - self.aggresivity / 20 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.pos[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.pos[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.pos, 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.pos, 1400) < 1.5: return refuel # teamplay else: if should_commit: return offense.any_shot(car, their_goal, my_hit) if car.boost < 50: return Refuel(car, info, my_goal) shadow_distance = 5500 shadow_distance -= self.aggresivity * 500 shadow_distance = max(shadow_distance, 3000) return ShadowDefense(car, info, their_best_hit.ground_pos, shadow_distance)
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)