def shot_valid(agent, shot, target=None): # Returns True if the ball is still where the shot anticipates it to be if target is None: target = shot.ball_location threshold = agent.best_shot_value * 2 # First finds the two closest slices in the ball prediction to shot's intercept_time # threshold controls the tolerance we allow the ball to be off by slices = agent.ball_prediction_struct.slices soonest = 0 latest = len(slices)-1 while len(slices[soonest:latest+1]) > 2: midpoint = (soonest+latest) // 2 if slices[midpoint].game_seconds > shot.intercept_time: latest = midpoint else: soonest = midpoint # preparing to interpolate between the selected slices dt = slices[latest].game_seconds - slices[soonest].game_seconds time_from_soonest = shot.intercept_time - slices[soonest].game_seconds soonest = (slices[soonest].physics.location.x, slices[soonest].physics.location.y, slices[soonest].physics.location.z) slopes = (Vector(slices[latest].physics.location.x, slices[latest].physics.location.y, slices[latest].physics.location.z) - Vector(*soonest)) * (1/dt) # Determining exactly where the ball will be at the given shot's intercept_time predicted_ball_location = Vector(*soonest) + (slopes * time_from_soonest) # Comparing predicted location with where the shot expects the ball to be return target.dist(predicted_ball_location) < threshold
def tmcp_ball_check(self): shot_name = self.get_stack_name() shot_time = self.stack[ 0].intercept_time if shot_name != "short_shot" else 7 if shot_time > 1 and (shot_name != "Aerial" or shot_time > 3): is_short_shot = shot_name == "short_shot" for friend in self.friends: action = friend.tmcp_action if action is not None and action[ 'type'] == "BALL" and action['time'] != -1: if is_short_shot: self.clear() break action_time = action['time'] - self.time if action_time > 0: f_shot_vector = Vector(*action['direction']) time_dif = 0.5 if f_shot_vector.magnitude( ) != 0 and self.stack[0].shot_vector.angle( f_shot_vector) > 1 else 0.2 if (action_time < shot_time - time_dif and shot_time > self.enemy_time_to_ball + time_dif ) or (action_time > shot_time + time_dif and action_time < self.enemy_time_to_ball - time_dif): self.clear() break
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, me, time): me.reset_ctrl() if self.jumping or (self.jump_time == -1 and me.on_ground): if self.jump_time == -1: self.jump_type_fast = self.fast_aerial self.jumping = True self.jump_time = time self.counter = 0 jump_elapsed = time - self.jump_time if self.jump_type_fast: if jump_elapsed <= jump_max_duration: me.ctrl.jump = True else: self.counter += 1 if self.counter == 3: me.ctrl.jump = True self.dodging = True elif self.counter == 4: self.dodging = self.jumping = False self.jump_time = -1 elif jump_elapsed <= jump_max_duration: me.ctrl.jump = True else: self.jumping = False self.jump_time = -1 delta_x = self.target - me.location if delta_x.magnitude() > boost_accel: delta_x *= boost_accel / delta_x.magnitude() delta_xy = Vector( delta_x.x - me.velocity.x, delta_x.y - me.velocity.y, 1000 if (not self.jumping or not self.jump_type_fast) else 0) direction = delta_xy.normalize() if self.counter in {0, 4}: defaultPD(me, me.local(delta_xy), up=sign(math.sin(time)) * (-1 if me.index == 60 else 1) * (Vector() - me.location).flatten().normalize()) me.ctrl.throttle = 1 # only boost/throttle if we're facing the right direction if abs(me.forward.angle(delta_xy)) < 1 and (not self.jumping or not self.jump_type_fast): # tap boost to keep height if delta_x.z - me.velocity.z * 0.5 > 0: me.ctrl.boost = True # if the target is relatively far, hold boost even when we're higher than the target to keep moving if delta_x.z < 0 and me.forward.z < 0.5: me.ctrl.boost = True
def post_correction(ball_location, left_target, right_target): # this function returns target locations that are corrected to account for the ball's radius # If the left and right post swap sides, a goal cannot be scored # We purposely make this a bit larger so that our shots have a higher chance of success ball_radius = 120 goal_line_perp = (right_target - left_target).cross(Vector(z=1)) left = left_target + ((left_target - ball_location).normalize().cross(Vector(z=-1))*ball_radius) right = right_target + ((right_target - ball_location).normalize().cross(Vector(z=1))*ball_radius) left = left_target if (left-left_target).dot(goal_line_perp) > 0 else left right = right_target if (right-right_target).dot(goal_line_perp) > 0 else right swapped = (left - ball_location).normalize().cross(Vector(z=1)).dot((right - ball_location).normalize()) > -0.1 return left, right, swapped
def __init__(self, pitch=0, yaw=0, roll=0): CP = math.cos(pitch) SP = math.sin(pitch) CY = math.cos(yaw) SY = math.sin(yaw) CR = math.cos(roll) SR = math.sin(roll) # List of 3 vectors, each descriping the direction of an axis: Forward, Left, and Up self.data = (Vector(CP * CY, CP * SY, SP), Vector(CY * SP * SR - CR * SY, SY * SP * SR + CR * CY, -CP * SR), Vector(-CR * CY * SP - SR * SY, -CR * SY * SP + SR * CY, CP * CR)) self.forward, self.right, self.up = self.data
def circular_procession(self, packet: GameTickPacket, drones, start_time) -> StepResult: self.ball.update(packet) elapsed = packet.game_info.seconds_elapsed - start_time inactive_drones = max((elapsed - 4) / 0.48, 0) radian_spacing = 2 * math.pi / max(60 - inactive_drones, 16) adjusted_radius = radius - elapsed * 75 for i, drone in enumerate(drones): if i >= inactive_drones: if i < 60: progress = i * radian_spacing + elapsed * .25 target = [ adjusted_radius * math.sin(progress), adjusted_radius * math.cos(progress), 0 ] slow_to_pos(drone, target) continue if len(self.drone_aerials) == i: progress = i * radian_spacing + (elapsed + 2) * .25 target = Vector(adjusted_radius * math.sin(progress), adjusted_radius * math.cos(progress), 200 + i * 10) self.drone_aerials.append(Hover(target, i != 60)) self.drone_aerials[i].target.z += 0.1 self.drone_aerials[i].run(drone, packet.game_info.seconds_elapsed) if i == 60: break return StepResult(finished=inactive_drones > 61)
def backsolve(target, car, time, gravity): # Finds the acceleration required for a car to reach a target in a specific amount of time d = target - car.location dvx = ((d.x/time) - car.velocity.x) / time dvy = ((d.y/time) - car.velocity.y) / time dvz = (((d.z/time) - car.velocity.z) / time) - (gravity.z * time) return Vector(dvx, dvy, dvz)
def find_landing_plane(l: Vector, v: Vector, g: float): if abs(l.y) >= 5120 or (v.x == 0 and v.y == 0 and g == 0): return 5 times = [ -1, -1, -1, -1, -1, -1 ] # side_wall_pos, side_wall_neg, back_wall_pos, back_wall_neg, ceiling, floor if v.x != 0: times[0] = (4080 - l.x) / v.x times[1] = (-4080 - l.x) / v.x if v.y != 0: times[2] = (5110 - l.y) / v.y times[3] = (-5110 - l.y) / v.y if g != 0: # this is the vertex of the equation, which also happens to be the apex of the trajectory h = v.z / -g # time to apex k = v.z * v.z / -g # vertical height at apex # a is the current gravity... because reasons # a = g climb_dist = -l.z # if the gravity is inverted, the the ceiling becomes the floor and the floor becomes the ceiling... if g < 0: climb_dist += 2030 if k >= climb_dist: times[4] = vertex_quadratic_solve_for_x_min_non_neg( g, h, k, climb_dist) elif g > 0: climb_dist += 20 if k <= climb_dist: times[5] = vertex_quadratic_solve_for_x_min_non_neg( g, h, k, climb_dist) # this is necessary because after we reach our terminal velocity, the equation becomes linear (distance_remaining / terminal_velocity) terminal_velocity = (2300 - v.flatten().magnitude()) * sign(g) falling_time_until_terminal_velocity = (terminal_velocity - v.z) / g falling_distance_until_terminal_velocity = v.z * falling_time_until_terminal_velocity + -g * ( falling_time_until_terminal_velocity * falling_time_until_terminal_velocity) / 2. fall_distance = -l.z if g < 0: times[5] = get_landing_time( fall_distance + 20, falling_time_until_terminal_velocity, falling_distance_until_terminal_velocity, terminal_velocity, k, h, g) else: times[4] = get_landing_time( fall_distance + 2030, falling_time_until_terminal_velocity, falling_distance_until_terminal_velocity, terminal_velocity, k, h, g) return times.index(min(item for item in times if item >= 0))
def get_shot(self, target=None, weight=None, cap=None): if self.predictions['self_min_time_to_ball'] == 7: return if self.can_shoot is None or self.predictions['own_goal'] or ( self.playstyle is self.playstyles.Neutral and target is self.best_shot): if weight is None: weight = get_weight(self, target) can_aerial = self.aerials can_double_jump = self.double_jump can_jump = self.jump can_ground = self.ground_shot if len(self.friends) == 0: can_aerial = can_aerial and (self.predictions['own_goal'] or (self.me.location.z > 300 and self.me.airborne)) self_intercept_location = self.ball_prediction_struct.slices[ self.min_intercept_slice].physics.location self_intercept_location = Vector( abs(self_intercept_location.x), self_intercept_location.y * side(self.team)) can_double_jump = can_double_jump and ( self_intercept_location.x < 1300 or self_intercept_location.y > 3840) if not can_aerial and not can_double_jump and not can_jump and not can_ground: return if target is self.anti_shot and self.me.location.y * side( self.team) > 5120: target = None shot = find_shot(self, target, weight=weight, cap_=6 if cap is None else cap, can_aerial=can_aerial, can_double_jump=can_double_jump, can_jump=can_jump, can_ground=can_ground ) if target is not None else find_any_shot( self, cap_=3 if cap is None else cap, can_aerial=can_aerial, can_double_jump=can_double_jump, can_jump=can_jump, can_ground=can_ground) if shot is not None: return { "weight": weight, "intercept_time": shot.intercept_time, "is_best_shot": target is self.best_shot, "shot": shot }
def setup_circle_align(self, packet: GameTickPacket, drones, start_time) -> StepResult: self.ball.update(packet) self.game_interface.set_game_state( GameState(ball=BallState(physics=Physics( location=Vector3(drones[60].location.x, drones[60].location.y), velocity=Vector3(0, 0), angular_velocity=Vector3(*drones[60].raw_angular_velocity))))) radian_spacing = 2 * math.pi / 20 radian_spacing_v = 2 * math.pi / 10 for i, drone in enumerate(drones): if i == 60: self.drone_aerials[i].target = Vector(0, 0, 1000) else: # 0 & 1: center circle # 2, 3, 4, & 5: side circles group = i % 6 if group < 2: progress = (i // 6 * 2 + group) * radian_spacing self.drone_aerials[i].target = Vector( radius2 * math.sin(progress), radius2 * math.cos(progress), 1000) elif group < 4: progress = (i // 6 * 2 + (group - 2)) * radian_spacing Q = radius3 * math.sin(progress) adjusted_radius = radius2 + Q self.drone_aerials[i].target = Vector( adjusted_radius * math.sin(progress), adjusted_radius * math.cos(progress), 1000 + Q) else: progress = (i // 6 * 2 + (group - 4)) * radian_spacing Q = radius3 * math.sin(progress) adjusted_radius = radius2 - Q self.drone_aerials[i].target = Vector( adjusted_radius * math.sin(progress), adjusted_radius * math.cos(progress), 1000 - Q) self.drone_aerials[i].run(drone, packet.game_info.seconds_elapsed) if i == 60: break return StepResult(finished=True)
def valid_ceiling_shot(agent: VirxERLU, cap_=5): struct = agent.ball_prediction_struct if struct is None: return end_slice = math.ceil(cap_ * 60) slices = struct.slices[30:end_slice:2] if agent.me.location.x * side(agent.team) > 0: quadrilateral = (round(agent.foe_goal.right_post.flatten()), round(agent.foe_goal.left_post.flatten())) else: quadrilateral = (round(agent.foe_goal.left_post.flatten()), round(agent.foe_goal.right_post.flatten())) quadrilateral += ( Vector(0, 640), Vector(round(agent.me.location.x - (200 * sign(agent.me.location.x))), round(agent.me.location.y - (200 * side(agent.team)))), ) agent.polyline(quadrilateral + (quadrilateral[0], ), agent.renderer.team_color(alt_color=True)) for ball_slice in slices: intercept_time = ball_slice.game_seconds time_remaining = intercept_time - agent.time if time_remaining <= 0: return ball_location = Vector(ball_slice.physics.location.x, ball_slice.physics.location.y, ball_slice.physics.location.z) if ball_location.z < 642: continue if not point_inside_quadrilateral_2d(round(ball_location.flatten()), quadrilateral): continue return ball_location
def get_cap(agent: VirxERLU, cap_, get_aerial_cap=False, is_anti_shot=False): if agent.num_friends == 0 or not ( agent.me.minimum_time_to_ball > agent.friend_times[0].minimum_time_to_ball and is_anti_shot): foes = len( tuple(foe for foe in agent.foes if not foe.demolished and foe.location.y * side(agent.team) < agent.ball.location.y * side(agent.team) - 150)) if foes != 0 and agent.enemy_time_to_ball != 7: future_ball_location_slice = min( round(agent.enemy_time_to_ball * 1.15 * 60), agent.ball_prediction_struct.num_slices - 1) foe_factor = max( abs(agent.closest_foes[0].local_velocity().angle2D( agent.closest_foes[0].local_location(agent.ball.location))) * 4, 4) foe_intercept_location = agent.ball_prediction_struct.slices[ future_ball_location_slice].physics.location foe_intercept_location = Vector(foe_intercept_location.x, foe_intercept_location.y) cap_slices = round(cap_) * 60 for i, ball_slice in enumerate( agent.ball_prediction_struct. slices[future_ball_location_slice:cap_slices:2]): ball_loc = Vector(ball_slice.physics.location.x, ball_slice.physics.location.y) if foe_intercept_location.dist( ball_loc) >= agent.ball_radius * foe_factor + ( agent.me.hitbox.width * (2 + foes)): cap_ = (i - 1) / 60 break if not get_aerial_cap: return cap_ aerial_cap = agent.enemy_time_to_ball if agent.enemy_time_to_ball < cap_ and agent.num_friends < 2 and agent.num_foes != 0 else cap_ if agent.num_friends == 0 and not agent.is_own_goal and agent.num_foes != 0: aerial_cap /= 2 return cap_, aerial_cap
def __init__(self, name, team, index): super().__init__(name, team, index) self.debug_2d_bool = self.name == self.true_name self.debug_vector = Vector(0, 0) self.goalie = False self.jump = True self.double_jump = True self.ground_shot = True self.aerials = True
def find_slope(shot_vector, car_to_target): # Finds the slope of your car's position relative to the shot vector (shot vector is y axis) # 10 = you are on the axis and the ball is between you and the direction to shoot in # -10 = you are on the wrong side # 1 = you're about 45 degrees offcenter d = shot_vector.dot(car_to_target) e = abs(shot_vector.cross(Vector(z=1)).dot(car_to_target)) try: f = d / e except ZeroDivisionError: return 10*sign(d) return cap(f, -3, 3)
def in_field(point, radius): # determines if a point is inside the standard soccer field point = Vector(abs(point.x), abs(point.y), abs(point.z)) if point.x > 4080 - radius: return False elif point.y > 5900 - radius: return False elif point.x > 880 - radius and point.y > 5105 - radius: return False elif point.x > 2650 and point.y > -point.x + 8025 - radius: return False return True
def get_shot(self, target=None, weight=None, cap=None): if self.me.minimum_time_to_ball == 7: return if weight is None: weight = self.get_weight(target) can_aerial = self.aerials can_double_jump = self.double_jump can_jump = self.jump can_ground = self.ground_shot if self.num_friends == 0 and self.num_foes == 1: can_aerial = can_aerial and ( self.is_own_goal or (self.me.location.z > 300 and self.me.airborne) or target is self.best_shot or not self.can_any_foe_aerial()) if self.num_friends == 0: self_intercept_location = self.ball_prediction_struct.slices[ self.min_intercept_slice].physics.location self_intercept_location = Vector( abs(self_intercept_location.x), self_intercept_location.y * self.side) can_double_jump = can_double_jump and ( self_intercept_location.x < 1300 or self_intercept_location.y > 3840) if not can_aerial and not can_double_jump and not can_jump and not can_ground: return if target is self.anti_shot and self.me.location.y * self.side > 5120: target = None shot = find_shot( self, target, weight=weight, cap_=6 if cap is None else cap, can_aerial=can_aerial, can_double_jump=can_double_jump, can_jump=can_jump, can_ground=can_ground) if target is not None else find_any_shot( self, cap_=4 if cap is None else cap, can_aerial=can_aerial, can_double_jump=can_double_jump, can_jump=can_jump, can_ground=can_ground) if shot is not None: return shot
def render_time_predictions(self): enemy_intercept_location = self.ball_prediction_struct.slices[ self.future_ball_location_slice].physics.location enemy_intercept_location = Vector(enemy_intercept_location.x, enemy_intercept_location.y, enemy_intercept_location.z) if self.enemy_time_to_ball != 7: self.sphere(enemy_intercept_location, self.ball_radius, self.renderer.red()) self_intercept_location = self.ball_prediction_struct.slices[ self.min_intercept_slice].physics.location self_intercept_location = Vector(self_intercept_location.x, self_intercept_location.y, self_intercept_location.z) if self.me.minimum_time_to_ball != 7: self.sphere(self_intercept_location, self.ball_radius, self.renderer.green()) cap_location = self.ball_prediction_struct.slices[ round(get_cap(self, 6) * 60) - 1].physics.location cap_location = Vector(cap_location.x, cap_location.y, cap_location.z) self.sphere(cap_location, self.ball_radius, self.renderer.orange())
def ray_intersects_with_line(origin, direction, point1, point2): v1 = origin - point1 v2 = point2 - point1 v3 = Vector(-direction.y, direction.x) v_dot = v2.dot(v3) t1 = v2.cross(v1).magnitude() / v_dot if t1 < 0: return t2 = v1.dot(v3) / v_dot if 0 <= t1 and t2 <= 1: return t1
def defaultPD(agent, local_target, upside_down=False): # points the car towards a given local target. # Direction can be changed to allow the car to steer towards a target while driving backwards up = agent.me.local(Vector(z=-1 if upside_down else 1)) # where "up" is in local coordinates target_angles = ( math.atan2(local_target.z, local_target.x), # angle required to pitch towards target math.atan2(local_target.y, local_target.x), # angle required to yaw towards target math.atan2(up.y, up.z) # angle required to roll upright ) # Once we have the angles we need to rotate, we feed them into PD loops to determing the controller inputs agent.controller.steer = steerPD(target_angles[1], 0) agent.controller.pitch = steerPD(target_angles[0], agent.me.angular_velocity.y/4) agent.controller.yaw = steerPD(target_angles[1], -agent.me.angular_velocity.z/4) agent.controller.roll = steerPD(target_angles[2], agent.me.angular_velocity.x/2) # Returns the angles, which can be useful for other purposes return target_angles
def backcheck(self, simple=False, clear_on_valid=False): if self.is_clear() or clear_on_valid: if self.playstyle is not self.playstyles.Defensive and not simple and self.ball.location.y * side( self.team) < 2560: if clear_on_valid: self.clear() self.push(dynamic_backcheck()) elif self.me.location.dist(self.friend_goal.location + Vector(y=-250 * side(self.team))) > 500: if clear_on_valid: self.clear() self.push(retreat()) else: return False return True
def initialize_agent(self): super().initialize_agent() self.cheating = self.true_name == "VirxEMB" if self.cheating: self.boost_amount = "unlimited" self.playstyles = Playstyle self.playstyle = self.playstyles.Neutral self.predictions = { "closest_enemy": 0, "team_from_goal": (), "team_to_ball": (), "self_from_goal": 0, "self_to_ball": 0, "was_down": False } self.closest_foes = [] self.friend_times = [] self.profiler_threshold = 0.8 self.profiler_loss = 0.005 self.profiler_gain = 0.21 self.profiler_last_save = 0 self.last_ball_touch_time = -1 self.unpause_timer = -1 self.enemy_time_to_ball = 7 self.max_shot_weight = 4 self.min_intercept_slice = 180 self.own_goal = {"location": Vector(), "slice": -1} self.is_own_goal = False self.is_goal = False self.side = side(self.team)
def update_predictions(self): is_other_tick = self.odd_tick % 2 == 0 is_forth_tick = self.odd_tick == 0 if self.num_foes > 0: foe_distances = tuple( self.ball.location.flat_dist(foe.location) for foe in self.alive_foes) if len(foe_distances) > 0: if is_forth_tick or len(self.closest_foes) != self.num_foes: for foe in self.foes: foe.minimum_time_to_ball = self.time_to_ball( foe) if not foe.demolished else 7 self.closest_foes = sorted( [foe for foe in self.foes], key=lambda foe: foe.minimum_time_to_ball) self.enemy_time_to_ball = self.closest_foes[ 0].minimum_time_to_ball self.predictions['closest_enemy'] = min(foe_distances) else: self.closest_foes = [] self.enemy_time_to_ball = 7 self.predictions['closest_enemy'] = math.inf else: self.closest_foes = [] self.enemy_time_to_ball = 7 self.predictions['closest_enemy'] = math.inf self.future_ball_location_slice = min( round(self.enemy_time_to_ball * 60), self.ball_prediction_struct.num_slices - 1) self.dbg_2d( f"Predicted enemy time to ball: {round(self.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 self.num_friends > 0: teammates = tuple(itertools.chain(self.alive_friends, [self.me])) self.predictions["team_from_goal"] = sorted( tuple( self.friend_goal.location.flat_dist(teammate.location) for teammate in teammates)) self.predictions["team_to_ball"] = sorted( tuple( self.ball.location.flat_dist(teammate.location) for teammate in teammates)) if self.cheating: if is_other_tick: cars = {self.index: CarState(boost_amount=100)} self.set_game_state(GameState(cars=cars)) self.me.boost = 100 if is_forth_tick: self.me.minimum_time_to_ball = self.time_to_ball(self.me) self.min_intercept_slice = min( round(self.me.minimum_time_to_ball * 60), self.ball_prediction_struct.num_slices - 1) self.dbg_2d( f"Minimum time to ball: {round(self.me.minimum_time_to_ball, 1)}") for friend in self.friends: if friend.demolished: friend.minimum_time_to_ball = 7 continue action = friend.tmcp_action if action is not None: action_type = action['type'] if action_type in {"BALL", "READY"} and action['time'] != -1: time = action['time'] - self.time friend.minimum_time_to_ball = time if time > 0 else 7 else: friend.minimum_time_to_ball = 7 elif is_forth_tick: mttb = self.time_to_ball(friend) * abs( friend.local_velocity().angle2D( friend.local_location(self.ball.location))) if mttb > 6: mttb = 7 friend.minimum_time_to_ball = mttb self.friend_times = sorted( [friend for friend in self.alive_friends], key=lambda friend: friend.minimum_time_to_ball) if is_other_tick: is_own_goal = False is_goal = False if self.ball_prediction_struct is not None: own_goal_slice = -1 for i, ball_slice in enumerate( self.ball_prediction_struct.slices[30::12]): location = ball_slice.physics.location.y * self.side if location >= 5212.75: is_own_goal = True own_goal_slice = i break if location <= -5212.75: is_goal = True break self.own_goal = {"location": Vector(), "slice": -1} if is_own_goal: for i, ball_slice in enumerate( self.ball_prediction_struct.slices[::15]): if ball_slice.physics.location.y * self.side >= 5200: self.own_goal["location"] = Vector.from_vector( ball_slice.physics.location) self.own_goal["slice"] = i break if self.cheating and not self.shooting: tp_own_goal = None for ball_slice in self.ball_prediction_struct.slices[: 30]: if ball_slice.physics.location.y * self.side >= 5200: tp_own_goal = ball_slice.physics.location break if tp_own_goal is not None: cars = { self.index: CarState( Physics(location=Vector3( tp_own_goal.x, tp_own_goal.y, tp_own_goal.z), velocity=Vector3(0, 0, 0))) } self.set_game_state(GameState(cars=cars)) if is_own_goal and not self.is_own_goal: self.send_quick_chat(QuickChats.CHAT_EVERYONE, QuickChats.Compliments_NiceShot) if is_goal and not self.is_goal: self.send_quick_chat(QuickChats.CHAT_EVERYONE, QuickChats.Reactions_Wow) self.is_own_goal = is_own_goal self.is_goal = is_goal
def goto_nearest_boost(self, only_small=False, clear_on_valid=False): if self.is_clear() or clear_on_valid: self.send_quick_chat(QuickChats.CHAT_TEAM_ONLY, QuickChats.Information_NeedBoost) ball_slice = self.ball_prediction_struct.slices[min( round(self.future_ball_location_slice * 1.1), 6)].physics ball = Vector(ball_slice.location.x, ball_slice.location.y, ball_slice.location.z) ball_v = Vector(ball_slice.velocity.x, ball_slice.velocity.y, ball_slice.velocity.z) ball_y = ball.y * side(self.team) if not only_small: if len(self.friends) > 0: large_boosts = ( boost for boost in self.boosts if boost.large and boost.active and ( (self.playstyle is self.playstyles.Offensive and boost.location.y * side(self.team) < -3000) or (self.playstyle is self.playstyles.Neutral and boost.location.y * side(self.team) > -100) or (self.playstyle is self.playstyles.Defensive and boost.location.y * side(self.team) > 3000))) else: if (ball_v.angle2D(self.foe_goal.location - ball) < 1 and ball_y > 0) or abs( ball.x) < 900 or self.predictions['own_goal']: large_boosts = None else: ball_x = sign(ball.x) large_boosts = ( boost for boost in self.boosts if boost.large and boost.active and ( boost.location.y * side(self.team) > ball_y - 50) and sign(boost.location.x) == ball_x) if large_boosts is not None: closest = peek_generator(large_boosts) if closest is not None: closest_distance = closest.location.flat_dist( self.me.location) for item in large_boosts: item_distance = item.location.flat_dist( self.me.location) if item_distance is closest_distance: if item.location.flat_dist( self.me.location ) < closest.location.flat_dist( self.me.location): closest = item closest_distance = item_distance elif item_distance < closest_distance: closest = item closest_distance = item_distance if clear_on_valid: self.clear() self.push(goto_boost(closest)) return True if (ball_v.angle2D(self.foe_goal.location - ball) < 1 and ball_y > 0) or self.predictions['own_goal']: return False ball_x = sign(ball.x) small_boosts = (boost for boost in self.boosts if not boost.large and boost.active and boost.location.y * side(self.team) > ball_y - 50 and sign(boost.location.x) == ball_x) closest = peek_generator(small_boosts) if closest is not None: closest_distance = closest.location.flat_dist( self.me.location) + (closest.location.flat_dist( self.friend_goal.location) / 400) for item in small_boosts: item_distance = item.location.flat_dist( self.me.location) + (item.location.flat_dist( self.friend_goal.location) / 400) if item_distance < closest_distance: item_loc = item.location.y * side(self.team) if (self.playstyle is self.playstyles.Offensive and item_loc < -2560 ) or ( self.playstyle is self.playstyles.Neutral and item_loc < -100) or ( self.playstyle is self.playstyles.Defensive and item_loc > 1280): closest = item closest_distance = item_distance if clear_on_valid: self.clear() self.push(goto_boost(closest)) return True return False
def run(self): # predictions self.update_predictions() # act on the predictions if not self.kickoff_done: if self.is_clear(): if len(self.friends) > 0: if almost_equals(self.predictions['team_to_ball'][0], self.predictions['self_to_ball'], 5): self.offensive_kickoff() elif almost_equals(self.predictions['team_to_ball'][-1], self.predictions['self_to_ball'], 5): self.defensive_kickoff() elif len(self.foes) == 0 or almost_equals( self.predictions['closest_enemy'], self.predictions['self_to_ball'], 10): self.offensive_kickoff() else: self.defensive_kickoff() return if self.can_shoot is None: self.dbg_3d("Can shoot: 0") else: self.dbg_3d( f"Can shoot: {round(3 - (self.time - self.can_shoot), 2)}") enemy_intercept_location = self.ball_prediction_struct.slices[ self.future_ball_location_slice].physics.location enemy_intercept_location = Vector(enemy_intercept_location.x, enemy_intercept_location.y, enemy_intercept_location.z) if self.predictions['enemy_time_to_ball'] != 7: self.sphere(enemy_intercept_location, 92.75, self.renderer.red()) self_intercept_location = self.ball_prediction_struct.slices[ self.min_intercept_slice].physics.location self_intercept_location = Vector(self_intercept_location.x, self_intercept_location.y, self_intercept_location.z) if self.predictions['self_min_time_to_ball'] != 7: self.sphere(self_intercept_location, 92.75, self.renderer.green()) if side(self.team) * enemy_intercept_location.y >= self.defense_switch[ self.playstyle] or self.predictions['own_goal']: for shot in self.defensive_shots: self.line(*shot, self.renderer.team_color(alt_color=True)) self.dbg_3d("(Defending)") if self.predictions['enemy_time_to_ball'] > self.predictions[ 'self_min_time_to_ball'] + ( 3 if self.shooting else 1) and self.me.boost < 36 and not self.is_clear( ) and self.get_stack_name() == 'goto_boost': return ball_loc = self_intercept_location * side(self.team) self_loc = self.me.location * side(self.team) # This is a list of all tm8s that are onside team_to_ball = [ car.location.flat_dist(self.ball.location) for car in self.friends if car.location.y * side(self.team) >= ball_loc.y + 95 and abs(car.location.x) < abs(self.ball.location.x) - 320 ] self_to_ball = self.me.location.flat_dist(self.ball.location) team_to_ball.append(self_to_ball) team_to_ball.sort() if len(team_to_ball) == 1 or team_to_ball[math.ceil( len(team_to_ball) / 2)] + 10 > self_to_ball: self.can_shoot = None if self.can_shoot is None and (self.is_clear() or self.odd_tick == 0): if self_loc.y > ball_loc.y + 95 and self.smart_shot( self.best_shot, cap=4): return if ball_loc.y - self_loc.y > ( ball_loc.x - self_loc.x ) * 1.5 and ( self.predictions['own_goal'] or (len(team_to_ball) > 1 and team_to_ball[math.ceil( len(team_to_ball) / 2)] + 10 > self_to_ball) or (len(team_to_ball) == 1 and self_to_ball < 2560) or (abs(ball_loc.x) < 900 and ball_loc.y > 1280) ) and team_to_ball[0] is self_to_ball and self.smart_shot( self.anti_shot, weight=self.max_shot_weight - 3, cap=4): return if self_loc.y > ball_loc.y + 95: for i, shot in enumerate( self.defensive_shots if self.predictions['self_min_time_to_ball'] * 2 < self.predictions['enemy_time_to_ball'] else self.defensive_shots[1:]): shot_weight = get_weight(self, index=i) if self.shooting and shot_weight < self.shot_weight: break shot = self.get_shot(shot, weight=shot_weight, cap=4) if shot is not None: if self.shooting: self.upgrade_shot(shot) else: self.shoot_from(shot, clear_on_valid=True) return if self.smart_shot(self.anti_shot, weight=self.max_shot_weight - 3, cap=3): return if not self.me.airborne and (not self.shooting or self.shot_weight == -1): if self.predictions['enemy_time_to_ball'] > self.predictions[ 'self_min_time_to_ball'] + ( 3 if self.shooting else 1) and self.me.boost < 36 and ( self.is_clear() or self.get_stack_name() != 'goto_boost' ) and self.goto_nearest_boost(clear_on_valid=True): return if not self.predictions[ 'own_goal'] and self_loc.y <= ball_loc.y - 50 and not self.is_clear( ) and self.get_stack_name() == 'goto_boost' and abs( ball_loc.x) > 1024 and self.backcheck( clear_on_valid=True): return if self.is_clear() and not self.backcheck(): 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 return if self.me.airborne and self.is_clear(): self.push(recovery()) if not self.is_clear() and self.get_stack_name( ) == "short_shot" and self.me.location.y * side( self.team) < self.ball.location.y * side(self.team): self.clear() self.playstyles_switch[self.playstyle]() ""
def init(self): foe_team = -1 if self.team == 1 else 1 team = -foe_team # note that the second value may be positive or negative self.kickoff_left = (-2048 * self.side, 2560) self.kickoff_right = (2048 * self.side, 2560) self.kickoff_back = (0, 4608) self.kickoff_back_left = (-256 * self.side, 3840) self.kickoff_back_right = (256 * self.side, 3840) self.offensive_shots = ((Vector(foe_team * 893, foe_team * 5120, min(self.ball_radius * 2, 321.3875)), Vector( foe_team * -893, foe_team * 5120, max(642.775 - self.ball_radius, 321.3875))), (Vector(foe_team * 893, foe_team * 5120, 321.3875), Vector(foe_team * 893, foe_team * 6000, 321.3875)), (Vector(foe_team * -893, foe_team * 5120, 321.3875), Vector(foe_team * -893, foe_team * 6000, 321.3875))) self.defensive_shots = (self.offensive_shots[0], (Vector( 4096, foe_team * 3968, 1900), Vector(2944, foe_team * 5120, 1900)), (Vector(-4096, foe_team * 3968, 1900), Vector(-2944, foe_team * 5120, 1900))) self.best_shot = (Vector(foe_team * 793, foe_team * 5213, min(self.ball_radius * 3, 321.3875)), Vector(-foe_team * 793, foe_team * 5213, max(642.775 - self.ball_radius * 2, 321.3875))) self.anti_shot = (Vector(-team * 1536, team * 5120, 1285.55), Vector(team * 1536, team * 5120, 1285.55)) if self.name in {"VirxEB", "VirxEMB"}: print(f"{self.name}: Check me out at https://www.virxcase.dev!!!")
def is_inside_turn_radius(turn_rad, local_target, steer_direction): # turn_rad is the turn radius local_target = local_target.flatten() circle = Vector(y=-steer_direction * turn_rad) return circle.dist(local_target) < turn_rad
def in_field(point, radius): # determines if a point is inside the standard soccer field point = Vector(abs(point.x), abs(point.y), abs(point.z)) return not (point.x > 4080 - radius or point.y > 5900 - radius or (point.x > 880 - radius and point.y > 5105 - radius) or (point.x > 2650 and point.y > -point.x + 8025 - radius))
def goto_nearest_boost(self, only_small=False, clear_on_valid=False): self.send_quick_chat(QuickChats.CHAT_TEAM_ONLY, QuickChats.Information_NeedBoost) # principle # if we can get to the boost and then back to into position before out opponent can get to the ball and then the ball can get to our net, the boost is marked as a viable option # out of all the viable options, we pick the option that's closest to our own car # however, we must respect the only_small clause as well as prioritize large boosts over the small pads, as well as rule out pads that our tm8s are going for (provided by their TMCP packets) min_foe = self.closest_foes[0] if self.num_foes > 0 else None ignore_foe = min_foe is None or min_foe.minimum_time_to_ball == 7 min_seconds = math.inf enemy_post = retreat().get_target(self) shadow_routine = shadow() friend_post = shadow_routine.get_target( self) if shadow_routine.is_viable(self) else enemy_post if self.is_own_goal: return if not ignore_foe: enemy_intercept_location = self.ball_prediction_struct.slices[ self.future_ball_location_slice].physics.location enemy_intercept_location = Vector(enemy_intercept_location.x, enemy_intercept_location.y, enemy_intercept_location.z) if enemy_intercept_location.flat_dist(enemy_post) < 2560: return dist_to_goal = enemy_intercept_location.dist(enemy_post) min_foe_speed = min_foe.location.dist( enemy_intercept_location) / self.enemy_time_to_ball min_seconds = dist_to_goal * 1.2 / min_foe_speed min_seconds = dist_to_goal / (min_foe_speed * (1 - (0.0305 / 120))** (120 * min_seconds)) foe_factor = max( abs(min_foe.local_velocity().angle2D( min_foe.local_location(self.ball.location))), 1) min_seconds += min_foe.minimum_time_to_ball * foe_factor claimed_boosts = tuple(friend.tmcp_action['target'] for friend in self.alive_friends if friend.tmcp_action is not None and friend.tmcp_action['type'] == "BOOST") active_unclaimed_boosts = tuple( boost for boost in self.boosts if boost.active and boost.index not in claimed_boosts) big_pads = (boost for boost in active_unclaimed_boosts if boost.large) small_pads = (boost for boost in active_unclaimed_boosts if not boost.large) car_mag = self.me.velocity.magnitude() car_mag_adj = (car_mag + 2300) / 2 car_speed = self.me.local_velocity().x turn_rad = turn_radius(car_mag) car_z = self.me.location.z - self.me.hitbox.height / 2 for boosts in ((big_pads if not only_small else ()), small_pads): viable_boosts = [] for boost in boosts: angle = boost.location.angle2D(self.me.forward) time = (angle * turn_rad / car_mag) if car_mag > 400 else (1.8 * (angle / math.pi)) dist = boost.location.flat_dist( self.me.location) + (car_z - 17) if dist > 1280: time += (dist - 1280) / car_mag_adj dist = 1280 dist += boost.location.flat_dist(friend_post) time += dist / ((car_mag + 1410) / 2) if time < min_seconds: viable_boosts.append({"pad": boost, "time": time}) if len(viable_boosts) > 0: if clear_on_valid: self.clear() self.push( goto_boost( min(viable_boosts, key=lambda boost: boost["time"])["pad"]))
def init(self): foe_team = -1 if self.team == 1 else 1 team = -foe_team # note that the second value may be positive or negative self.kickoff_left = (-2048 * side(self.team), 2560) self.kickoff_right = (2048 * side(self.team), 2560) self.kickoff_back = (0, 4608) self.kickoff_back_left = (-256 * side(self.team), 3840) self.kickoff_back_right = (256 * side(self.team), 3840) self.offensive_shots = ((Vector(foe_team * 893, foe_team * 5120, 321.3875), Vector(foe_team * -893, foe_team * 5120, 321.3875)), (Vector(foe_team * 893, foe_team * 5120, 321.3875), Vector(foe_team * 893, foe_team * 6000, 321.3875)), (Vector(foe_team * -893, foe_team * 5120, 321.3875), Vector(foe_team * -893, foe_team * 6000, 321.3875))) self.defensive_shots = (self.offensive_shots[0], (Vector( 4096, foe_team * 3968, 1900), Vector(2944, foe_team * 5120, 1900)), (Vector(-4096, foe_team * 3968, 1900), Vector(-2944, foe_team * 5120, 1900))) self.best_shot = (Vector(foe_team * 793, foe_team * 5213, 321.3875), Vector(-foe_team * 793, foe_team * 5213, 321.3875)) self.anti_shot = (Vector(-team * 2048, team * 5120, 2000), Vector(team * 2048, team * 5120, 2000)) self.max_shot_weight = 4 self.playstyles_switch = { self.playstyles.Defensive: self.defend, self.playstyles.Neutral: self.neutral, self.playstyles.Offensive: self.attack } self.defense_switch = { self.playstyles.Defensive: 1920, self.playstyles.Neutral: 1280, self.playstyles.Offensive: 640 }