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 find_any_jump_shot(agent, cap_=3): slices = get_slices(agent, cap_) if slices is None: return me = (agent.me.location.tuple(), agent.me.forward.tuple(), agent.me.boost if agent.boost_amount != 'unlimited' else 1000000, agent.me.local_velocity().x) game_info = (agent.best_shot_value, agent.boost_accel) for ball_slice in slices: intercept_time = ball_slice.game_seconds time_remaining = intercept_time - agent.time if time_remaining <= 0: continue ball_location = (ball_slice.physics.location.x, ball_slice.physics.location.y, ball_slice.physics.location.z) if abs(ball_location[1]) > 5212.75: return shot = virxrlcu.parse_slice_for_jump_shot(time_remaining - 0.1, *game_info, ball_location, *me) if shot['found'] == 1: return jump_shot(intercept_time, Vector(*shot['best_shot_vector']))
def find_jump_shot(agent, target, weight=None, cap_=6): # Takes a tuple of (left,right) target pairs and finds routines that could hit the ball between those target pairs # Only meant for routines that require a defined intercept time/place in the future # Here we get the slices that need to be searched - by defining a cap or a weight, we can reduce the number of slices and improve search times slices = get_slices(agent, cap_, weight=weight) if slices is None: return # Assemble data in a form that can be passed to C target = (target[0].tuple(), target[1].tuple()) me = (agent.me.location.tuple(), agent.me.forward.tuple(), agent.me.boost if agent.boost_amount != 'unlimited' else 100000, agent.me.local_velocity().x) game_info = (agent.best_shot_value, agent.boost_accel) # Loop through the slices for ball_slice in slices: # Gather some data about the slice intercept_time = ball_slice.game_seconds time_remaining = intercept_time - agent.time if time_remaining <= 0: continue ball_location = (ball_slice.physics.location.x, ball_slice.physics.location.y, ball_slice.physics.location.z) if abs(ball_location[1]) > 5212.75: return # abandon search if ball is scored at/after this point # Check if we can make a shot at this slice # This operation is very expensive, so we use a custom C function to improve run time shot = virxrlcu.parse_slice_for_jump_shot_with_target( time_remaining - 0.1, *game_info, ball_location, *me, *target) # If we found a viable shot, pass the data into the shot routine and return the shot if shot['found'] == 1: return jump_shot(intercept_time, Vector(*shot['best_shot_vector']))
def find_any_shot(agent, cap_=3, can_aerial=True, can_double_jump=True, can_jump=True): if not can_aerial and not can_double_jump and not can_jump: agent.print("WARNING: All shots were disabled when find_shot was ran") return slices = get_slices(agent, cap_) if slices is None: return me = (agent.me.location.tuple(), agent.me.forward.tuple(), agent.me.boost if agent.boost_amount != 'unlimited' else 100000, agent.me.local_velocity().x) game_info = (agent.best_shot_value, agent.boost_accel) if can_aerial: me_a = (me[0], agent.me.velocity.tuple(), agent.me.up.tuple(), me[1], 1 if agent.me.airborne else -1, me[2]) gravity = agent.gravity.tuple() max_aerial_height = 643 if len(agent.friends) == 0 and len( agent.foes) == 1 else math.inf min_aerial_height = 551 if max_aerial_height > 643 and agent.me.location.z >= 2044 - agent.me.hitbox.height * 1.1 else ( 0 if agent.boost_amount == 'unlimited' else 450) for ball_slice in slices: intercept_time = ball_slice.game_seconds time_remaining = intercept_time - agent.time if time_remaining <= 0: return ball_location = (ball_slice.physics.location.x, ball_slice.physics.location.y, ball_slice.physics.location.z) if abs(ball_location[1]) > 5212.75: return if can_jump: shot = virxrlcu.parse_slice_for_jump_shot(time_remaining - 0.1, *game_info, ball_location, *me) if shot['found'] == 1: return jump_shot(intercept_time, Vector(*shot['best_shot_vector'])) if can_double_jump and time_remaining >= 0.5: shot = virxrlcu.parse_slice_for_double_jump( time_remaining - 0.3, *game_info, ball_location, *me) if shot['found'] == 1: return double_jump(intercept_time, Vector(*shot['best_shot_vector'])) if can_aerial and not (min_aerial_height > ball_location[2] or ball_location[2] > max_aerial_height): shot = virxrlcu.parse_slice_for_aerial_shot( time_remaining, *game_info, gravity, ball_location, me_a) if shot['found'] == 1: return Aerial(intercept_time, Vector(*shot['best_shot_vector']), shot['fast'])
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