def utility_score(self, bot) -> float: car = bot.info.my_car ball = bot.info.ball car_to_ball = car.pos - ball.pos bouncing_b = ball.pos.z > 130 or abs(ball.vel.z) > 300 if not bouncing_b: return 0 dist_01 = clip01(1 - norm(car_to_ball) / 3000) head_dir = lerp(Vec3(0, 0, 1), car.forward, 0.13) ang = angle_between(head_dir, car_to_ball) ang_01 = clip01(1 - ang / (math.pi / 2)) xy_speed_delta_01 = lin_fall(norm(xy(car.vel - ball.vel)), 800) obj_bonus = { Objective.UNKNOWN: 0.8, Objective.GO_FOR_IT: 1.0, Objective.FOLLOW_UP: 0, Objective.ROTATING: 0, Objective.SOLO: 1.0, }[car.objective] return obj_bonus * clip01(xy_speed_delta_01 * ang_01 * dist_01 + self.is_dribbling * self.extra_utility_bias)
def exec(self, bot): goal_to_ball = bot.info.ball.pos - bot.info.own_goal.pos goal_to_car = bot.info.my_car.pos - bot.info.own_goal.pos car_prj = proj_onto(goal_to_car, goal_to_ball) target = lerp(bot.info.own_goal.pos + car_prj, lerp(bot.info.ball.pos, bot.info.opp_goal.pos, 0.4), 0.08) draw.line(bot.info.my_car.pos, target, bot.renderer.purple()) speed = max( (norm(bot.info.my_car.pos - bot.info.ball.pos) - 900) * 0.6, 100) return bot.drive.towards_point( bot, target, target_vel=speed, slide=True, boost_min=0, can_keep_speed=norm(bot.info.my_car.pos - bot.info.ball.pos) > 3000, can_dodge=True, wall_offset_allowed=125)
def utility_score(self, bot) -> float: car = bot.info.my_car if len(bot.info.teammates) == 0: team_committed01 = 0 no_defence01 = 1 else: mates = bot.info.teammates sum_pos = mates[0].pos + mates[0].vel * 0.5 for mate in mates[1:]: sum_pos += mate.pos + mate.vel * 0.5 avg_pos = sum_pos / len(mates) team_committed01 = clip01( norm(avg_pos - bot.info.own_goal.pos) / Field.LENGTH2) no_defence01 = clip01( argmin(mates, lambda mate: norm(mate.pos - bot.info.own_goal.pos))[1] / 1000) dist_to_ball01 = clip01( norm(car.pos - bot.info.ball.pos) / Field.LENGTH2) obj_bonus = { Objective.UNKNOWN: 1.0, Objective.GO_FOR_IT: 0, Objective.FOLLOW_UP: 0, Objective.ROTATING: 1.0, Objective.SOLO: 1.0, }[car.objective] return 0.85 * team_committed01 * dist_to_ball01 * no_defence01 * obj_bonus
def run(self, bot) -> SimpleControllerState: car = bot.info.my_car ball = bot.info.ball dist_ball = norm(car.pos - ball.pos) target_pos = bot.analyzer.ideal_follow_up_pos dist_target = norm(car.pos - target_pos) return bot.drive.towards_point(bot, bot.analyzer.ideal_follow_up_pos, target_vel=dist_target * 0.8, slide=dist_target > 1000, can_dodge=False)
def utility_score(self, bot) -> float: car = bot.info.my_car ball = bot.info.ball my_hit_time = predict.time_till_reach_ball(car, ball) ball_soon = predict.ball_predict(bot, min(my_hit_time, 1.0)) close_to_ball_01 = clip01(1.0 - norm(car.pos - ball_soon.pos) / 3500) ** 0.5 # FIXME Not great reachable_ball = predict.ball_predict(bot, predict.time_till_reach_ball(bot.info.my_car, ball)) xy_ball_to_goal = xy(bot.info.opp_goal.pos - reachable_ball.pos) xy_car_to_ball = xy(reachable_ball.pos - bot.info.my_car.pos) in_position_01 = ease_out(clip01(dot(xy_ball_to_goal, xy_car_to_ball)), 0.5) # Chase ball right after kickoff. High right after kickoff kickoff_bias01 = max(0, 1 - bot.info.time_since_last_kickoff * 0.3) * float(bot.info.my_car.objective == Objective.UNKNOWN) obj_bonus = { Objective.UNKNOWN: 1, Objective.GO_FOR_IT: 1, Objective.FOLLOW_UP: 0, Objective.ROTATING: 0, Objective.SOLO: 1, }[bot.info.my_car.objective] return clip01(close_to_ball_01 * in_position_01 + kickoff_bias01) * obj_bonus
def find_landing_orientation(car: Car, num_points: int) -> Mat33: """ dummy = DummyObject(car) trajectory = [Vec3(dummy.pos)] for i in range(0, num_points): fall(dummy, 0.0333) # Apply physics and let car fall through the air trajectory.append(Vec3(dummy.pos)) up = dummy.pitch_surface_normal() if norm(up) > 0.0 and i > 10: up = normalize(up) forward = normalize(dummy.vel - dot(dummy.vel, up) * up) left = cross(up, forward) return Mat33.from_columns(forward, left, up) return Mat33(car.rot) """ forward = normalize(xy( car.vel)) if norm(xy(car.vel)) > 20 else car.forward up = Vec3(z=1) left = cross(up, forward) return Mat33.from_columns(forward, left, up)
def exec(self, bot) -> SimpleControllerState: car = bot.info.my_car # Somehow we didn't find a good pad if self.target_pad is None: self.done = True return SimpleControllerState() # Since this maneuver is stopped early, we make sure some time pass before announcing. This prevents spam if not self.announced_in_tmcp and self.time_begin + 0.1 < bot.info.time: self.announced_in_tmcp = True bot.send_tmcp( TMCPMessage.boost_action(bot.team, bot.index, self.target_pad.index)) # End maneuver when almost there or pad becomes inactive car_to_pad = self.target_pad.pos - car.pos vel = proj_onto_size(car.vel, car_to_pad) dist = norm(car_to_pad) if dist < 50 + vel * 0.2 or car.boost > 50 or not self.target_pad.is_active: self.done = True draw.line(car.pos, self.target_pad.pos, bot.renderer.yellow()) return bot.drive.towards_point(bot, self.target_pad.pos, target_vel=self.target_vel, slide=True, boost_min=0, can_dodge=self.target_pad.is_big)
def find_landing_orientation(car: Car) -> Mat33: # FIXME: This uses a cheap approximation of the walls to find landing orientation obj = DummyObject(car) prev_pos = obj.pos for i in range(100): predict.fall(obj, 0.1) # Checking for intersections for plane in Field.SIDE_WALLS_AND_GROUND: if intersects_plane(prev_pos, obj.pos, plane): # Bingo! fall_dir = normalize(obj.pos - prev_pos) left = -cross(fall_dir, plane.normal) forward = -cross(plane.normal, left) return Mat33.from_columns(forward, left, plane.normal) prev_pos = obj.pos # No wall/ground intersections found in fall # Default to looking in direction of velocity, but upright forward = normalize(xy( car.vel)) if norm(xy(car.vel)) > 20 else car.forward up = Vec3(z=1) left = cross(up, forward) return Mat33.from_columns(forward, left, up)
def time_till_reach_ball(car, ball): """ Rough estimate about when we can reach the ball in 2d. """ car_to_ball = xy(ball.pos - car.pos) dist = norm(car_to_ball) - Ball.RADIUS / 2 vel_c_f = proj_onto_size(car.vel, car_to_ball) vel_b_f = proj_onto_size(ball.vel, car_to_ball) vel_c_amp = lerp(vel_c_f, norm(car.vel), 0.58) vel_f = vel_c_amp - vel_b_f dist_long_01 = clip01(dist / 10_000.0) time_normal = dist / max(220, vel_f) time_long = dist / max(norm(car.vel), 1410) time = lerp(time_normal, time_long, dist_long_01) arrive_time = time * 0.85 # Combine slightly with old prediction to negative rapid changes result = lerp(arrive_time, car.last_expected_time_till_reach_ball, 0.22) car.last_expected_time_till_reach_ball = arrive_time return result
def exec(self, bot) -> SimpleControllerState: car = bot.info.my_car self.done = norm(car.pos) < 1100 # End when getting close to ball (approx at boost pad) curve_point = curve_from_arrival_dir(car.pos, self.target_loc, self.target_dir) return bot.drive.towards_point(bot, curve_point, target_vel=1200, slide=True, boost_min=20, can_keep_speed=False)
def exec(self, bot) -> SimpleControllerState: DODGE_DIST = 250 MIDDLE_OFFSET = 430 # Since ball is at (0,0) we don't we a car_to_ball variable like we do so many other places car = bot.info.my_car dist = norm(car.pos) vel_p = -proj_onto_size(car.vel, car.pos) point = Vec3(0, bot.info.team_sign * (dist / 2.6 - MIDDLE_OFFSET), 0) speed = 2300 opp_dist = norm(bot.info.opponents[0].pos) opp_does_kick = opp_dist < dist + 600 # Opponent is not going for kickoff, so we slow down a bit if not opp_does_kick: speed = 2210 point = Vec3(0, bot.info.team_sign * (dist / 2.05 - MIDDLE_OFFSET), 0) point += Vec3(35 * sign(car.pos.x), 0, 0) # Dodge when close to (0, 0) - but only if the opponent also goes for kickoff. # The dodge itself should happen in about 0.3 seconds if dist - DODGE_DIST < vel_p * 0.3 and opp_does_kick: bot.drive.start_dodge(bot) # Make two dodges when spawning far back elif dist > 3640 and vel_p > 1200 and not opp_does_kick: bot.drive.start_dodge(bot) # Pickup boost when spawning back corner by driving a bit towards the middle boost pad first elif abs(car.pos.x) > 230 and abs(car.pos.y) > 2880: # The pads exact location is (0, 2816), but don't have to be exact point.y = bot.info.team_sign * 2790 self.done = not bot.info.is_kickoff bot.renderer.draw_line_3d(car.pos, point, bot.renderer.white()) return bot.drive.towards_point(bot, point, target_vel=speed, slide=False, boost_min=0, can_dodge=False, can_keep_speed=False)
def closest_enemy(self, pos: Vec3): enemy = None dist = -1 for e in self.opponents: d = norm(e.pos - pos) if enemy is None or d < dist: enemy = e dist = d return enemy, dist
def exec(self, bot): controls = SimpleControllerState() dt = bot.info.dt car = bot.info.my_car relative_rotation = dot(transpose(car.rot), self.target) geodesic_local = rotation_to_axis(relative_rotation) # figure out the axis of minimal rotation to target geodesic_world = dot(car.rot, geodesic_local) # get the angular acceleration alpha = Vec3( self.controller(geodesic_world.x, car.ang_vel.x, dt), self.controller(geodesic_world.y, car.ang_vel.y, dt), self.controller(geodesic_world.z, car.ang_vel.z, dt) ) # reduce the corrections for when the solution is nearly converged alpha.x = self.q(abs(geodesic_world.x) + abs(car.ang_vel.x)) * alpha.x alpha.y = self.q(abs(geodesic_world.y) + abs(car.ang_vel.y)) * alpha.y alpha.z = self.q(abs(geodesic_world.z) + abs(car.ang_vel.z)) * alpha.z # set the desired next angular velocity ang_vel_next = car.ang_vel + alpha * dt # determine the controls that produce that angular velocity roll_pitch_yaw = AerialTurnManeuver.aerial_rpy(car.ang_vel, ang_vel_next, car.rot, dt) controls.roll = roll_pitch_yaw.x controls.pitch = roll_pitch_yaw.y controls.yaw = roll_pitch_yaw.z self._timer += dt if ((norm(car.ang_vel) < self.epsilon_ang_vel and norm(geodesic_world) < self.epsilon_rotation) or self._timer >= self.timeout or car.on_ground): self.done = True controls.throttle = 1.0 return controls
def is_viable(self, car, current_time: float): up = car.up T = self.intercept_time - current_time xf = car.pos + car.vel * T + 0.5 * GRAVITY * T ** 2 vf = car.vel + GRAVITY * T if self.jumping: if self.jump_begin_time == -1: jump_elapsed = 0 else: jump_elapsed = current_time - self.jump_begin_time # How much longer we can press jump and still gain upward force tau = JUMP_MAX_DUR - jump_elapsed # Add jump pulse if jump_elapsed == 0: vf += up * JUMP_SPEED xf += up * JUMP_SPEED * T # Acceleration from holding jump vf += up * JUMP_ACCEL * tau xf += up * JUMP_ACCEL * tau * (T - 0.5 * tau) if self.do_second_jump: # Impulse from the second jump vf += up * JUMP_SPEED xf += up * JUMP_SPEED * (T - tau) delta_x = self.hit_pos - xf dir = normalize(delta_x) phi = angle_between(dir, car.forward) turn_time = 0.7 * (2 * math.sqrt(phi / 9)) tau1 = turn_time * clip(1 - 0.3 / phi, 0.02, 1) required_acc = (2 * norm(delta_x)) / ((T - tau1) ** 2) ratio = required_acc / BOOST_ACCEL tau2 = T - (T - tau1) * math.sqrt(1 - clip01(ratio)) velocity_estimate = vf + BOOST_ACCEL * (tau2 - tau1) * dir boost_estimate = (tau2 - tau1) * BOOST_PR_SEC enough_boost = boost_estimate < 0.95 * car.boost enough_time = abs(ratio) < 0.9 return norm(velocity_estimate) < 0.9 * MAX_SPEED and enough_boost and enough_time
def find_landing_orientation(car: Car) -> Mat33: # FIXME: If we knew the arena's mesh we could test if we are landing on a wall or something forward = normalize(xy( car.vel)) if norm(xy(car.vel)) > 20 else car.forward up = Vec3(z=1) left = cross(up, forward) return Mat33.from_columns(forward, left, up)
def pick_pad(self, bot, pads: List[BoostPad]): # Find closest boost pad my_pos = bot.info.my_car.pos shortest_dist = 99999999 for pad in pads: if pad.is_active: dist = norm(my_pos - pad.pos) if dist < shortest_dist: self.target_pad = pad shortest_dist = dist
def index_of_teammate_at_kickoff_spawn(bot, loc): """ Returns index of teammate at loc, or -1 if there is no teammate """ # RLU Cars does not contain index, so we have to find that ourselves :( for car in bot.info.teammates: dist = norm(car.pos - loc) if dist < 150: return car.index return -1
def exec(self, bot) -> SimpleControllerState: car = bot.info.my_car car_to_pad = self.boost_pad_pos - car.pos vel = proj_onto_size(car.vel, car_to_pad) dist = norm(car_to_pad) if dist < vel * 0.3: self.done = True # Drive towards the pad return bot.drive.towards_point(bot, self.boost_pad_pos, target_vel=2300, boost_min=0, can_keep_speed=True)
def utility(self, bot) -> float: team_sign = bot.info.team_sign ball = bot.info.ball ball_to_goal = bot.info.own_goal - ball.pos too_close = norm(ball_to_goal) < Field.GOAL_WIDTH / 2 + Ball.RADIUS hits_goal_prediction = predict.will_ball_hit_goal(bot) hits_goal = hits_goal_prediction.happens and sign( ball.vel.y) == team_sign and hits_goal_prediction.time < 3 return hits_goal or too_close
def run(self, bot) -> SimpleControllerState: self.is_dribbling = True car = bot.info.my_car ball = bot.info.ball ball_landing = predict.next_ball_landing(bot) ball_to_goal = bot.info.opp_goal.pos - ball.pos # Decide on target pos and speed target = ball_landing.data["obj"].pos - self.offset_bias * normalize( ball_to_goal) dist = norm(target - bot.info.my_car.pos) speed = 1400 if ball_landing.time == 0 else dist / ball_landing.time # Do a flick? car_to_ball = ball.pos - car.pos dist = norm(car_to_ball) enemy, enemy_dist = bot.info.closest_enemy(ball.pos) if dist <= self.required_distance_to_ball_for_flick: self.flick_timer += bot.info.dt if self.flick_timer > self.wait_before_flick and enemy_dist < 900: bot.maneuver = DodgeManeuver( bot, bot.info.opp_goal.pos) # use flick_init_jump_duration? else: self.flick_timer = 0 if bot.do_rendering: bot.renderer.draw_line_3d(car.pos, target, bot.renderer.pink()) return bot.drive.towards_point(bot, target, target_vel=speed, slide=False, can_keep_speed=False, can_dodge=True, wall_offset_allowed=0)
def sdf_wall_dist(point: Vec3) -> float: """ Returns the distance to the nearest wall of using an SDF approximation. The result is negative if the point is outside the arena. """ # SDF box https://www.youtube.com/watch?v=62-pRVZuS5c # SDF rounded corners https://www.youtube.com/watch?v=s5NGeUV2EyU ONES = Vec3(1, 1, 1) # Base cube base_q = abs(point - Vec3(z=Field.HEIGHT / 2)) - SEMI_SIZE + ONES * ROUNDNESS base_dist_outside = norm(vec_max(base_q, Vec3())) base_dist_inside = min(max_comp(base_q), 0) base_dist = base_dist_outside + base_dist_inside # Corners cube corner_q = abs((dot(ROT_45_MAT, point)) - Vec3( z=Field.HEIGHT / 2)) - CORNER_SEMI_SIZE + ONES * ROUNDNESS corner_dist_outside = norm(vec_max(corner_q, Vec3())) corner_dist_inside = min(max_comp(corner_q), 0) corner_dist = corner_dist_outside + corner_dist_inside # Intersection of base and corners base_corner_dist = max(base_dist, corner_dist) - ROUNDNESS # Goals cube goals_q = abs(point - Vec3(z=Field.GOAL_HEIGHT / 2)) - GOALS_SEMI_SIZE + ONES * ROUNDNESS goals_dist_outside = norm(vec_max(goals_q, Vec3())) goals_dist_inside = min(max_comp(goals_q), 0) goals_dist = base_dist_outside + base_dist_inside # Union with goals and invert result return -min(base_corner_dist, goals_dist)
def get_boost_pad_convenience_score(self, pad): if not pad.is_active: return 0 car_to_pad = pad.pos - self.my_car.pos angle = angle_between(self.my_car.forward, car_to_pad) # Pads behind the car is bad if abs(angle) > 1.3: return 0 dist = norm(car_to_pad) dist_score = 1 - clip((abs(dist) / 2500)**2, 0, 1) angle_score = 1 - clip((abs(angle) / 3), 0, 1) return dist_score * angle_score * (0.8, 1)[pad.is_big]
def utility(self, bot) -> float: car = bot.info.my_car ball = bot.info.ball car_to_ball = car.pos - ball.pos bouncing_b = ball.pos.z > 130 or abs(ball.vel.z) > 300 if not bouncing_b: return 0 dist_01 = clip01(1 - norm(car_to_ball) / 3000) head_dir = lerp(Vec3(0, 0, 1), car.forward, 0.1) ang = angle_between(head_dir, car_to_ball) ang_01 = clip01(1 - ang / (math.pi / 2)) return clip01(0.6 * ang_01 + 0.4 * dist_01 # - 0.3 * bot.analyzer.team_mate_has_ball_01 + self.is_dribbling * self.extra_utility_bias)
def utility_score(self, bot) -> float: team_sign = bot.info.team_sign ball = bot.info.ball ball_to_goal = bot.info.own_goal.pos - ball.pos too_close = norm(ball_to_goal) < Goal.WIDTH2 + Ball.RADIUS hits_goal_prediction = predict.will_ball_hit_goal(bot) hits_goal = hits_goal_prediction.happens and sign( ball.vel.y) == team_sign and hits_goal_prediction.time < 3 obj_bonus = { Objective.UNKNOWN: 1, Objective.GO_FOR_IT: 1, Objective.FOLLOW_UP: 0, Objective.ROTATING: 0, Objective.SOLO: 1, }[bot.info.my_car.objective] return float(hits_goal or too_close) * obj_bonus
def find_landing_orientation(car: Car, num_points: int) -> Mat33: dummy = DummyObject(car) for i in range(0, num_points): fall(dummy, 0.0333) # Apply physics and let car fall through the air if i > 5 and sdf_contains(dummy.pos): up = normalize(sdf_normal(dummy.pos)) left = cross(normalize(dummy.vel), up) forward = cross(up, left) return Mat33.from_columns(forward, left, up) forward = normalize(xy( car.vel)) if norm(xy(car.vel)) > 20 else car.forward up = Vec3(z=1) left = cross(up, forward) return Mat33.from_columns(forward, left, up)
def exec(self, bot) -> SimpleControllerState: car = bot.info.my_car # Somehow we didn't find a good pad if self.closest_pad is None: self.done = True return SimpleControllerState() # End maneuver when almost there or pad becomes inactive car_to_pad = self.closest_pad.pos - car.pos vel = proj_onto_size(car.vel, car_to_pad) dist = norm(car_to_pad) if dist < 50 + vel * 0.2 or car.boost > 50 or not self.closest_pad.is_active: self.done = True bot.renderer.draw_line_3d(car.pos, self.closest_pad.pos, bot.renderer.yellow()) return bot.drive.go_towards_point(bot, self.closest_pad.pos, target_vel=2200, slide=True, boost_min=0, can_dodge=self.closest_pad.is_big)
def stay_at(self, bot, point: Vec3, looking_at: Vec3): OKAY_DIST = 100 car = bot.info.my_car car_to_point = point - car.pos car_to_point_dir = normalize(point - car.pos) dist = norm(car_to_point) if dist > OKAY_DIST: return self.towards_point(bot, point, int(dist * 2)) else: look_dir = normalize(looking_at - car.pos) facing_correctly = dot(car.forward, look_dir) > 0.9 if facing_correctly: return SimpleControllerState() else: ctrls = SimpleControllerState() ctrls.throttle = 0.7 * sign(dot(car.forward, car_to_point_dir)) ctrls.steer = -ctrls.throttle * sign( dot(car.left, car_to_point_dir)) return ctrls
def go_home(self, bot): car = bot.info.my_car home = bot.info.own_goal target = home closest_enemy, enemy_dist = bot.info.closest_enemy(bot.info.ball.pos) car_to_home = home - car.pos dist = norm(car_to_home) vel_f_home = proj_onto_size(car.vel, car_to_home) if vel_f_home * 2 > dist: target = bot.info.ball.pos boost = 40 - (dist / 100) + enemy_dist / 200 dodge = dist > 1500 or enemy_dist < dist return self.go_towards_point(bot, target, 2300, True, boost_min=boost, can_dodge=dodge)
def exec(self, bot): car = bot.info.my_car ball = bot.info.ball car_to_ball = ball.pos - car.pos ball_to_enemy_goal = bot.info.enemy_goal - ball.pos own_goal_to_ball = ball.pos - bot.info.own_goal dist = norm(car_to_ball) offence = ball.pos.y * bot.info.team_sign < 0 dot_enemy = dot(car_to_ball, ball_to_enemy_goal) dot_own = dot(car_to_ball, own_goal_to_ball) right_side_of_ball = dot_enemy > 0 if offence else dot_own > 0 if right_side_of_ball: # Aim cone dir_to_post_1 = (bot.info.enemy_goal + Vec3(3800, 0, 0)) - bot.info.ball.pos dir_to_post_2 = (bot.info.enemy_goal + Vec3(-3800, 0, 0)) - bot.info.ball.pos cone = AimCone(dir_to_post_1, dir_to_post_2) cone.get_goto_point(bot, car.pos, bot.info.ball.pos) if bot.do_rendering: cone.draw(bot, bot.info.ball.pos) # Chase ball return bot.drive.go_towards_point(bot, xy(ball.pos), 2000, True, True, can_dodge=dist > 2200) else: # Go home return bot.drive.go_towards_point(bot, bot.info.own_goal_field, 2000, True, True)
def utility_score(self, bot) -> float: car = bot.info.my_car ball = bot.info.ball follow_up_pos = bot.analyzer.ideal_follow_up_pos _, missing_follow_up_guy01 = argmin( bot.info.team_cars, lambda mate: 1.0 - clip01( norm(mate.pos - follow_up_pos) / Field.LENGTH2)) attack_in_front = any( is_closer_to_goal_than(car.pos, mate.pos, car.team) for mate in bot.info.teammates if mate.objective == Objective.GO_FOR_IT) ball_in_front = is_closer_to_goal_than(car.pos, ball.pos, car.team) obj_bonus = { Objective.UNKNOWN: 0.5, Objective.GO_FOR_IT: 0, Objective.FOLLOW_UP: 1, Objective.ROTATING: 0, Objective.SOLO: 0.9, }[car.objective] return attack_in_front * ball_in_front * missing_follow_up_guy01 * bot.info.my_car.onsite * obj_bonus