def good_position(spot: Location) -> float: """Score how good it is to be in the given location.""" score = 0 ball = Match.predict_ball(dt=2) # Wrong side of the ball? score -= max(0, spot.to(ball).dot(Match.field.backward) / 1000) # Differential distance to ball: score += clamp( Match.opponent_car.distance(Match.ball) - spot.distance(Match.ball) / 2000) return clamp(score, 0, 1)
def ball_on_wall_curve() -> float: ball = Match.ball field = Match.field x_dist = field.to_side_wall - abs(ball.x) - ball.radius y_dist = field.to_end_wall - abs(ball.y) - ball.radius z_dist = ball.z - ball.radius return clamp(1 - max(z_dist / 500, min(x_dist, y_dist) / 500), 0, 1)
def ball_distance_advantage(max_distance: float = 1000) -> float: return 0.5 + clamp( Match.opponent_car.distance(Match.ball) - Match.agent_car.distance(Match.ball), -max_distance, max_distance, ) / (2 * max_distance)
def ball_rolling_on_ground() -> float: return clamp( 1 - (Match.ball.z - Match.ball.radius) / 200 - abs(Match.ball.velocity.z) / 500, 0, 1, )
def rolling_into_corner() -> float: """Is the ball rolling into a corner (toward the goal)?""" ball = Match.ball field = Match.field x_dist = field.to_side_wall - abs(ball.x) - ball.radius score = 1 score -= x_dist / 500 score -= abs(ball.velocity.x) / 500 score -= max(0, 500 - abs(ball.velocity.y)) / 500 return clamp(score, 0, 1)
def desired_ball_direction(ball: Ball = None) -> Vec3: """Returns a normalized vector pointing the way we want the ball to go. On offense it'll be into the opponent's goal; on defense, it'll be away from ours.""" if ball is None: ball = Match.ball t = clamp( Match.field.own_goal_center.to(ball).dot(Match.field.forward) / 2000, 0, 1) away_from_own_goal = (Match.field.left if ball.dot(Match.field.left) > 0 else Match.field.right) toward_opp_goal = ball.to(Match.field.opp_goal_center).normalized() direction = away_from_own_goal.lerp(toward_opp_goal, t) # draw.line_3d(ball, ball + direction * 500, "red") return direction
def predict(self, dt: float) -> Ball: """Return a Ball instance predicted `dt` match seconds into the future.""" coarse_scan = 16 # step size for initial scan of slices t = clamp( self.game_time + dt, self.prediction.slices[0].game_seconds, self.prediction.slices[self.prediction.num_slices - 1].game_seconds, ) index = 0 for index in range(0, self.prediction.num_slices - coarse_scan, coarse_scan): if self.prediction.slices[index + coarse_scan].game_seconds > t: break while self.prediction.slices[index].game_seconds < t: index += 1 phys = self.prediction.slices[index].physics return Ball(phys=phys, time=self.prediction.slices[index].game_seconds)
def high_ball() -> float: score = 0 score += (Match.ball.z - Match.ball.radius) / 2000 score += Match.ball.velocity.z / 2000 return clamp(score, 0, 1)
def roll(self, value: float): self.controls.roll = clamp(value)
def pitch(self, value: float): self.controls.pitch = clamp(value)
def yaw(self, value: float): self.controls.yaw = clamp(value)
def throttle(self, value: float): self.controls.throttle = clamp(value)
def steer(self, value: float): self.controls.steer = clamp(value)