def __init__(self, bot): team_sign = bot.info.team_sign self.own_goal_right = Vec3(-820 * team_sign, 5120 * team_sign, 0) self.own_goal_left = Vec3(820 * team_sign, 5120 * team_sign, 0) self.aim_cone = None self.ball_to_goal_right = None self.ball_to_goal_left = None
def __init__(self, bot): super().__init__() # These vectors will help us make the curve ts = bot.info.team_sign self.target_loc = Vec3(0, ts * 400, 0) self.target_dir = Vec3(0, -ts, 0)
def _aerial_rpy(ang_vel_start, ang_vel_next, rot, dt): """ :param ang_vel_start: beginning step angular velocity (world coordinates) :param ang_vel_next: next step angular velocity (world coordinates) :param rot: orientation matrix :param dt: time step :return: Vec3 with roll pitch yaw controls """ # car's moment of inertia (spherical symmetry) J = 10.5 # aerial control torque coefficients T = Vec3(-400.0, -130.0, 95.0) # aerial damping torque coefficients H = Vec3(-50.0, -30.0, -20.0) # get angular velocities in local coordinates w0_local = dot(ang_vel_start, rot) w1_local = dot(ang_vel_next, rot) # PWL equation coefficients a = [T[i] * dt / J for i in range(0, 3)] b = [-w0_local[i] * H[i] * dt / J for i in range(0, 3)] c = [w1_local[i] - (1 + H[i] * dt / J) * w0_local[i] for i in range(0, 3)] # RL treats roll damping differently b[0] = 0 return Vec3( solve_PWL(a[0], b[0], c[0]), solve_PWL(a[1], b[1], c[1]), solve_PWL(a[2], b[2], c[2]) )
def __init__(self, index=-1, name="Unknown", team=0, pos=Vec3(), vel=Vec3(), ang_vel=Vec3(), rot=Mat33(), time=0.0): self.id = index self.name = name self.team = team self.pos = pos self.vel = vel self.rot = rot self.ang_vel = ang_vel self.time = time self.is_demolished = False self.jumped = False self.double_jumped = False self.on_ground = True self.supersonic = False self.last_expected_time_till_reach_ball = 3 self.last_input = SimpleControllerState()
def __init__(self, index, team): self.team = team self.index = index self.team_sign = -1 if team == 0 else 1 self.dt = 0.016666 self.time = 0 self.is_kickoff = False self.last_kickoff_end_time = 0 self.time_since_last_kickoff = 0 self.ball = Ball() self.boost_pads = [] self.small_boost_pads = [] self.big_boost_pads = [] self.convenient_boost_pad = None self.convenient_boost_pad_score = 0 self.my_car = Car() self.cars = [] self.teammates = [] self.opponents = [] self.own_goal = Vec3(0, self.team_sign * Field.LENGTH / 2, 0) self.own_goal_field = self.own_goal * 0.86 self.enemy_goal = Vec3(0, -self.team_sign * Field.LENGTH / 2, 0) self.enemy_goal_right = Vec3(820 * self.team_sign, -5120 * self.team_sign, 0) self.enemy_goal_left = Vec3(-820 * self.team_sign, -5120 * self.team_sign, 0) self.field_info_loaded = False
def __init__(self, team: int): team_sign = -1 if team == 0 else 1 self.pos = Vec3(0, team_sign * Field.LENGTH2, 0) self.right_post = Vec3(-(Goal.WIDTH2 - 30) * team_sign, team_sign * Field.LENGTH2, 0) self.left_post = Vec3((Goal.WIDTH2 - 30) * team_sign, team_sign * Field.LENGTH2, 0) self.front = self.pos * 0.86 # A spot in front the goal
def towards(self, bot, target: Vec3, time: float, allowed_uncertainty: float = 0.3, dodge_hit: bool = True): ball_soon = ball_predict(bot, time) ball_soon_to_target_dir = normalize(target - ball_soon.pos) right = dot(axis_to_rotation(Vec3(z=allowed_uncertainty)), ball_soon_to_target_dir) left = dot(axis_to_rotation(Vec3(z=-allowed_uncertainty)), ball_soon_to_target_dir) aim_cone = AimCone(right, left) aim_cone.draw(ball_soon.pos, r=0, g=0) return self.with_aiming(bot, aim_cone, time, dodge_hit)
def __init__(self, index: int = -1, time: float = 0.0, pos=Vec3(), normal=Vec3(z=1), team: int = -1): self.index = index self.time = time self.pos = pos self.normal = normal self.team = team self.new = False
def __init__(self, bot): if bot.team == 0: # blue ra = 0.85 * math.pi la = 0.15 * math.pi self.aim_cone = AimCone(Vec3(math.cos(ra), math.sin(ra), 0), Vec3(math.cos(la), math.sin(la), 0)) else: # orange ra = -0.15 * math.pi la = -0.85 * math.pi self.aim_cone = AimCone(Vec3(math.cos(ra), math.sin(ra), 0), Vec3(math.cos(la), math.sin(la), 0))
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 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 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: if bot.info.time > 10 and bot.info.time > self.next_test: self.next_test = bot.info.time + DURATION dir = Vec3(random.random() - 0.5, random.random() - 0.5, random.random() - 0.5) self.ball_pos = CAR_POS + normalize(dir) * BALL_OFFSET bot.set_game_state( GameState( ball=BallState( Physics(location=self.ball_pos.to_desired_vec(), velocity=ANTI_GRAV.to_desired_vec())), cars={ bot.index: CarState( physics=Physics(location=CAR_POS.to_desired_vec(), velocity=ANTI_GRAV.to_desired_vec())) })) target_rot = looking_in_dir(self.ball_pos - CAR_POS) return bot.fly.align(bot, target_rot)
def __init__(self): self.opp_closest_to_ball = None self.opp_closest_to_ball_dist = 99999 self.car_with_possession = None self.ally_with_possession = None self.opp_with_possession = None self.first_to_reach_ball = None self.ideal_follow_up_pos = Vec3()
class Field: WIDTH = 8192 LENGTH = 10240 HEIGHT = 2044 GOAL_WIDTH = 1900 GOAL_HEIGHT = 642 CORNER_WALL_AX_INTERSECT = 8064 SIZE = Vec3(WIDTH, LENGTH, HEIGHT)
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 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 draw(self, bot, center, arm_len=500, arm_count=5, r=255, g=255, b=255): renderer = bot.renderer ang_step = self.span_size() / (arm_count - 1) for i in range(arm_count): ang = self.right_ang - ang_step * i arm_dir = Vec3(math.cos(ang), math.sin(ang), 0) end = center + arm_dir * arm_len alpha = 255 if i == 0 or i == arm_count - 1 else 110 renderer.draw_line_3d(center, end, renderer.create_color(alpha, r, g, b))
def cross(self, point: Vec3, color, arm_length: float = 30): self.line(point + Vec3(x=arm_length), point + Vec3(x=-arm_length), color) self.line(point + Vec3(y=arm_length), point + Vec3(y=-arm_length), color) self.line(point + Vec3(z=arm_length), point + Vec3(z=-arm_length), color)
def draw_cross(bot, point: Vec3, color, arm_length=30): bot.renderer.draw_line_3d(point + Vec3(x=arm_length), point + Vec3(x=-arm_length), color) bot.renderer.draw_line_3d(point + Vec3(y=arm_length), point + Vec3(y=-arm_length), color) bot.renderer.draw_line_3d(point + Vec3(z=arm_length), point + Vec3(z=-arm_length), color)
def __init__(self, index=-1, name="Unknown", team=0, pos=Vec3(), vel=Vec3(), ang_vel=Vec3(), rot=Mat33(), time=0.0): self.id = index self.name = name self.team = team self.team_sign = -1 if team == 0 else 1 self.pos = pos self.vel = vel self.rot = rot self.ang_vel = ang_vel self.time = time self.is_demolished = False self.jumped = False self.double_jumped = False self.on_ground = True self.supersonic = False self.last_quick_chat = None self.last_expected_time_till_reach_ball = 3 self.last_input = SimpleControllerState() # Analytic info self.effective_pos = pos # A point a bit in front of them self.objective = Objective.UNKNOWN self.possession = 0 self.onsite = False self.reach_ball_time = 0 self.last_ball_touch = 0.0
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 fan(self, center: Vec3, right_ang: float, radians: float, radius: float, color): steps = int((radius**0.7) * 2 * math.pi / radians) + 5 step_size = radians / (steps - 1) points = [center] for i in range(steps): ang = right_ang - step_size * i arm_dir = Vec3(math.cos(ang), math.sin(ang), 0) end = center + arm_dir * radius points.append(end) points.append(center) self.polyline(points, color)
def sdf_normal(point: Vec3) -> Vec3: """ Returns the normalized gradient at the given point. At wall distance 0 this is the arena's surface normal. """ # SDF normals https://www.iquilezles.org/www/articles/normalsSDF/normalsSDF.htm d = 0.0004 return normalize( Vec3( sdf_wall_dist(point + Vec3(d, 0, 0)) - sdf_wall_dist(point - Vec3(d, 0, 0)), sdf_wall_dist(point + Vec3(0, d, 0)) - sdf_wall_dist(point - Vec3(0, d, 0)), sdf_wall_dist(point + Vec3(0, 0, d)) - sdf_wall_dist(point - Vec3(0, 0, d)), ))
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: # Read packet if not self.info.field_info_loaded: self.info.read_field_info(self.get_field_info()) if not self.info.field_info_loaded: return SimpleControllerState() self.renderer.begin_rendering() self.info.read_packet(packet) self.analyzer.update(self) # Check if match is over if packet.game_info.is_match_ended: return celebrate(self) # Assume we won! controller = self.use_brain() # Additional rendering if self.do_rendering: doing = self.maneuver or self.choice state_color = { Objective.GO_FOR_IT: self.renderer.lime(), Objective.FOLLOW_UP: self.renderer.yellow(), Objective.ROTATING: self.renderer.red(), Objective.SOLO: self.renderer.team_color(alt_color=True), Objective.UNKNOWN: self.renderer.team_color(alt_color=True) }[self.info.my_car.objective] if doing is not None: self.renderer.draw_string_2d( 330, 700 + self.index * 20, 1, 1, f"{self.name}:", self.renderer.team_color(alt_color=True)) self.renderer.draw_string_2d(500, 700 + self.index * 20, 1, 1, doing.__class__.__name__, state_color) self.renderer.draw_rect_3d(self.info.my_car.pos + Vec3(z=60), 16, 16, True, state_color) self.renderer.end_rendering() # Save some stuff for next tick self.feedback(controller) return controller
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 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 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 read_field_info(self, field_info: FieldInfo): if field_info is None or field_info.num_boosts == 0: return self.boost_pads = [] self.small_boost_pads = [] self.big_boost_pads = [] for i in range(field_info.num_boosts): pad = field_info.boost_pads[i] pos = Vec3(pad.location) pad = BoostPad(i, pos, pad.is_full_boost, True, 0.0) self.boost_pads.append(pad) if pad.is_big: self.big_boost_pads.append(pad) else: self.small_boost_pads.append(pad) self.convenient_boost_pad = self.boost_pads[0] self.convenient_boost_pad_score = 0 self.field_info_loaded = True
def __init__(self, base=None): if base is not None: # Position if hasattr(base, "location"): self.pos = Vec3(base.location.x, base.location.y, base.location.z) else: self.pos = Vec3(base.pos) # Velocity if hasattr(base, "velocity"): self.vel = Vec3(base.velocity.x, base.velocity.y, base.velocity.z) else: self.vel = Vec3(base.vel) else: self.pos = Vec3(0, 0, 0) self.vel = Vec3(0, 0, 0)