def exec(self, bot) -> SimpleControllerState: ct = bot.info.time - self._start_time controls = SimpleControllerState() controls.throttle = 1 car = bot.info.my_car # Target is allowed to be a function that takes bot as a parameter. Check what it is if callable(self.target): target = self.target(bot) else: target = self.target # To boost or not to boost, that is the question car_to_target = target - car.pos vel_p = proj_onto_size(car.vel, car_to_target) angle = angle_between(car_to_target, car.forward) controls.boost = self.boost and angle < self._boost_ang_req and vel_p < self._max_speed # States of dodge (note reversed order) # Land on ground if ct >= self._t_finishing: self._almost_finished = True if car.on_ground: self.done = True else: bot.maneuver = RecoveryManeuver(bot) self.done = True return controls elif ct >= self._t_second_unjump: # Stop pressing jump and rotate and wait for flip is done pass elif ct >= self._t_aim: if ct >= self._t_second_jump: controls.jump = 1 # Direction, yaw, pitch, roll if self.target is None: controls.roll = 0 controls.pitch = -1 controls.yaw = 0 else: target_local = dot(car_to_target, car.rot) target_local.z = 0 direction = normalize(target_local) controls.roll = 0 controls.pitch = -direction.x controls.yaw = sign(car.rot.get(2, 2)) * direction.y # Stop pressing jump elif ct >= self._t_first_unjump: pass # First jump else: controls.jump = 1 return controls
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 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 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 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 exec(self, bot): if not self.announced_in_quick_chat: self.announced_in_quick_chat = True bot.send_quick_chat(QuickChats.CHAT_EVERYONE, QuickChats.Information_IGotIt) ct = bot.info.time car = bot.info.my_car up = car.up controls = SimpleControllerState() # Time remaining till intercept time T = self.intercept_time - bot.info.time # Expected future position xf = car.pos + car.vel * T + 0.5 * GRAVITY * T ** 2 # Expected future velocity vf = car.vel + GRAVITY * T # Is set to false while jumping to avoid FeelsBackFlipMan rotate = True if self.jumping: if self.jump_begin_time == -1: jump_elapsed = 0 self.jump_begin_time = ct else: jump_elapsed = ct - 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 rotate = False # Acceleration from holding jump vf += up * JUMP_SPEED * tau xf += up * JUMP_SPEED * 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) if jump_elapsed < JUMP_MAX_DUR: controls.jump = True else: controls.jump = False if self.do_second_jump: if self.jump_pause_counter < 4: # Do a 4-tick pause between jumps self.jump_pause_counter += 1 else: # Time to start second jump # we do this by resetting our jump counter and pretend and our aerial started in the air self.jump_begin_time = -1 self.jumping = True self.do_second_jump = False else: # We are done jumping self.jumping = False else: controls.jump = False delta_pos = self.hit_pos - xf direction = normalize(delta_pos) car_to_hit_pos = self.hit_pos - car.pos dodging = self.dodge_begin_time != -1 if dodging: controls.jump = True # We are not pressing jump, so let's align the car if rotate and not dodging: if self.do_dodge and norm(car_to_hit_pos) < Ball.RADIUS + 80: # Start dodge self.dodge_begin_time = ct hit_local = dot(car_to_hit_pos, car.rot) hit_local.z = 0 dodge_direction = normalize(hit_local) controls.roll = 0 controls.pitch = -dodge_direction.x controls.yaw = sign(car.rot.get(2, 2)) * direction.y controls.jump = True else: # Adjust orientation if norm(delta_pos) > 50: pd = bot.fly.align(bot, looking_in_dir(delta_pos)) else: if self.target_rot is not None: pd = bot.fly.align(bot, self.target_rot) else: pd = bot.fly.align(bot, looking_in_dir(self.hit_pos - car.pos)) controls.roll = pd.roll controls.pitch = pd.pitch controls.yaw = pd.yaw if not dodging and angle_between(car.forward, direction) < 0.3: if norm(delta_pos) > 40: controls.boost = 1 controls.throttle = 0 else: controls.boost = 0 controls.throttle = clip01(0.5 * THROTTLE_AIR_ACCEL * T ** 2) else: controls.boost = 0 controls.throttle = 0 prediction = predict.ball_predict(bot, T) self.done = T < 0 if norm(self.hit_pos - prediction.pos) > 50: # Jump shot failed self.done = True bot.send_quick_chat(QuickChats.CHAT_EVERYONE, QuickChats.Apologies_Cursing) if bot.do_rendering: car_to_hit_dir = normalize(self.hit_pos - car.pos) color = bot.renderer.pink() rendering.draw_cross(bot, self.hit_pos, color, arm_length=100) rendering.draw_circle(bot, lerp(car.pos, self.hit_pos, 0.25), car_to_hit_dir, 40, 12, color) rendering.draw_circle(bot, lerp(car.pos, self.hit_pos, 0.5), car_to_hit_dir, 40, 12, color) rendering.draw_circle(bot, lerp(car.pos, self.hit_pos, 0.75), car_to_hit_dir, 40, 12, color) bot.renderer.draw_line_3d(car.pos, self.hit_pos, color) return controls
def towards_point(self, bot, point: Vec3, target_vel=1430, slide=False, boost_min=101, can_keep_speed=True, can_dodge=True, wall_offset_allowed=125) -> SimpleControllerState: REQUIRED_ANG_FOR_SLIDE = 1.65 REQUIRED_VELF_FOR_DODGE = 1100 car = bot.info.my_car # Dodge is done if self.dodge is not None and self.dodge.done: self.dodge = None self.last_dodge_end_time = bot.info.time # Continue dodge elif self.dodge is not None: self.dodge.target = point return self.dodge.exec(bot) # Begin recovery if not car.on_ground: bot.maneuver = RecoveryManeuver() return self.controls # Get down from wall by choosing a point close to ground if not is_near_wall(point, wall_offset_allowed) and angle_between( car.up, Vec3(0, 0, 1)) > math.pi * 0.31: point = lerp(xy(car.pos), xy(point), 0.5) # If the car is in a goal, avoid goal posts self._avoid_goal_post(bot, point) car_to_point = point - car.pos # The vector from the car to the point in local coordinates: # point_local.x: how far in front of my car # point_local.y: how far to the left of my car # point_local.z: how far above my car point_local = dot(point - car.pos, car.rot) # Angle to point in local xy plane and other stuff angle = math.atan2(point_local.y, point_local.x) dist = norm(point_local) vel_f = proj_onto_size(car.vel, car.forward) vel_towards_point = proj_onto_size(car.vel, car_to_point) # Start dodge if can_dodge and abs(angle) <= 0.02 and vel_towards_point > REQUIRED_VELF_FOR_DODGE\ and dist > vel_towards_point + 500 + 900 and bot.info.time > self.last_dodge_end_time + self.dodge_cooldown: self.dodge = DodgeManeuver(bot, point) # Start half-flip elif can_dodge and abs(angle) >= 3 and vel_towards_point < 0\ and dist > -vel_towards_point + 500 + 900 and bot.info.time > self.last_dodge_end_time + self.dodge_cooldown: self.dodge = HalfFlipManeuver(bot, boost=car.boost > boost_min + 10) # Is point right behind? Maybe reverse instead if -100 < point_local.x < 0 and abs(point_local.y) < 50: #bot.print("Reversing?") pass # Is in turn radius deadzone? tr = turn_radius(abs(vel_f + 50)) # small bias tr_side = sign(angle) tr_center_local = Vec3(0, tr * tr_side, 10) point_is_in_turn_radius_deadzone = norm(point_local - tr_center_local) < tr # Draw turn radius deadzone if car.on_ground and False: tr_center_world = car.pos + dot(car.rot, tr_center_local) tr_center_world_2 = car.pos + dot(car.rot, -1 * tr_center_local) color = draw.orange() draw.circle(tr_center_world, car.up, tr, color) draw.circle(tr_center_world_2, car.up, tr, color) if point_is_in_turn_radius_deadzone: # Hard turn self.controls.steer = sign(angle) self.controls.boost = False self.controls.throttle = 0 if vel_f > 150 else 0.1 if point_local.x < 110 and point_local.y < 400 and norm( car.vel) < 300: # Brake or go backwards when the point is really close but not in front of us self.controls.throttle = clip(-0.25 + point_local.x / -110.0, 0, -1) self.controls.steer = -0.5 * sign(angle) else: # Should drop speed or just keep up the speed? if can_keep_speed and target_vel < vel_towards_point: target_vel = vel_towards_point else: # Small lerp adjustment target_vel = lerp(vel_towards_point, target_vel, 1.1) # Turn and maybe slide self.controls.steer = clip(angle + (2.5 * angle)**3, -1.0, 1.0) if slide and abs(angle) > REQUIRED_ANG_FOR_SLIDE: self.controls.handbrake = True self.controls.steer = sign(angle) else: self.controls.handbrake = False # Overshoot target vel for quick adjustment target_vel = lerp(vel_towards_point, target_vel, 1.2) # Find appropriate throttle/boost if vel_towards_point < target_vel: self.controls.throttle = 1 if boost_min < car.boost and vel_towards_point + 80 < target_vel and target_vel > 1400 \ and not self.controls.handbrake and is_heading_towards(angle, dist): self.controls.boost = True else: self.controls.boost = False else: vel_delta = target_vel - vel_towards_point self.controls.throttle = clip(0.2 + vel_delta / 500, 0, -1) self.controls.boost = False if self.controls.handbrake: self.controls.throttle = min(0.4, self.controls.throttle) # Saved if something outside calls start_dodge() in the meantime self.last_point = point return self.controls