def is_viable(self, agent): T = self.intercept_time - agent.time xf = agent.me.location + agent.me.velocity * T + 0.5 * agent.gravity * T * T vf = agent.me.velocity + agent.gravity * T if not agent.me.airborne: vf += agent.me.up * (2 * jump_speed + jump_acc * jump_max_duration) xf += agent.me.up * ( jump_speed * (2 * T - jump_max_duration) + jump_acc * (T * jump_max_duration - 0.5 * jump_max_duration**2)) delta_x = self.target - xf f = delta_x.normalize() phi = f.angle3D(agent.me.forward) turn_time = 0.7 * (2 * math.sqrt(phi / 9)) tau1 = turn_time * cap(1 - 0.3 / phi, 0, 1) required_acc = (2 * delta_x.norm()) / ((T - tau1) * (T - tau1)) ratio = required_acc / agent.boost_accel tau2 = T - (T - tau1) * math.sqrt(1 - cap(ratio, 0, 1)) velocity_estimate = vf + agent.boost_accel * (tau2 - tau1) * f boost_estimate = (tau2 - tau1) * 30 enough_boost = boost_estimate < 0.95 * agent.me.boost enough_time = abs(ratio) < 0.9 enough_speed = velocity_estimate.normalize(True)[1] < 0.9 * max_speed return enough_speed and enough_boost and enough_time
def run(self, agent): agent.shooting = True if self.start_time is None: self.start_time = agent.time car_to_ball, distance = (agent.ball.location - agent.me.location).normalize(True) ball_to_target = (self.target - agent.ball.location).normalize() relative_velocity = car_to_ball.dot(agent.me.velocity - agent.ball.velocity) if relative_velocity != 0: eta = cap(distance / cap(relative_velocity, 400, 2300), 0, 1.5) else: eta = 1.5 # If we are approaching the ball from the wrong side the car will try to only hit the very edge of the ball left_vector = car_to_ball.cross(Vector(z=1)) right_vector = car_to_ball.cross(Vector(z=-1)) target_vector = -ball_to_target.clamp(left_vector, right_vector) final_target = agent.ball.location + (target_vector * (distance / 2)) # Some adjustment to the final target to ensure we don't try to dirve through any goalposts to reach it if abs(agent.me.location.y) > 5150: final_target.x = cap(final_target.x, -750, 750) agent.line(final_target - Vector(z=100), final_target + Vector(z=100), [255, 255, 255]) angles = defaultPD(agent, agent.me.local(final_target - agent.me.location)) defaultThrottle( agent, 2300 if distance > 1600 else 2300 - cap(1600 * abs(angles[1]), 0, 2050)) agent.controller.boost = False if agent.me.airborne or abs( angles[1]) > 0.3 else agent.controller.boost agent.controller.handbrake = True if abs( angles[1]) > 2.3 else agent.controller.handbrake if abs(angles[1]) < 0.05 and (eta < 0.45 or distance < 150): agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 agent.push(flip(agent.me.local(car_to_ball))) elif agent.time - self.start_time > 3: agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1
def defend(self): if not self.me.airborne: if self.shooting and not self.predictions[ 'own_goal'] and self.ball.location.y * side( self.team) < self.defense_switch[self.playstyle]: self.clear() if self.is_clear(): ball = self.ball_prediction_struct.slices[cap( round(self.predictions['enemy_time_to_ball'] * 0.95) * 60, 0, len(self.ball_prediction_struct.slices) - 1)].physics.location ball = Vector(ball.x, ball.y, ball.z) if self.predictions['self_from_goal'] > 2560: self.backcheck() if self.me.boost < 72 and ball.y * side(self.team) < -1280: self.goto_nearest_boost( only_small=ball.y * side(self.team) > -2560) elif self.predictions['self_from_goal'] > 750: self.backcheck() else: face_target_routine = face_target(ball=True) ball_f = face_target_routine.get_ball_target(self) if ball_f.y * side(self.team) > -3840 and abs( Vector(x=1).angle2D(self.me.local_location(ball_f)) ) >= 1 and self.me.velocity.magnitude() < 100: self.push(face_target_routine) return
def run(self, agent): ball_slice = agent.ball_prediction_struct.slices[ agent.future_ball_location_slice].physics.location ball = Vector(ball_slice.x, cap(ball_slice.y, -5100, 5100)) agent.line(ball, ball + Vector(z=185), agent.renderer.white()) ball.y *= side(agent.team) if ball.y < agent.ball.location.y * side(agent.team): ball = Vector(agent.ball.location.x, agent.ball.location.y * side(agent.team) + 640) target = self.get_target(agent) agent.line(target, target + Vector(z=642), (255, 0, 255)) if target.flat_dist(agent.me.location) < 350: if agent.me.velocity.magnitude() > 100: self.brake.run(agent, manual=True) elif abs(Vector(x=1).angle2D(agent.me.local_location(ball))) > 0.5: agent.pop() agent.push(face_target(ball=True)) else: agent.pop() else: self.goto.target = target self.goto.run(agent, manual=True)
def run(self, agent): if self.start_time is None: self.start_time = agent.time ball_loc = agent.ball.location.y * side(agent.team) if agent.time - self.start_time > 0.5 or agent.playstyle is agent.playstyles.Defensive or ball_loc > 2560: agent.pop() return target = Vector(y=(ball_loc + 1280) * side(agent.team)) if agent.ball.location.x > 2560: target.x = 2560 if ball_loc <= 0 else 1024 elif agent.ball.location.x < -2560: target.x = -2560 if ball_loc <= 0 else -1024 self_to_target = agent.me.location.dist(target) if self_to_target > 250: self.goto.target = target self.goto.vector = agent.ball.location self.goto.run(agent, manual=True) if self_to_target < 500: agent.controller.boost = False agent.controller.throttle = cap(agent.controller.throttle, -0.75, 0.75)
def run(self, agent): target = agent.ball.location car_to_target = target - agent.me.location distance_remaining = car_to_target.flatten().magnitude() # Some adjustment to the final target to ensure it's inside the field and we don't try to drive through any goalposts to reach it if abs(agent.me.location.y) > 5150: target.x = cap(target.x, -750, 750) local_target = agent.me.local(target - agent.me.location) angles = defaultPD(agent, local_target) defaultThrottle(agent, 1400) agent.controller.boost = False agent.controller.handbrake = True if abs( angles[1]) > 2.3 else agent.controller.handbrake velocity = 1 + agent.me.velocity.magnitude() if distance_remaining < self.exit_distance: agent.pop() if self.exit_flip: agent.push(flip(local_target)) elif abs( angles[1] ) < 0.05 and velocity > 600 and velocity < 2150 and distance_remaining / velocity > 2.0: agent.push(flip(local_target)) elif abs(angles[1]) > 2.8 and velocity < 200: agent.push(flip(local_target, True)) elif agent.me.airborne: agent.push(recovery(target))
def run(self, agent): target = agent.ball.location car_to_target = target - agent.me.location distance_remaining = car_to_target.flatten().magnitude() angle_to_target = abs( Vector(x=1).angle2D(agent.me.local_location(target))) direction = 1 if angle_to_target < 2.2 else -1 local_target = agent.me.local_location(target) # Some adjustment to the final target to ensure it's inside the field and we don't try to drive through any goalposts to reach it if abs(agent.me.location.y) > 5120 - (agent.me.hitbox.width / 2): local_target.x = cap(local_target.x, -750, 750) angles = defaultPD(agent, local_target) defaultThrottle(agent, 1400) agent.controller.handbrake = angle_to_target > 1.54 velocity = agent.me.local_velocity().x if distance_remaining < self.exit_distance: agent.pop() if self.exit_flip: agent.push(flip(local_target)) elif angle_to_target < 0.05 and velocity > 600 and velocity < 1400 and distance_remaining / velocity > 3: agent.push(flip(local_target)) elif angle_to_target >= 2 and distance_remaining > 1000 and velocity < 200 and distance_remaining / abs( velocity) > 2: agent.push(flip(local_target, True)) elif agent.me.airborne: agent.push(recovery(local_target))
def get_slices(agent, cap_): # Get the struct struct = agent.ball_prediction_struct # Make sure it isn't empty if struct is None: return start_slice = 6 end_slices = None # If we're shooting, crop the struct if agent.shooting and agent.stack[0].__class__.__name__ != "short_shot": # Get the time remaining time_remaining = agent.stack[0].intercept_time - agent.time if time_remaining < 0.5 and time_remaining >= 0: return # if the shot is done but it's working on it's 'follow through', then ignore this stuff if time_remaining > 0: # Convert the time remaining into number of slices, and take off the minimum gain accepted from the time min_gain = 0.05 end_slice = round(min(time_remaining - min_gain, cap_) * 60) if end_slices is None: # Cap the slices end_slice = round(cap_ * 60) # We can't end a slice index that's lower than the start index if end_slice <= start_slice: return # for every second worth of slices that we have to search, skip 1 more slice (for performance reasons) - min 1 and max 3 skip = cap(end_slice - start_slice / 60, 1, 3) return struct.slices[start_slice:end_slice:skip]
def get_intercept(self, agent): struct = agent.predictions['ball_struct'] intercepts = [] i = 30 # Begin by looking 0.3 seconds into the future while i < struct.num_slices: intercept_time = struct.slices[i].game_seconds time_remaining = intercept_time - agent.time ball_location = Vector(struct.slices[i].physics.location.x, struct.slices[i].physics.location.y, struct.slices[i].physics.location.z) if abs(ball_location.y) > 5212: break last_ball_location = Vector(struct.slices[i-2].physics.location.x, struct.slices[i-2].physics.location.y, struct.slices[i-2].physics.location.z) ball_velocity = Vector(struct.slices[i].physics.velocity.x, struct.slices[i].physics.velocity.y, struct.slices[i].physics.velocity.z).magnitude() i += 15 - cap(int(ball_velocity//150), 0, 13) if time_remaining < 0 or ball_location.z > 120 or ball_location.flat_dist(agent.friend_goal.location) > last_ball_location.flat_dist(agent.friend_goal.location): continue car_to_ball = ball_location - agent.me.location direction, distance = car_to_ball.normalize(True) forward_angle = direction.angle(agent.me.forward) backward_angle = math.pi - forward_angle forward_time = time_remaining - (forward_angle * 0.318) backward_time = time_remaining - (backward_angle * 0.418) forward_flag = forward_time > 0 and (distance / forward_time) < 1800 backward_flag = distance < 1500 and backward_time > 0 and (distance / backward_time) < 1200 if forward_flag or backward_flag: intercepts.append(( ball_location, intercept_time, 1 if forward_flag else -1 )) if len(intercepts) == 0: return None, None, None intercepts = list(filter(lambda i: i[1] - agent.time < 3, intercepts)) intercepts.sort(key=lambda i: agent.me.location.dist(i[0])) final = (None, None, None) last_speed = math.inf for intercept in intercepts: speed = agent.me.location.dist(intercept[0]) / ((intercept[1] / agent.time) * (3/5)) if abs(speed - 1100) < abs(last_speed - 1100): final = intercept last_speed = speed return final
def run(agent, manual=False): # current forward velocity speed = agent.me.local_velocity().x if abs(speed) > 10: # apply our throttle in the opposite direction # Once we're below a velocity of 50uu's, start to ease the throttle agent.controller.throttle = cap(speed / -50, -1, 1) elif not manual: agent.pop()
def find_aerial(agent, target, cap_=4): struct = agent.predictions['ball_struct'] if struct is None: return max_aerial_height = math.inf if len(agent.friends) == 0 and len(agent.foes) == 1: max_aerial_height = 643 i = 10 # Begin by looking 0.2 seconds into the future while i < struct.num_slices: intercept_time = struct.slices[i].game_seconds time_remaining = intercept_time - agent.time if time_remaining <= 0: return ball_location = Vector(struct.slices[i].physics.location.x, struct.slices[i].physics.location.y, struct.slices[i].physics.location.z) if abs(ball_location.y) > 5212: break ball_velocity = Vector( struct.slices[i].physics.velocity.x, struct.slices[i].physics.velocity.y, struct.slices[i].physics.velocity.z).magnitude() i += 15 - cap(int(ball_velocity // 150), 0, 13) # we don't actually need to calculate if we can get there; it is_viable function will do that for us # we just need a few variables to find the best shot vector car_to_ball = ball_location - agent.me.location direction = car_to_ball.normalize() left, right, swapped = post_correction(ball_location, target[0], target[1]) if not swapped: left_vector = (left - ball_location).normalize() right_vector = (right - ball_location).normalize() best_shot_vector = direction.clamp(left_vector, right_vector) # relax the in_field requirement # reduce cap_ from 6 to 4 (by default) if in_field(ball_location - (100 * best_shot_vector), 1) and is_fast_shot(agent.me.location, ball_location, intercept_time, agent.time, cap_): slope = find_slope(best_shot_vector, car_to_ball) ball_intercept = ball_location - 92 * best_shot_vector if slope > 0.5 and 275 < ball_intercept.z and ball_intercept.z < max_aerial_height: aerial = Aerial(ball_intercept, intercept_time) if aerial.is_viable(agent): return aerial
def find_any_jump_shot(agent, cap_=3): struct = agent.predictions['ball_struct'] if struct is None: return i = 5 while i < struct.num_slices: intercept_time = struct.slices[i].game_seconds time_remaining = intercept_time - agent.time if time_remaining <= 0: return ball_location = Vector(struct.slices[i].physics.location.x, struct.slices[i].physics.location.y, struct.slices[i].physics.location.z) if abs(ball_location.y) > 5212: break ball_velocity = Vector( struct.slices[i].physics.velocity.x, struct.slices[i].physics.velocity.y, struct.slices[i].physics.velocity.z).magnitude() i += 15 - cap(int(ball_velocity // 150), 0, 13) if ball_location.z > 350: continue car_to_ball = ball_location - agent.me.location direction, distance = car_to_ball.normalize(True) forward_angle = direction.angle(agent.me.forward) backward_angle = math.pi - forward_angle forward_time = time_remaining - (forward_angle * 0.318) backward_time = time_remaining - (backward_angle * 0.418) forward_flag = forward_time > 0 and ( distance * 1.05 / forward_time) < (2275 if agent.me.boost > distance / 100 else 1400) backward_flag = distance < 1500 and backward_time > 0 and ( distance * 1.05 / backward_time) < 1200 # our current direction IS our best shot vector, as we just want to get to the ball as fast as possible if (forward_flag or backward_flag) and is_fast_shot( agent.me.location, ball_location, intercept_time, agent.time, cap_): slope = find_slope(direction, car_to_ball) if forward_flag and ball_location.z <= 275 and slope > 0: return jump_shot(ball_location, intercept_time, direction) elif backward_flag and ball_location.z <= 250 and slope > 1.5: return jump_shot(ball_location, intercept_time, direction, -1)
def run(self, agent): if not agent.shooting: agent.shooting = True if self.start_time is None: self.start_time = agent.time car_to_ball, distance = (agent.ball.location - agent.me.location).normalize(True) ball_to_target = (self.target - agent.ball.location).normalize() angle_to_target = abs(Vector(x=1).angle2D(agent.me.local(car_to_ball))) relative_velocity = car_to_ball.dot(agent.me.velocity - agent.ball.velocity) eta = cap(distance / cap(relative_velocity, 400, 1400), 0, 1.5) if relative_velocity != 0 else 1.5 # If we are approaching the ball from the wrong side the car will try to only hit the very edge of the ball left_vector = car_to_ball.cross(Vector(z=1)) right_vector = car_to_ball.cross(Vector(z=-1)) target_vector = -ball_to_target.clamp2D(left_vector, right_vector) final_target = agent.ball.location + (target_vector * (distance / 2)) # Some adjustment to the final target to ensure we don't try to drive through any goalposts to reach it if abs(agent.me.location.y) > 5130: final_target.x = cap(final_target.x, -750, 750) agent.line(final_target - Vector(z=100), final_target + Vector(z=100), (255, 255, 255)) angles = defaultPD(agent, agent.me.local_location(final_target)) defaultThrottle(agent, 1400) agent.controller.handbrake = angle_to_target > 1.54 if abs(angles[1]) < 0.05 and (eta < 0.45 or distance < 150): agent.pop() agent.shooting = False agent.push(flip(agent.me.local(car_to_ball))) elif agent.time - self.start_time > 0.24: # This will run for 3 ticks, then pop agent.pop() agent.shooting = False
def run(self, agent): if self.start_time is None: self.start_time = agent.time car_to_boost = self.boost.location - agent.me.location distance_remaining = car_to_boost.flatten().magnitude() agent.line(self.boost.location - Vector(z=500), self.boost.location + Vector(z=500), [0, 255, 0]) if self.target is not None: vector = (self.target - self.boost.location).normalize() side_of_vector = sign(vector.cross(Vector(z=1)).dot(car_to_boost)) car_to_boost_perp = car_to_boost.cross( Vector(z=side_of_vector)).normalize() adjustment = car_to_boost.angle(vector) * distance_remaining / 3.14 final_target = self.boost.location + (car_to_boost_perp * adjustment) car_to_target = (self.target - agent.me.location).magnitude() else: adjustment = 9999 car_to_target = 0 final_target = self.boost.location # Some adjustment to the final target to ensure it's inside the field and we don't try to dirve through any goalposts to reach it if abs(agent.me.location.y) > 5150: final_target.x = cap(final_target.x, -750, 750) local_target = agent.me.local(final_target - agent.me.location) angles = defaultPD(agent, local_target) defaultThrottle(agent, 2300) agent.controller.boost = self.boost.large if abs( angles[1]) < 0.3 else False agent.controller.handbrake = True if abs( angles[1]) > 2.3 else agent.controller.handbrake velocity = 1 + agent.me.velocity.magnitude() if not self.boost.active or agent.me.boost >= 99.0 or distance_remaining < 350: agent.pop() elif agent.me.airborne: agent.push(recovery(self.target)) elif abs(angles[1]) < 0.05 and velocity > 600 and velocity < 2150 and ( distance_remaining / velocity > 2.0 or (adjustment < 90 and car_to_target / velocity > 2.0)): agent.push(flip(local_target)) elif agent.time - self.start_time > 6 and not agent.stack[ -1].__class__.__name__ == "flip": agent.pop()
def get_slices(agent, cap_, weight=None, start_slice=12): # Get the struct struct = agent.ball_prediction_struct min_time_to_ball = agent.predictions['self_min_time_to_ball'] - (1 / 15) # Make sure it isn't empty if struct is None or min_time_to_ball > cap_: return if start_slice / 60 < min_time_to_ball: start_slice = round(min_time_to_ball * 60) - 1 ball_y = agent.ball.location.y * side(agent.team) foes = len( tuple(foe for foe in agent.foes if not foe.demolished and foe.location.y * side(agent.team) < ball_y + 75)) if not agent.predictions[ 'goal'] and agent.ball_to_goal > 2560 and agent.ball.location.dist( agent.foe_goal.location) > 900 and foes > 0: factor = 1.2 - 0.04 * foes cap_ = min(agent.predictions['enemy_time_to_ball'] * factor, cap_) end_slices = None # If we're shooting, crop the struct if agent.shooting and agent.shot_weight != -1: # Get the time remaining time_remaining = agent.stack[0].intercept_time - agent.time if time_remaining < 0.5 and time_remaining >= 0: return # if the shot is done but it's working on it's 'follow through', then ignore this stuff if time_remaining > 0: # Convert the time remaining into number of slices, and take off the minimum gain accepted from the time min_gain = 0.05 if weight is None or weight is agent.shot_weight else -( agent.max_shot_weight - agent.shot_weight + 1) end_slice = round(min(time_remaining - min_gain, cap_) * 60) if end_slices is None: # Cap the slices end_slice = round(cap_ * 60) # We can't end a slice index that's lower than the start index if end_slice <= start_slice: return # for every second worth of slices that we have to search, skip 1 more slice (for performance reasons) - min 1 and max 3 skip = cap(end_slice - start_slice / 60, 1, 3) return struct.slices[start_slice:end_slice:skip]
def run(self, agent, manual=False): car_to_target = self.target - agent.me.location distance_remaining = car_to_target.flatten().magnitude() agent.dbg_2d(distance_remaining) agent.line(self.target - Vector(z=500), self.target + Vector(z=500), [255, 0, 255]) if (not self.brake and distance_remaining < 350) or (self.brake and distance_remaining < (agent.me.local(agent.me.velocity).x ** 2 * -1) / (2 * brake_accel.x)): if not manual: agent.pop() if self.brake: agent.push(brake()) return if self.vector != None: # See commends for adjustment in jump_shot or aerial for explanation side_of_vector = sign(self.vector.cross(Vector(z=1)).dot(car_to_target)) car_to_target_perp = car_to_target.cross(Vector(z=side_of_vector)).normalize() adjustment = car_to_target.angle(self.vector) * distance_remaining / 3.14 final_target = self.target + (car_to_target_perp * adjustment) else: final_target = self.target # Some adjustment to the final target to ensure it's inside the field and we don't try to dirve through any goalposts to reach it if abs(agent.me.location.y) > 5150: final_target.x = cap(final_target.x, -750, 750) local_target = agent.me.local(final_target - agent.me.location) angles = defaultPD(agent, local_target, self.direction) defaultThrottle(agent, 2300, self.direction) if len(agent.friends) > 0 and agent.me.local(agent.me.velocity).x < 250 and agent.controller.throttle > 0.75 and min(agent.me.location.flat_dist(car.location) for car in agent.friends) < 251: agent.push(flip(Vector(y=250))) return if agent.me.boost < 30 or (agent.playstyle is agent.playstyles.Defensive and agent.predictions['self_from_goal'] < 4000): agent.controller.boost = False agent.controller.handbrake = True if abs(angles[1]) >= 2.3 or (agent.me.local(agent.me.velocity).x >= 1400 and abs(angles[1]) > 1.5) else agent.controller.handbrake velocity = 1+agent.me.velocity.magnitude() if abs(angles[1]) < 0.05 and velocity > 600 and velocity < 2150 and distance_remaining / velocity > 2: agent.push(flip(local_target)) elif abs(angles[1]) > 2.8 and velocity < 200 and distance_remaining / velocity > 2: agent.push(flip(local_target, True)) elif agent.me.airborne: agent.push(recovery(self.target))
def run(self, agent): agent.shooting = True agent.shot_weight = agent.max_shot_weight - 1 if self.ball_location is None or not shot_valid(agent, self, threshold=75): self.ball_location, self.intercept_time, self.direction = self.get_intercept(agent) if self.ball_location is None: agent.shooting = False agent.shot_weight = -1 agent.pop() return agent.shot_time = self.intercept_time t = self.intercept_time - agent.time # if we ran out of time, just pop # this can be because we were successful or not - we can't tell if t < -0.3: agent.shooting = False agent.shot_weight = -1 agent.pop() return if self.brake: if agent.ball.location.dist(agent.me.location) < 250 and agent.ball.location.y * side(agent.team) + 10 < agent.me.location.y and agent.ball.location.z < 190: agent.pop() agent.flip(agent.me.local(agent.ball.location)) else: # current velocity u = agent.me.local(agent.me.velocity).x a = brake_accel.x # calculate how much distance we need to slow down x = (u ** 2 * -1) / (2 * a) if self.ball_location.dist(agent.me.location) <= x: self.brake = True agent.push(brake()) return agent.line(self.ball_location.flatten(), self.ball_location.flatten() + Vector(z=250), color=[255, 0, 255]) angles = defaultPD(agent, agent.me.local(self.ball_location - agent.me.location), self.direction) # We want to get there before the ball does, so take the time we have and get 3 fifths of it required_speed = cap(agent.me.location.dist(self.ball_location) / ((self.intercept_time - agent.time) * (3/5)), 600, 2275) defaultThrottle(agent, required_speed, self.direction) agent.controller.boost = False if abs(angles[1]) > 0.3 else agent.controller.boost agent.controller.handbrake = True if abs(angles[1]) >= 2.3 or (agent.me.local(agent.me.velocity).x >= 1400 and abs(angles[1]) > 1.5) and self.direction == 1 else False
def find_any_aerial(agent, cap_=3): struct = agent.predictions['ball_struct'] if struct is None: return max_aerial_height = math.inf if len(agent.friends) == 0 and len(agent.foes) == 1: max_aerial_height = 643 i = 10 # Begin by looking 0.2 seconds into the future while i < struct.num_slices: intercept_time = struct.slices[i].game_seconds time_remaining = intercept_time - agent.time if time_remaining <= 0: return ball_location = Vector(struct.slices[i].physics.location.x, struct.slices[i].physics.location.y, struct.slices[i].physics.location.z) if abs(ball_location.y) > 5212: break ball_velocity = Vector( struct.slices[i].physics.velocity.x, struct.slices[i].physics.velocity.y, struct.slices[i].physics.velocity.z).magnitude() i += 15 - cap(int(ball_velocity // 150), 0, 13) if 275 > ball_location.z or ball_location.z > max_aerial_height: continue # remove the need for a target to hit the ball to if is_fast_shot(agent.me.location, ball_location, intercept_time, agent.time, cap_): aerial = Aerial(ball_location, intercept_time) if aerial.is_viable(agent): return aerial
def get_slices(agent, cap_, weight=0, start_slice=6): # Get the struct struct = agent.ball_prediction_struct min_time_to_ball = max(agent.me.minimum_time_to_ball / 3 * 2, 0) # Make sure it isn't empty if struct is None or min_time_to_ball > cap_: return if start_slice / 60 < min_time_to_ball: start_slice = round(min_time_to_ball * 60) - 1 end_slices = None # If we're shooting, crop the struct if agent.shooting: shot_weight = agent.stack[0].weight if shot_weight != -1: # Get the time remaining time_remaining = agent.stack[0].intercept_time - agent.time if 0 < time_remaining < 0.5: return # if the shot is done but it's working on it's 'follow through', then ignore this stuff if time_remaining > 0: # Convert the time remaining into number of slices, and take off the minimum gain accepted from the time min_gain = 0.2 if weight is None or weight is shot_weight else (-1 * (agent.max_shot_weight - shot_weight + 1)) end_slice = round(min(time_remaining - min_gain, cap_) * 60) if end_slices is None: # Cap the slices end_slice = round(cap_ * 60) # We can't end a slice index that's lower than the start index if end_slice <= start_slice: return # for every second worth of slices that we have to search, skip 1 more slice (for performance reasons) - min 1 and max 3 skip = cap(end_slice - start_slice / 60, 1, 3) return struct.slices[start_slice:end_slice:skip]
def run(self, agent): if self.ball: target = (agent.ball.location - agent.me.location).flatten() else: target = agent.me.velocity.flatten() if self.target is None else ( self.target - agent.me.location).flatten() if agent.gravity.z < -550 and agent.gravity.z > -750: if self.counter == 0 and abs(Vector(x=1).angle(target)) <= 0.05: agent.pop() return if self.counter < 3: self.counter += 1 target = agent.me.local(target) if self.counter < 3: agent.controller.jump = True elif agent.me.airborne and abs(Vector(x=1).angle(target)) > 0.05: defaultPD(agent, target) else: agent.pop() else: target = agent.me.local(target) angle_to_target = abs(Vector(x=1).angle2D(target)) if angle_to_target > 0.1: if self.start_loc is None: self.start_loc = agent.me.location direction = -1 if angle_to_target < 1.57 else 1 agent.controller.steer = cap(target.y / 100, -1, 1) * direction agent.controller.throttle = 0.5 * direction agent.controller.handbrake = True else: agent.pop() if self.start_loc is not None: agent.push(goto(self.start_loc, target, True))
def run(self, agent): agent.shooting = True raw_time_remaining = self.intercept_time - agent.time # Capping raw_time_remaining above 0 to prevent division problems time_remaining = cap(raw_time_remaining, 0.001, 10.0) car_to_ball = self.ball_location - agent.me.location # whether we are to the left or right of the shot vector side_of_shot = sign( self.shot_vector.cross(Vector(z=1)).dot(car_to_ball)) car_to_dodge_point = self.dodge_point - agent.me.location car_to_dodge_perp = car_to_dodge_point.cross( Vector(z=side_of_shot)) # perpendicular distance_remaining = car_to_dodge_point.magnitude() speed_required = distance_remaining / time_remaining acceleration_required = backsolve( self.dodge_point, agent.me, time_remaining, 0 if not self.jumping else agent.gravity.z) local_acceleration_required = agent.me.local(acceleration_required) # The adjustment causes the car to circle around the dodge point in an effort to line up with the shot vector # The adjustment slowly decreases to 0 as the bot nears the time to jump adjustment = car_to_dodge_point.angle( self.shot_vector) * distance_remaining / 2.0 # size of adjustment # factoring in how close to jump we are adjustment *= (cap(self.jump_threshold - (acceleration_required.z), 0, self.jump_threshold) / self.jump_threshold) # we don't adjust the final target if we are already jumping final_target = self.dodge_point + ( (car_to_dodge_perp.normalize() * adjustment) if not self.jumping else 0) + Vector(z=50) # Ensuring our target isn't too close to the sides of the field, where our car would get messed up by the radius of the curves # Some adjustment to the final target to ensure it's inside the field and we don't try to dirve through any goalposts to reach it if abs(agent.me.location.y) > 5150: final_target.x = cap(final_target.x, -750, 750) local_final_target = agent.me.local(final_target - agent.me.location) # drawing debug lines to show the dodge point and final target (which differs due to the adjustment) agent.line(agent.me.location, self.dodge_point, agent.renderer.white()) agent.line(self.dodge_point - Vector(z=100), self.dodge_point + Vector(z=100), agent.renderer.red()) agent.line(final_target - Vector(z=100), final_target + Vector(z=100), agent.renderer.green()) # Calling our drive utils to get us going towards the final target angles = defaultPD(agent, local_final_target, self.direction) defaultThrottle(agent, speed_required, self.direction) agent.line(agent.me.location, agent.me.location + (self.shot_vector * 200), agent.renderer.white()) agent.controller.boost = False if abs( angles[1]) > 0.3 or agent.me.airborne else agent.controller.boost agent.controller.handbrake = True if abs( angles[1] ) > 2.3 and self.direction == 1 else agent.controller.handbrake if not self.jumping: if raw_time_remaining <= 0 or ( speed_required - 2300) * time_remaining > 45 or not shot_valid(agent, self): # If we're out of time or not fast enough to be within 45 units of target at the intercept time, we pop agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 if agent.me.airborne: agent.push(recovery()) elif local_acceleration_required.z > self.jump_threshold and local_acceleration_required.z > local_acceleration_required.flatten( ).magnitude(): # Switch into the jump when the upward acceleration required reaches our threshold, and our lateral acceleration is negligible self.jumping = True else: if (raw_time_remaining > 0.2 and not shot_valid(agent, self, 150) ) or raw_time_remaining <= -0.9 or (not agent.me.airborne and self.counter > 0): agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 agent.push(recovery()) elif self.counter == 0 and local_acceleration_required.z > 0 and raw_time_remaining > 0.083: # Initial jump to get airborne + we hold the jump button for extra power as required agent.controller.jump = True elif self.counter < 3: # make sure we aren't jumping for at least 3 frames agent.controller.jump = False self.counter += 1 elif raw_time_remaining <= 0.1 and raw_time_remaining > -0.9: # dodge in the direction of the shot_vector agent.controller.jump = True if not self.dodging: vector = agent.me.local(self.shot_vector) self.p = abs(vector.x) * -sign(vector.x) self.y = abs(vector.y) * sign(vector.y) * self.direction self.dodging = True # simulating a deadzone so that the dodge is more natural agent.controller.pitch = self.p if abs(self.p) > 0.2 else 0 agent.controller.yaw = self.y if abs(self.y) > 0.3 else 0
def run(self, agent): agent.shooting = True raw_time_remaining = self.intercept_time - agent.time # Capping raw_time_remaining above 0 to prevent division problems time_remaining = cap(raw_time_remaining, 0.001, 10.0) car_to_ball = self.ball_location - agent.me.location # whether we are to the left or right of the shot vector side_of_shot = sign( self.shot_vector.cross(Vector(z=1)).dot(car_to_ball)) car_to_intercept = self.intercept - agent.me.location car_to_intercept_perp = car_to_intercept.cross( Vector(z=side_of_shot)) # perpendicular distance_remaining = car_to_intercept.flatten().magnitude() speed_required = distance_remaining / time_remaining # When still on the ground we pretend agent.gravity doesn't exist, for better or worse acceleration_required = backsolve( self.intercept, agent.me, time_remaining, 0 if self.jump_time == 0 else agent.gravity.z) local_acceleration_required = agent.me.local(acceleration_required) # The adjustment causes the car to circle around the dodge point in an effort to line up with the shot vector # The adjustment slowly decreases to 0 as the bot nears the time to jump adjustment = car_to_intercept.angle( self.shot_vector) * distance_remaining / 1.57 # size of adjustment # factoring in how close to jump we are adjustment *= cap(self.jump_threshold - (acceleration_required.z), 0, self.jump_threshold) / self.jump_threshold # we don't adjust the final target if we are already jumping final_target = self.intercept + ( (car_to_intercept_perp.normalize() * adjustment) if self.jump_time == 0 else 0) # Some extra adjustment to the final target to ensure it's inside the field and we don't try to dirve through any goalposts to reach it if abs(agent.me.location.y) > 5150: final_target.x = cap(final_target.x, -750, 750) local_final_target = agent.me.local(final_target - agent.me.location) # drawing debug lines to show the dodge point and final target (which differs due to the adjustment) agent.line(agent.me.location, self.intercept, agent.renderer.white()) agent.line(self.intercept - Vector(z=100), self.intercept + Vector(z=100), agent.renderer.red()) agent.line(final_target - Vector(z=100), final_target + Vector(z=100), agent.renderer.green()) angles = defaultPD(agent, local_final_target) if self.jump_time == 0: defaultThrottle(agent, speed_required) agent.controller.boost = False if abs( angles[1] ) > 0.3 or agent.me.airborne else agent.controller.boost agent.controller.handbrake = True if abs( angles[1]) > 2.3 else agent.controller.handbrake if acceleration_required.z > self.jump_threshold: # Switch into the jump when the upward acceleration required reaches our threshold, hopefully we have aligned already... self.jump_time = agent.time else: time_since_jump = agent.time - self.jump_time # While airborne we boost if we're within 30 degrees of our local acceleration requirement if agent.me.airborne and local_acceleration_required.magnitude( ) * time_remaining > 100: angles = defaultPD(agent, local_acceleration_required) if abs(angles[0]) + abs(angles[1]) < 0.5: agent.controller.boost = True if self.counter == 0 and (time_since_jump <= 0.2 and local_acceleration_required.z > 0): # hold the jump button up to 0.2 seconds to get the most acceleration from the first jump agent.controller.jump = True elif time_since_jump > 0.2 and self.counter < 3: # Release the jump button for 3 ticks agent.controller.jump = False self.counter += 1 elif local_acceleration_required.z > 300 and self.counter == 3: # the acceleration from the second jump is instant, so we only do it for 1 frame agent.controller.jump = True agent.controller.pitch = 0 agent.controller.yaw = 0 agent.controller.roll = 0 self.counter += 1 if raw_time_remaining < -0.25 or not shot_valid(agent, self): agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 agent.push(recovery())
def run(self, agent, manual=False): car_to_target = self.target - agent.me.location distance_remaining = car_to_target.flatten().magnitude() angle_to_target = abs( Vector(x=1).angle2D(agent.me.local(car_to_target))) direction = 1 if angle_to_target <= 2 or ( agent.gravity.z > -450 and distance_remaining >= 1000) else -1 agent.dbg_2d(f"Angle to target: {angle_to_target}") agent.dbg_2d(f"Distance to target: {distance_remaining}") agent.line(self.target - Vector(z=500), self.target + Vector(z=500), (255, 0, 255)) if (not self.brake and distance_remaining < 350) or ( self.brake and distance_remaining * 0.95 < (agent.me.local_velocity().x**2 * -1) / (2 * brake_accel.x)): if not manual: agent.pop() if self.brake: agent.push(brake()) return final_target = self.target.copy() # Some adjustment to the final target to ensure it's inside the field and we don't try to drive through any goalposts to reach it if abs(agent.me.location.y) > 5120 - (agent.me.hitbox.length / 2): final_target.x = cap(final_target.x, -750, 750) if self.vector is not None: # See comments for adjustment in jump_shot for explanation side_of_vector = sign( self.vector.cross(Vector(z=1)).dot(car_to_target)) car_to_target_perp = car_to_target.cross( Vector(z=side_of_vector)).normalize() adjustment = car_to_target.angle2D( self.vector) * distance_remaining / 3.14 final_target += car_to_target_perp * adjustment local_target = agent.me.local_location(final_target) defaultPD(agent, local_target) target_speed = 2300 if distance_remaining > 640 else 1400 defaultThrottle(agent, target_speed * direction) # this is to break rule 1's with TM8'S ONLY # 251 is the distance between center of the 2 longest cars in the game, with a bit extra if len(agent.friends) > 0 and agent.me.local_velocity( ).x < 50 and agent.controller.throttle == 1 and min( agent.me.location.flat_dist(car.location) for car in agent.friends) < 251: if self.rule1_timer == -1: self.rule1_timer = agent.time elif agent.time - self.rule1_timer > 1.5: agent.push(flip(Vector(y=250))) return elif self.rule1_timer != -1: self.rule1_timer = -1 if distance_remaining < 320: agent.controller.boost = False agent.controller.handbrake = angle_to_target > 1.54 if direction == 1 else angle_to_target < 2.2 velocity = agent.me.local_velocity().x if agent.boost_amount != 'unlimited' and angle_to_target < 0.03 and distance_remaining > 1920 and velocity > 600 and velocity < 2150 and distance_remaining / velocity > 2: if agent.gravity.z < -450: agent.push(wave_dash()) else: agent.push(flip(local_target)) elif direction == -1 and distance_remaining > 1000 and velocity < 200: agent.push(flip(local_target, True)) elif agent.me.airborne: agent.push(recovery(self.target))
def find_hits(agent, targets, cap_=6): # find_hits takes a dict of (left,right) target pairs and finds routines that could hit the ball between those target pairs # find_hits is only meant for routines that require a defined intercept time/place in the future # find_hits should not be called more than once in a given tick, as it has the potential to use an entire tick to calculate hits = {name: [] for name in targets} struct = agent.predictions['ball_struct'] if struct is None: return hits max_aerial_height = 400 max_jump_hit_height = 200 min_aerial_height = 150 if (len(agent.foes) > 1 and len(agent.friends) == 0) or len(agent.friends) > 2: max_jump_hit_height = 300 max_aerial_height = 500 # Begin looking at slices 0.2s into the future i = 10 while i < struct.num_slices: # Gather some data about the slice intercept_time = struct.slices[i].game_seconds time_remaining = intercept_time - agent.time if time_remaining > 0: ball_location = Vector(struct.slices[i].physics.location.x, struct.slices[i].physics.location.y, struct.slices[i].physics.location.z) if abs(ball_location.y) > 5200: break # abandon search if ball is scored at/after this point ball_velocity = Vector( struct.slices[i].physics.velocity.x, struct.slices[i].physics.velocity.y, struct.slices[i].physics.velocity.z).magnitude() # determine the next slice we will look at, based on ball velocity (slower ball needs fewer slices) i += 15 - cap(int(ball_velocity // 150), 0, 13) # If the ball is above what this function can handle, don't bother with any further processing and skip to the next slice if ball_location.z > max_aerial_height: continue car_to_ball = ball_location - agent.me.location # Adding a True to a vector's normalize will have it also return the magnitude of the vector direction, distance = car_to_ball.normalize(True) # How far the car must turn in order to face the ball, for forward and reverse forward_angle = direction.angle(agent.me.forward) backward_angle = math.pi - forward_angle # Accounting for the average time it takes to turn and face the ball # Backward is slightly longer as typically the car is moving forward and takes time to slow down forward_time = time_remaining - (forward_angle * 0.318) backward_time = time_remaining - (backward_angle * 0.418) # If the car only had to drive in a straight line, we ensure it has enough time to reach the ball (a few assumptions are made) forward_flag = forward_time > 0 and ( distance * 1.05 / forward_time) < ( 2290 if agent.me.boost > distance / 100 else 1400) backward_flag = distance < 1500 and backward_time > 0 and ( distance * 1.05 / backward_time) < 1200 # Provided everything checks out, we begin to look at the target pairs if forward_flag or backward_flag: for pair in targets: # First we correct the target coordinates to account for the ball's radius # If swapped is True, the shot isn't possible because the ball wouldn't fit between the targets left, right, swapped = post_correction( ball_location, targets[pair][0], targets[pair][1]) if not swapped: # Now we find the easiest direction to hit the ball in order to land it between the target points left_vector = (left - ball_location).normalize() right_vector = (right - ball_location).normalize() best_shot_vector = direction.clamp( left_vector, right_vector) # Check to make sure our approach is inside the field # Check if our shot is fast enough. This is equal to 500uu's per 1/2 second, for a max time of `cap` (defaut 6) seconds. if in_field(ball_location - (200 * best_shot_vector), 1) and is_fast_shot( agent.me.location, ball_location, intercept_time, agent.time, cap_): # The slope represents how close the car is to the chosen vector, higher = better # A slope of 1.0 would mean the car is 45 degrees off slope = find_slope(best_shot_vector, car_to_ball) if forward_flag: if ball_location.z > min_aerial_height and ball_location.z <= max_aerial_height and slope > 1.0 and ( ball_location.z - 250) * 0.14 > agent.me.boost: hits[pair].append( aerial_shot(ball_location, intercept_time, best_shot_vector, slope)) return hits if ball_location.z <= max_jump_hit_height and slope > 0: hits[pair].append( jump_shot(ball_location, intercept_time, best_shot_vector, slope)) return hits elif backward_flag and ball_location.z <= 280 and slope > 0.25: hits[pair].append( jump_shot(ball_location, intercept_time, best_shot_vector, slope, -1)) return hits else: return hits return hits
def update_predictions(self): len_friends = len(self.friends) can_shoot = True if len(self.foes) > 0: foe_distances = tuple( self.ball.location.flat_dist(foe.location) for foe in self.foes if not foe.demolished) self_dist = self.ball.location.flat_dist(self.me.location) if len(foe_distances) > 0: if self.odd_tick == 0: self.predictions['enemy_time_to_ball'] = min( tuple(self.time_to_ball(foe) for foe in self.foes)) self.predictions['closest_enemy'] = min(foe_distances) else: self.predictions['enemy_time_to_ball'] = 7 self.predictions['closest_enemy'] = math.inf else: self.predictions['enemy_time_to_ball'] = 7 self.predictions['closest_enemy'] = math.inf self.future_ball_location_slice = cap( round(self.predictions['enemy_time_to_ball'] * 60), 0, len(self.ball_prediction_struct.slices) - 1) self.dbg_2d( f"Predicted enemy time to ball: {round(self.predictions['enemy_time_to_ball'], 1)}" ) self.predictions[ 'self_from_goal'] = self.friend_goal.location.flat_dist( self.me.location) self.predictions['self_to_ball'] = self.ball.location.flat_dist( self.me.location) if not self.predictions['was_down']: self.predictions[ 'was_down'] = self.game.friend_score - self.game.foe_score > 1 if len_friends > 0: teammates = tuple(itertools.chain(self.friends, [self.me])) self.predictions["team_from_goal"] = sorted( tuple( self.friend_goal.location.flat_dist(teammate.location) if not teammate.demolished else math.inf for teammate in teammates)) self.predictions["team_to_ball"] = sorted( tuple( self.ball.location.flat_dist(teammate.location) if not teammate.demolished else math.inf for teammate in teammates)) if len_friends >= 2 and can_shoot: can_shoot = self.predictions[ 'self_from_goal'] != self.predictions["team_from_goal"][0] if self.odd_tick == 0: self.predictions['self_min_time_to_ball'] = self.time_to_ball( self.me) self.min_intercept_slice = cap( round(self.predictions['self_min_time_to_ball'] * 60), 0, len(self.ball_prediction_struct.slices) - 1) if self.odd_tick % 2 == 0: if self.goalie: self.playstyle = self.playstyles.Defensive # elif len_friends > 0: # ball_loc_y = self.ball.location.y * side(self.team) # if ball_loc_y < 2560: # # If we're down or up by 2 goals in 2's, then start playing more defensive # self_time_to_ball = self.predictions['self_min_time_to_ball'] * 1.05 # team_time_to_ball = min(tuple(self.time_to_ball(teammate) for teammate in self.friends)) * 1.05 # if ball_loc_y < -1280 and self_time_to_ball < team_time_to_ball and self.predictions['self_from_goal'] != self.predictions["team_from_goal"][0]: # self.playstyle = self.playstyles.Offensive if len_friends > 1 or (len_friends == 1 and (self.predictions['was_down'] or abs(self.game.friend_score - self.game.foe_score) <= 1)) else self.playstyles.Neutral # elif self.predictions['self_from_goal'] == self.predictions["team_from_goal"][0]: # self.playstyle = self.playstyles.Defensive if len_friends > 1 else self.playstyles.Neutral # else: # self.playstyle = self.playstyles.Neutral # else: # self.playstyle = self.playstyles.Defensive else: self_time_to_ball = self.predictions[ 'self_min_time_to_ball'] * 1.05 if self.ball.location.y * side(self.team) < 640: self.playstyle = self.playstyles.Offensive if self_time_to_ball < self.predictions[ 'enemy_time_to_ball'] else self.playstyles.Neutral else: self.playstyle = self.playstyles.Neutral if self_time_to_ball < self.predictions[ 'enemy_time_to_ball'] else self.playstyles.Defensive is_own_goal = False is_goal = False if self.ball_prediction_struct is not None: for ball_slice in self.ball_prediction_struct.slices[30::12]: location = ball_slice.physics.location.y * side(self.team) if location >= 5212.75: is_own_goal = True break if location <= -5212.75: is_goal = True break if is_own_goal and not self.predictions['own_goal']: self.send_quick_chat(QuickChats.CHAT_EVERYONE, QuickChats.Compliments_NiceShot) if is_goal and not self.predictions['goal']: self.send_quick_chat(QuickChats.CHAT_EVERYONE, QuickChats.Reactions_Wow) self.predictions["own_goal"] = is_own_goal self.predictions["goal"] = is_goal self.dbg_2d( f"Minimum time to ball: {round(self.predictions['self_min_time_to_ball'], 1)}" ) if not can_shoot and self.can_shoot is None: self.can_shoot = self.time - 2.9 if self.can_shoot is not None and (self.time - self.can_shoot >= 3 or self.predictions['own_goal']): self.can_shoot = None
def run(self, agent): # This routine is the same as jump_shot, but it's designed to hit the ball above 250uus (and below 551uus or so) without any boost if not agent.shooting: agent.shooting = True T = self.intercept_time - agent.time # Capping T above 0 to prevent division problems time_remaining = cap(T, 0.000001, 6) slice_n = round(T * 60) agent.dbg_2d(f"Shot slice #: {slice_n}") if T > 0.3 or self.ball_location is None: ball = agent.ball_prediction_struct.slices[ slice_n].physics.location self.ball_location = Vector(ball.x, ball.y, ball.z) self.dodge_point = self.ball_location - (self.shot_vector * agent.best_shot_value) if self.dodge_point.z > 490 or self.dodge_point.z < 380: agent.pop() return car_to_ball = self.ball_location - agent.me.location # whether we should go forwards or backwards angle_to_target = abs( Vector(x=1).angle2D(agent.me.local_location(self.dodge_point))) # whether we are to the left or right of the shot vector side_of_shot = sign( self.shot_vector.cross(Vector(z=1)).dot(car_to_ball)) final_target = self.dodge_point.copy() # Some adjustment to the final target to ensure it's inside the field and we don't try to drive through any goalposts to reach it if abs(agent.me.location.y) > 5120 - (agent.me.hitbox.length / 2): final_target.x = cap(final_target.x, -750, 750) car_to_dodge_point = final_target - agent.me.location car_to_dodge_perp = car_to_dodge_point.cross( Vector(z=side_of_shot)) # perpendicular distance_remaining = car_to_dodge_point.flatten().magnitude() acceleration_required = backsolve( self.dodge_point, agent.me, time_remaining, Vector() if not self.jumping else agent.gravity) local_acceleration_required = agent.me.local_velocity( acceleration_required) # The adjustment causes the car to circle around the dodge point in an effort to line up with the shot vector # The adjustment slowly decreases to 0 as the bot nears the time to jump adjustment = car_to_dodge_point.angle2D( self.shot_vector) * distance_remaining / 2 # size of adjustment # controls how soon car will jump based on acceleration required # we set this based on the time remaining # bigger = later, which allows more time to align with shot vector # smaller = sooner jump_threshold = cap(T * 200, 250, 584) # factoring in how close to jump we are adjustment *= (cap(jump_threshold - (acceleration_required.z), 0, jump_threshold) / jump_threshold) # we don't adjust the final target if we are already jumping final_target += ((car_to_dodge_perp.normalize() * adjustment) if not self.jumping else 0) + Vector(z=50) distance_remaining = (final_target - agent.me.location).flatten().magnitude() local_final_target = agent.me.local_location(final_target) # drawing debug lines to show the dodge point and final target (which differs due to the adjustment) agent.line(agent.me.location, self.dodge_point, agent.renderer.white()) agent.line(self.dodge_point - Vector(z=100), self.dodge_point + Vector(z=100), agent.renderer.green()) vf = agent.me.velocity + agent.gravity * T distance = agent.me.local_location( self.dodge_point).x if agent.me.airborne else distance_remaining speed_required = 2300 if distance_remaining > 2560 else distance / time_remaining agent.dbg_2d(f"Speed required: {speed_required}") if not self.jumping: velocity = agent.me.local_velocity().x local_dodge_point = agent.me.local_location( self.dodge_point.flatten()) local_vf = agent.me.local(vf.flatten()) if T <= 0 or not virxrlcu.double_jump_shot_is_viable( T + 0.3, agent.boost_accel, agent.me.location.dist( self.ball_location), self.shot_vector.tuple(), agent.me.forward.tuple(), agent.me.boost if agent.boost_amount != 'unlimited' else 100000, velocity): # If we're out of time or the ball was hit away or we just can't get enough speed, pop agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 if agent.me.airborne: agent.push(ball_recovery()) elif abs(local_dodge_point.y ) < 92 and local_vf.x >= local_dodge_point.x: # Switch into the jump when the upward acceleration required reaches our threshold, and our lateral acceleration is negligible, and we're close enough to the point in time where the ball will be at the given location self.jumping = True elif agent.boost_amount != 'unlimited' and angle_to_target < 0.03 and velocity > 600 and velocity < speed_required - 500 and distance_remaining / velocity > 3: if agent.gravity.z < -450: agent.push(wave_dash()) else: agent.push(flip(local_final_target)) elif agent.boost_amount != 'unlimited' and angle_to_target >= 2 and distance_remaining > 1000 and velocity < 200: agent.push(flip(local_final_target, True)) elif agent.me.airborne: agent.push(recovery(local_final_target)) else: defaultPD(agent, local_final_target) defaultThrottle(agent, speed_required) agent.controller.handbrake = angle_to_target > 1.54 else: # Mark the time we started jumping so we know when to dodge if self.jump_time == -1: self.jump_time = agent.time jump_elapsed = agent.time - self.jump_time tau = jump_max_duration - jump_elapsed xf = agent.me.location + agent.me.velocity * T + 0.5 * agent.gravity * T * T if jump_elapsed == 0: vf += agent.me.up * jump_speed xf += agent.me.up * jump_speed * T vf += agent.me.up * jump_acc * tau xf += agent.me.up * jump_acc * tau * (T - 0.5 * tau) vf += agent.me.up * jump_speed xf += agent.me.up * jump_speed * (T - tau) delta_x = self.dodge_point - xf direction = delta_x.normalize() if abs(agent.me.forward.dot(direction)) > 0.5: delta_v = delta_x.dot(agent.me.forward) / T if agent.me.boost > 0 and delta_v >= agent.boost_accel * min_boost_time: agent.controller.boost = True else: agent.controller.throttle = cap( delta_v / (throttle_accel * min_boost_time), -1, 1) if T <= -0.4 or (not agent.me.airborne and self.counter > 0): agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 agent.push(ball_recovery()) elif jump_elapsed < jump_max_duration and vf.z <= self.dodge_point.z: agent.controller.jump = True elif self.counter < 4: self.counter += 1 if self.counter == 3: agent.controller.jump = True elif self.counter == 4: defaultPD(agent, agent.me.local_location(self.dodge_point), upside_down=True) if self.counter < 3: defaultPD( agent, agent.me.local( (self.dodge_point - agent.me.location).flatten())) l_vf = vf + agent.me.location agent.line(l_vf - Vector(z=100), l_vf + Vector(z=100), agent.renderer.red())
def run(self, agent): agent.shooting = True if self.time is -1: elapsed = 0 self.time = agent.time agent.print( f"Enter aerial - Hit ball {round(agent.me.location.dist(self.target) * 1000) / 1000}uu's away in {round((self.intercept_time - self.time) * 1000) / 1000}s" ) else: elapsed = agent.time - self.time T = self.intercept_time - agent.time xf = agent.me.location + agent.me.velocity * T + 0.5 * agent.gravity * T**2 vf = agent.me.velocity + agent.gravity * T if self.jumping: if self.jump_time == -1: jump_elapsed = 0 self.jump_time = agent.time else: jump_elapsed = agent.time - self.jump_time tau = jump_max_duration - jump_elapsed if jump_elapsed == 0: vf += agent.me.up * jump_speed xf += agent.me.up * jump_speed * T vf += agent.me.up * jump_acc * tau xf += agent.me.up * jump_acc * tau * (T - 0.5 * tau) vf += agent.me.up * jump_speed xf += agent.me.up * jump_speed * (T - tau) if jump_elapsed < jump_max_duration: agent.controller.jump = True elif elapsed >= jump_max_duration and self.counter < 3: agent.controller.jump = False self.counter += 1 elif elapsed < 0.3: agent.controller.jump = True else: self.jumping = jump_elapsed <= 0.3 # jump_max_duration else: agent.controller.jump = False delta_x = self.target - xf direction = delta_x.normalize() agent.line(agent.me.location, self.target, agent.renderer.white()) agent.line(self.target - Vector(z=100), self.target + Vector(z=100), agent.renderer.red()) agent.line(agent.me.location, direction, agent.renderer.green()) if delta_x.norm() > 50: defaultPD(agent, agent.me.local(delta_x)) else: defaultPD(agent, agent.me.local(self.target)) if jump_max_duration <= elapsed and elapsed < 0.3 and self.counter**3: agent.controller.roll = agent.controller.pitch = agent.controller.yaw = agent.controller.steer = 0 if agent.me.forward.angle3D(direction) < 0.3: if delta_x.norm() > 50: agent.controller.boost = 1 agent.controller.throttle = 0 else: agent.controller.boost = 0 agent.controller.throttle = cap(0.5 * throttle_accel * T * T, 0, 1) else: agent.controller.boost = agent.controller.throttle = 0 still_valid = shot_valid(agent, self, threshold=250, target=self.target) if T <= 0 or not still_valid: if not still_valid: agent.print("Aerial is no longer valid") agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 agent.push(recovery(agent.friend_goal.location))
def run(self, agent): if not agent.shooting: agent.shooting = True T = self.intercept_time - agent.time # Capping T above 0 to prevent division problems time_remaining = cap(T, 0.000001, 6) slice_n = round(T * 60) agent.dbg_2d(f"Shot slice #: {slice_n}") if T > 0.3 or self.ball_location is None: ball = agent.ball_prediction_struct.slices[ slice_n].physics.location self.ball_location = Vector(ball.x, ball.y, ball.z) self.dodge_point = self.ball_location - (self.shot_vector * agent.best_shot_value) if self.dodge_point.z > 300: agent.pop() return car_to_ball = self.ball_location - agent.me.location # whether we should go forwards or backwards angle_to_target = abs(Vector(x=1).angle2D(agent.me.local(car_to_ball))) # whether we are to the left or right of the shot vector side_of_shot = sign( self.shot_vector.cross(Vector(z=1)).dot(car_to_ball)) final_target = self.dodge_point.copy() # Some adjustment to the final target to ensure it's inside the field and we don't try to drive through any goalposts to reach it if abs(agent.me.location.y) > 5120 - (agent.me.hitbox.length / 2): final_target.x = cap(final_target.x, -750, 750) car_to_dodge_point = final_target - agent.me.location car_to_dodge_perp = car_to_dodge_point.cross( Vector(z=side_of_shot)) # perpendicular acceleration_required = backsolve( self.dodge_point, agent.me, time_remaining, Vector() if not self.jumping else agent.gravity) local_acceleration_required = agent.me.local(acceleration_required) distance_remaining = car_to_dodge_point.flatten().magnitude() # The adjustment causes the car to circle around the dodge point in an effort to line up with the shot vector # The adjustment slowly decreases to 0 as the bot nears the time to jump adjustment = car_to_dodge_point.angle2D( self.shot_vector) * distance_remaining / 2 # size of adjustment # controls how soon car will jump based on acceleration required # we set this based on the time remaining # bigger = later, which allows more time to align with shot vector # smaller = sooner jump_threshold = cap(T * 200, 250, 584) # factoring in how close to jump we are adjustment *= (cap(jump_threshold - (acceleration_required.z), 0, jump_threshold) / jump_threshold) # we don't adjust the final target if we are already jumping final_target += ((car_to_dodge_perp.normalize() * adjustment) if not self.jumping else 0) + Vector(z=50) distance_remaining = (final_target - agent.me.location).flatten().magnitude() direction = 1 if angle_to_target < 2.1 or ( agent.gravity.z > -450 and distance_remaining >= 1000) else -1 local_final_target = agent.me.local_location(final_target) # drawing debug lines to show the dodge point and final target (which differs due to the adjustment) agent.line(agent.me.location, self.dodge_point, agent.renderer.white()) agent.line(self.dodge_point - Vector(z=100), self.dodge_point + Vector(z=100), agent.renderer.green()) agent.line(final_target - Vector(z=100), final_target + Vector(z=100), agent.renderer.blue()) vf = agent.me.velocity + agent.gravity * T xf = agent.me.location + agent.me.velocity * T + 0.5 * agent.gravity * T * T ball_l = agent.me.local_location(agent.ball.location) speed_required = 2300 if ( self.ball_location.z < 92 + agent.me.hitbox.height / 2 and abs(ball_l.y) < 46 and ball_l.x > agent.me.hitbox.length ) or distance_remaining > 2560 else distance_remaining * 0.975 / time_remaining agent.dbg_2d(f"Speed required: {speed_required}") if not self.jumping: defaultPD(agent, local_final_target) defaultThrottle(agent, speed_required * direction) agent.controller.handbrake = angle_to_target > 1.54 if direction == 1 else angle_to_target < 2.2 local_dodge_point = agent.me.local_location( self.dodge_point.flatten()) # use algebra to get the required jump time min_jump_time = self.dodge_point - agent.me.location min_jump_time -= agent.me.velocity * T min_jump_time -= 0.5 * agent.gravity * T * T min_jump_time -= agent.me.up * jump_speed * T min_jump_time /= jump_acc # This leaves us with xf = dT - 0.5d^2, so we use the quadratic formula to solve it min_jump_time = max(quadratic(-0.5, T, sum(min_jump_time) / 3)) min_jump_time += 0.05 f_vf = vf + (agent.me.up * (jump_speed + jump_acc * min_jump_time)) local_vf = agent.me.local(f_vf.flatten()) velocity = agent.me.local_velocity().x if T <= 0 or not virxrlcu.jump_shot_is_viable( T + 0.3, agent.boost_accel, agent.me.location.dist( self.ball_location), self.shot_vector.tuple(), agent.me.forward.tuple(), agent.me.boost if agent.boost_amount != 'unlimited' else 100000, velocity): # If we're out of time or not fast enough to be within 45 units of target at the intercept time, we pop agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 if agent.me.airborne: agent.push(recovery()) elif self.dodge_point.z > 92 + agent.me.hitbox.height / 2 and ( (T <= min_jump_time and self.dodge_point.z >= 175) or (T <= 0.3 and self.dodge_point.z < 175)) and abs(local_dodge_point.y) < ( 92 if find_slope(self.shot_vector, car_to_ball) > 2 or self.dodge_point.z <= 92 + agent.me.hitbox.height / 2 else math.ceil(92 + agent.me.hitbox.width / 2)) and local_vf.x >= local_dodge_point.x: # Switch into the jump when the upward acceleration required reaches our threshold, and our lateral acceleration is negligible self.jumping = True elif agent.boost_amount != 'unlimited' and angle_to_target < 0.03 and velocity > 600 and velocity < speed_required - 150 and distance_remaining / velocity > 3: if agent.gravity.z < -450: agent.push(wave_dash()) else: agent.push(flip(local_final_target)) elif direction == -1 and velocity < 200 and distance_remaining / abs( velocity) > 2: agent.push(flip(local_final_target, True)) elif agent.me.airborne: agent.push(recovery(local_final_target)) else: if self.jump_time == -1: self.jump_time = agent.time jump_elapsed = agent.time - self.jump_time tau = jump_max_duration - jump_elapsed if jump_elapsed == 0: vf += agent.me.up * jump_speed xf += agent.me.up * jump_speed * T vf += agent.me.up * jump_acc * tau xf += agent.me.up * jump_acc * tau * (T - 0.5 * tau) delta_x = self.dodge_point - xf d_direction = delta_x.normalize() if abs(agent.me.forward.dot( d_direction)) > 0.5 and self.counter < 3: delta_v = delta_x.dot(agent.me.forward) / T if agent.me.boost > 0 and delta_v >= agent.boost_accel * min_boost_time: agent.controller.boost = True else: agent.controller.throttle = cap( delta_v / (throttle_accel * min_boost_time), -1, 1) if T <= -0.4 or (not agent.me.airborne and self.counter >= 3): agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 agent.push(recovery()) return else: if self.counter == 3 and agent.me.location.dist( self.dodge_point) < (92.75 + agent.me.hitbox.length) * 1.05: # Get the required pitch and yaw to flip correctly vector = agent.me.local(self.shot_vector) self.p = abs(vector.x) * -sign(vector.x) self.y = abs(vector.y) * sign(vector.y) * direction # simulating a deadzone so that the dodge is more natural self.p = cap(self.p, -1, 1) if abs(self.p) > 0.1 else 0 self.y = cap(self.y, -1, 1) if abs(self.y) > 0.1 else 0 agent.controller.pitch = self.p agent.controller.yaw = self.y # Wait 1 more frame before dodging self.counter += 1 elif self.counter == 4: # Dodge agent.controller.jump = True agent.controller.pitch = self.p agent.controller.yaw = self.y else: # Face the direction we're heading in, with the z of our target target = agent.me.local(agent.me.velocity.flatten()) target.z = agent.me.local_location(self.dodge_point).z defaultPD(agent, target) if jump_elapsed < jump_max_duration and vf.z < self.dodge_point.z: # Initial jump to get airborne + we hold the jump button for extra power as required agent.controller.jump = True elif self.counter < 3: # Make sure we aren't jumping for at least 3 frames self.counter += 1 l_vf = vf + agent.me.location agent.line(l_vf - Vector(z=100), l_vf + Vector(z=100), agent.renderer.red())
def run(self, agent): if not agent.shooting: agent.shooting = True if self.time == -1: self.time = agent.time elapsed = agent.time - self.time T = self.intercept_time - agent.time xf = agent.me.location + agent.me.velocity * T + 0.5 * agent.gravity * T * T vf = agent.me.velocity + agent.gravity * T slice_n = math.ceil(T * 60) agent.dbg_2d(f"Shot slice #: {slice_n}") if T > 0.1 or self.target is None: ball = agent.ball_prediction_struct.slices[ slice_n].physics.location self.ball = Vector(ball.x, ball.y, ball.z) self.target = self.ball - (self.shot_vector * agent.best_shot_value * 0.8) if agent.me.location.z > 2044 - agent.me.hitbox.height * 1.1: self.ceiling = True self.target -= Vector(z=92) if not self.ceiling and (self.jumping or not agent.me.airborne): agent.dbg_2d("Jumping") if not self.jumping or not agent.me.airborne: self.jumping = True self.jump_time = agent.time self.counter = 0 jump_elapsed = agent.time - self.jump_time tau = jump_max_duration - jump_elapsed if jump_elapsed == 0: vf += agent.me.up * jump_speed xf += agent.me.up * jump_speed * T vf += agent.me.up * jump_acc * tau xf += agent.me.up * jump_acc * tau * (T - 0.5 * tau) if self.fast_aerial: vf += agent.me.up * jump_speed xf += agent.me.up * jump_speed * (T - tau) if jump_elapsed < jump_max_duration: agent.controller.jump = True elif self.counter < 6: self.counter += 1 if self.counter == 3: agent.controller.jump = True self.dodging = True elif self.counter == 6: self.dodging = self.jumping = False elif jump_elapsed < jump_max_duration: agent.controller.jump = True else: self.jumping = False if self.ceiling: agent.dbg_2d(f"Ceiling shot") delta_x = self.target - xf direction = delta_x.normalize() agent.line(agent.me.location, self.target, agent.renderer.white()) c_vf = vf + agent.me.location agent.line(c_vf - Vector(z=100), c_vf + Vector(z=100), agent.renderer.blue()) agent.line(xf - Vector(z=100), xf + Vector(z=100), agent.renderer.red()) agent.line(self.target - Vector(z=100), self.target + Vector(z=100), agent.renderer.green()) if not self.dodging: target = delta_x if delta_x.magnitude() > 50 else ( self.target - agent.me.location) if self.jumping: target = target.flatten() target = agent.me.local(target) if abs(Vector(x=1).angle(target)) > 0.005: defaultPD(agent, target, upside_down=self.shot_vector.z < 0 and not self.jumping) if abs(agent.me.forward.dot(direction)) > 0.5: delta_v = delta_x.dot(agent.me.forward) / T if agent.me.boost > 0 and delta_v >= agent.boost_accel * min_boost_time: agent.controller.boost = True else: agent.controller.throttle = cap( delta_v / (throttle_accel * min_boost_time), -1, 1) if T <= 0 or (not self.jumping and not agent.me.airborne) or ( not self.jumping and T > 2 and self.fast_aerial and not virxrlcu.aerial_shot_is_viable( T + 0.3, 144, agent.boost_accel, agent.gravity.tuple(), agent.me.location.tuple(), agent.me.velocity.tuple(), agent.me.up.tuple(), agent.me.forward.tuple(), 1 if agent.me.airborne else -1, agent.me.boost if agent.boost_amount != 'unlimited' else 100000, self.ball.tuple())): agent.pop() agent.shooting = False agent.shot_weight = -1 agent.shot_time = -1 agent.push(ball_recovery()) elif (self.ceiling and self.target.dist(agent.me.location) < 92 + agent.me.hitbox.length and not agent.me.doublejumped and agent.me.location.z < agent.ball.location.z + 92 and self.target.y * side(agent.team) > -4240) or ( not self.ceiling and not self.fast_aerial and self.target.dist(agent.me.location) < 92 + agent.me.hitbox.length and not agent.me.doublejumped): agent.dbg_2d("Flipping") agent.controller.jump = True local_target = agent.me.local_location(self.target) agent.controller.pitch = abs( local_target.x) * -sign(local_target.x) agent.controller.yaw = abs(local_target.y) * sign(local_target.y)
def run(self, agent): self.recovery.target = agent.ball.location self.recovery.target.y = cap(self.recovery.target.y, -5100, 5100) self.recovery.run(agent)