def shot_valid(agent, shot, threshold=45, target=None): # Returns True if the ball is still where the shot anticipates it to be if target is None: target = shot.ball_location # 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.predictions['ball_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 handle_panic(self, far_panic=5100, close_panic=1000): if self.ball_to_goal < far_panic or self.predictions['own_goal']: for d_shots in self.defensive_shots: # for d_shot in d_shots: self.line(*d_shots, self.renderer.team_color(alt_color=True)) if not self.shooting: self.panic = True for shot in self.defensive_shots: if self.smart_shot(shot, cap=4): return if self.ball_to_goal < close_panic: if not self.smart_shot((self.friend_goal.right_post, self.friend_goal.left_post), weight=0, cap=2): if abs(self.me.location.y) > abs(self.ball.location.y): self.is_clear() self.push(short_shot(Vector(z=320))) elif self.is_clear(): team = -1 if self.team == 0 else 1 self.push( goto( Vector(y=self.ball.location.y + (team * 200)))) else: self.panic = False
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 * -1 * time) return Vector(dvx, dvy, dvz)
def backcheck(self, simple=False): if self.is_clear(): self_from_goal = self.predictions['self_from_goal'] if self_from_goal > 500: if self.playstyle != self.playstyles.Defensive and not simple and ( (self.team == 0 and self.ball.location.y > 2048) or (self.team == 1 and self.ball.location.y < -2048)): bc_x = 0 bc_y = 0 ball_loc = self.ball.location.y * side(not self.team) if ball_loc > 2560 * side(not self.team): if self.ball.location.x > 2048: bc_x = 2048 elif self.ball.location.x < -2048: bc_x = -2048 if len(self.predictions['teammates_from_goal'] ) > 0 and max( self.predictions['teammates_from_goal'] ) is self_from_goal: bc_y = max(1024, ball_loc - 1000) * side(not self.team) self.push(goto(Vector(bc_x, bc_y, 17), self.ball.location)) else: self.push(goto(self.friend_goal.location)) return True return False return 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 purposly 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 = True if (left - ball_location).normalize().cross(Vector( z=1)).dot((right - ball_location).normalize()) > -0.1 else False return left, right, swapped
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.0 = 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 defaultPD(agent, local_target, direction=1.0): # points the car towards a given local target. # Direction can be changed to allow the car to steer towards a target while driving backwards local_target *= direction up = agent.me.local(Vector(z=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) * direction 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