def run_hivemind(agent: MyHivemind): agent.debug_stack() if agent.kickoff_flag and all( len(drone.stack) < 1 for drone in agent.drones): if len(agent.friends + agent.drones) == 3: setup_3s_kickoff(agent) elif len(agent.friends + agent.drones) == 2: setup_2s_kickoff(agent) else: setup_other_kickoff(agent) elif not agent.kickoff_flag: for drone in agent.drones: drones = copy(agent.drones) drones.remove(drone) team = agent.friends + drones if len(drone.stack) < 1 or drone.action == Action.Shadowing: if drone.on_side and drone.closest or agent.conceding: push_shot(drone, agent) if len(drone.stack) < 1: if drone.action == Action.Going: if any(teammate.on_side for teammate in team): drone.push( GotoBoost(closest_boost(agent, drone.location))) drone.action = Action.Boost else: drone.push(Shadow(agent.ball.location)) drone.action = Action.Shadowing elif drone.action == Action.Shadowing: drone.push(Shadow(agent.ball.location)) drone.action = Action.Shadowing elif drone.action == Action.Boost: drone.push(Shadow(agent.ball.location)) drone.action = Action.Shadowing
def run(self, drone: CarObject, agent: MyHivemind): # An example of pushing routines to the stack: agent.line(Vector3(0, 0, 50), 2000 * self.vector.flatten(), color=[255, 0, 0]) agent.line(Vector3(0, 0, 50), 2000 * drone.forward.flatten(), color=[0, 255, 0]) robbies_constant = (self.vector * 1.5 * 2200 - drone.velocity * 1.5) * 2 * 1.5 ** -2 robbies_boost_constant = drone.forward.flatten().normalize().dot(robbies_constant.flatten().normalize()) > ( 0.3 if not drone.airborne else 0.1) drone.controller.boost = robbies_boost_constant and self.boost and not drone.supersonic if self.time == -1: elapsed = 0 self.time = agent.time else: elapsed = agent.time - self.time if elapsed < self.delay: if elapsed < self.duration: drone.controller.jump = True else: drone.controller.jump = False self.counter += 1 defaultPD(drone, self.preorientation) elif elapsed >= self.delay and self.counter < 3: drone.controller.jump = False defaultPD(drone, self.preorientation) self.counter += 1 elif elapsed < self.delay + 0.05: drone.controller.jump = True defaultPD(drone, self.vector) else: drone.pop() drone.push(Recovery(boost=self.boost, time=agent.time))
def run(self, drone: CarObject, agent: MyHivemind): car_to_ball, distance = (agent.ball.location - drone.location).normalize(True) ball_to_target = (self.target - agent.ball.location).normalize() relative_velocity = car_to_ball.dot(drone.velocity - agent.ball.velocity) if relative_velocity != 0.0: eta = cap(distance / cap(relative_velocity, 400, 2300), 0.0, 1.5) else: eta = 1.5 # If we are approaching the ball from the wrong side the car will try to only hit the very edge of the ball left_vector = car_to_ball.cross((0, 0, 1)) right_vector = car_to_ball.cross((0, 0, -1)) target_vector = -ball_to_target.clamp(left_vector, right_vector) final_target = agent.ball.location + (target_vector * (distance / 2)) # Some adjustment to the final target to ensure we don't try to drive through any goalposts to reach it if abs(drone.location[1]) > 5150: final_target[0] = cap(final_target[0], -750, 750) agent.line(final_target - Vector3(0, 0, 100), final_target + Vector3(0, 0, 100), [255, 255, 255]) angles = defaultPD(drone, drone.local(final_target - drone.location)) defaultThrottle(drone, 2300 if distance > 1600 else 2300 - cap(1600 * abs(angles[1]), 0, 2050)) drone.controller.boost = False if drone.airborne or abs(angles[1]) > 0.3 else drone.controller.boost drone.controller.handbrake = True if abs(angles[1]) > 2.3 else drone.controller.handbrake if abs(angles[1]) < 0.05 and (eta < 0.45 or distance < 150): drone.pop() drone.push(Flip(drone.local(car_to_ball)))
def run_1v1(agent: MyHivemind): agent.debug_stack() drone = agent.drones[0] if agent.kickoff_flag and len(drone.stack) < 1: if abs(drone.location.x) < 250: drone.push(CenterKickoff()) drone.action = Action.Going elif abs(drone.location.x) < 1000: drone.push(OffCenterKickoff()) drone.action = Action.Going else: drone.push(DiagonalKickoff()) drone.action = Action.Going elif not agent.kickoff_flag: on_side = (drone.location - agent.friend_goal.location).magnitude() < ( agent.ball.location - agent.friend_goal.location).magnitude() if len(drone.stack) < 1: if drone.action == Action.Going: if on_side and (drone.location - agent.ball.location).magnitude() < 2000: push_shot(drone, agent) if len(drone.stack) < 1: drone.push(Shadow(agent.ball.location)) drone.action = Action.Shadowing elif drone.action == Action.Shadowing: push_shot(drone, agent) if len(drone.stack) < 1: drone.push(Shadow(agent.ball.location)) drone.action = Action.Shadowing
def setup_2s_kickoff(agent: MyHivemind): x_pos = [round(drone.location.x) for drone in agent.drones] x_pos.extend([round(friend.location.x) for friend in agent.friends]) if sorted(x_pos) == [-2048, 2048]: for drone in agent.drones: if round(drone.location.x) == agent.side() * -2048: drone.push(KickOff()) drone.action = Action.Going elif round(drone.location.x) == agent.side() * 2048: drone.push(Shadow()) drone.action = Action.Shadowing elif sorted(x_pos) == [-256, 256]: for drone in agent.drones: if round(drone.location.x) == agent.side() * -256: drone.push(KickOff()) drone.action = Action.Going elif round(drone.location.x) == agent.side() * 256: drone.push(Shadow()) drone.action = Action.Shadowing elif -2048 in x_pos or 2048 in x_pos: for drone in agent.drones: if round(abs(drone.location.x)) == 2048: drone.push(KickOff()) drone.action = Action.Going else: drone.push(Shadow()) drone.action = Action.Shadowing elif -256 in x_pos or 256 in x_pos: for drone in agent.drones: if round(abs(drone.location.x)) == 256: drone.push(KickOff()) drone.action = Action.Going else: drone.push(Shadow()) drone.action = Action.Shadowing
def run_1v1(agent: MyHivemind): agent.debug_stack() drone: CarObject = agent.drones[0] if agent.kickoff_flag and len(drone.stack) < 1: drone.push(KickOff()) drone.action = Action.Going elif not agent.kickoff_flag: if len(drone.stack) < 1 or drone.action == Action.Shadowing: if drone.on_side or agent.conceding: shot = find_shot(drone, (agent.foe_goal.left_post, agent.foe_goal.right_post)) if shot is not None: shot = find_shot(drone, (agent.foe_goal.left_post, agent.foe_goal.right_post)) if shot is not None: drone.push(shot) drone.action = Action.Going else: my_shot = find_any_shot(drone) enemy_shot = find_any_shot(agent.foes[0]) if my_shot is not None: if enemy_shot is None: drone.push(my_shot) drone.action = Action.Going elif my_shot.intercept_time < enemy_shot.intercept_time or agent.desperate: drone.push(my_shot) drone.action = Action.Going if len(drone.stack) < 1: drone.push(Shadow()) drone.action = Action.Shadowing
def setup_3s_kickoff(agent: MyHivemind): x_pos = [round(drone.location.x) for drone in agent.drones] x_pos.extend([round(friend.location.x) for friend in agent.friends]) if sorted(x_pos) in [[-2048, -256, 2048], [-2048, 0, 2048], [-2048, 256, 2048]]: for drone in agent.drones: if round(drone.location.x) == agent.side() * -2048: drone.push(KickOff()) drone.action = Action.Going elif round(drone.location.x) == agent.side() * 2048: target = agent.friend_goal.location + 2 * ( agent.ball.location - agent.friend_goal.location) / 3 drone.push(Goto(target)) drone.action = Action.Cheating else: drone.push(GotoBoost(closest_boost(agent, drone.location))) drone.action = Action.Boost elif sorted(x_pos) == [-256, 0, 256]: for drone in agent.drones: if round(drone.location.x) == agent.side() * -256: drone.push(KickOff()) drone.action = Action.Going elif round(drone.location.x) == agent.side() * 256: target = agent.friend_goal.location + 2 * ( agent.ball.location - agent.friend_goal.location) / 3 drone.push(Goto(target)) drone.action = Action.Cheating else: drone.push(GotoBoost(closest_boost(agent, drone.location))) drone.action = Action.Boost elif -2048 in x_pos or 2048 in x_pos: for drone in agent.drones: if round(abs(drone.location.x)) == 2048: drone.push(KickOff()) drone.action = Action.Going elif round(drone.location.x) == agent.side() * -256: target = agent.friend_goal.location + 2 * ( agent.ball.location - agent.friend_goal.location) / 3 drone.push(Goto(target)) drone.action = Action.Cheating elif round(drone.location.x) == 0: drone.push(GotoBoost(closest_boost(agent, drone.location))) drone.action = Action.Boost else: if 0 in x_pos: target = agent.friend_goal.location + 2 * ( agent.ball.location - agent.friend_goal.location) / 3 drone.push(Goto(target)) drone.action = Action.Cheating else: drone.push(GotoBoost(closest_boost(agent, drone.location))) drone.action = Action.Boost
def shot_valid(agent: MyHivemind, shot: Union[AerialShot, JumpShot, Aerial], threshold: float = 45) -> bool: # Returns True if the ball is still where the shot anticipates it to be # 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.get_ball_prediction_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 slopes = (Vector3(slices[latest].physics.location) - Vector3(slices[soonest].physics.location)) * (1 / dt) # Determining exactly where the ball will be at the given shot's intercept_time predicted_ball_location = Vector3( slices[soonest].physics.location) + (slopes * time_from_soonest) # Comparing predicted location with where the shot expects the ball to be return (shot.ball_location - predicted_ball_location).magnitude() < threshold
def run(self, drone: CarObject, agent: MyHivemind): target = agent.ball.location + Vector3(0, 200 * agent.side(), 0) local_target = drone.local(target - drone.location) defaultPD(drone, local_target) defaultThrottle(drone, 2300) if local_target.magnitude() < 650: drone.pop() drone.push(Flip(drone.local(agent.foe_goal.location - drone.location)))
def push_shot(drone: CarObject, agent: MyHivemind): left = Vector3(4200 * -agent.side(), agent.ball.location.y + (1000 * -agent.side()), 0) right = Vector3(4200 * agent.side(), agent.ball.location.y + (1000 * -agent.side()), 0) targets = { "goal": (agent.foe_goal.left_post, agent.foe_goal.right_post), "upfield": (left, right) } shots = find_hits(drone, agent, targets) if len(shots["goal"]) > 0: drone.clear() drone.push(shots["goal"][0]) drone.action = Action.Going elif len(shots["upfield"]) > 0: drone.clear() drone.push(shots["upfield"][0]) drone.action = Action.Going
def run(self, drone: CarObject, agent: MyHivemind): target = Vector3(0, 3116 * agent.side(), 0) local_target = drone.local(target - drone.location) defaultPD(drone, local_target) defaultThrottle(drone, 2300) if local_target.magnitude() < 400: drone.pop() drone.push(DiagonalKickoff()) drone.push(Flip(drone.local(agent.ball.location - drone.location)))
def run_hivemind(agent: MyHivemind): agent.debug_stack() if agent.kickoff_flag and all(len(drone.stack) < 1 for drone in agent.drones): if len(agent.friends + agent.drones) == 3: setup_3s_kickoff(agent) elif len(agent.friends + agent.drones) == 2: setup_2s_kickoff(agent) else: setup_other_kickoff(agent) elif not agent.kickoff_flag: for drone in agent.drones: drones = copy(agent.drones) drones.remove(drone) team = agent.friends + drones empty_stack = len(drone.stack) < 1 and drone.on_side and drone.closest should_go = ( drone.action == Action.Shadowing) and drone.on_side and drone.closest conceding = (agent.conceding and not any(teammate.on_side for teammate in team)) or ( agent.conceding and drone.on_side and drone.closest) cheating = drone.action == Action.Cheating if empty_stack or should_go or conceding or cheating: if empty_stack or drone.stack[0].__class__.__name__ not in ["GroundShot", "JumpShot", "DoubleJump"]: shot = find_any_shot(drone) if shot is not None: drone.push(shot) drone.action = Action.Going if len(drone.stack) < 1: if drone.action == Action.Going: if any(teammate.on_side for teammate in team) and drone.boost < 66: drone.push(GotoBoost(closest_boost(agent, drone.location))) drone.action = Action.Boost else: drone.push(Shadow()) drone.action = Action.Shadowing elif drone.action == Action.Shadowing: if all(teammate.on_side for teammate in team) and drone.boost < 66: drone.push(GotoBoost(closest_boost(agent, drone.location))) drone.action = Action.Boost else: drone.push(Shadow()) drone.action = Action.Shadowing elif drone.action == Action.Boost: drone.push(Shadow()) drone.action = Action.Shadowing
def run(self, drone: CarObject, agent: MyHivemind): if self.boost is None: drone.pop() return car_to_boost = self.boost.location - drone.location distance_remaining = car_to_boost.flatten().magnitude() agent.line(self.boost.location - Vector3(0, 0, 500), self.boost.location + Vector3(0, 0, 500), [0, 255, 0]) if self.target is not None: vector = (self.target - self.boost.location).normalize() side_of_vector = sign(vector.cross((0, 0, 1)).dot(car_to_boost)) car_to_boost_perp = car_to_boost.cross((0, 0, side_of_vector)).normalize() adjustment = car_to_boost.angle2D(vector) * distance_remaining / 3.14 final_target = self.boost.location + (car_to_boost_perp * adjustment) car_to_target = (self.target - drone.location).magnitude() else: adjustment = 9999 car_to_target = 0 final_target = self.boost.location # Some adjustment to the final target to ensure it's inside the field and # we don't try to dirve through any goalposts to reach it if abs(drone.location[1]) > 5150: final_target[0] = cap(final_target[0], -750, 750) local_target = drone.local(final_target - drone.location) angles = defaultPD(drone, local_target) defaultThrottle(drone, 2300) drone.controller.boost = self.boost.large if abs(angles[1]) < 0.3 else False drone.controller.handbrake = True if abs(angles[1]) > 2.3 else drone.controller.handbrake velocity = 1 + drone.velocity.magnitude() if not self.boost.active or drone.boost >= 99.0 or distance_remaining < 350: drone.pop() elif drone.airborne: drone.push(Recovery(self.target)) elif abs(angles[1]) < 0.05 and 600 < velocity < 2150 and ( distance_remaining / velocity > 2.0 or (adjustment < 90 and car_to_target / velocity > 2.0)): drone.push(Flip(local_target))
def run_1v1(agent: MyHivemind): agent.debug_stack() drone = agent.drones[0] if agent.kickoff_flag and len(drone.stack) < 1: if abs(drone.location.x) < 250: drone.push(CenterKickoff()) drone.action = Action.Going elif abs(drone.location.x) < 1000: drone.push(OffCenterKickoff()) drone.action = Action.Going else: drone.push(DiagonalKickoff()) drone.action = Action.Going elif not agent.kickoff_flag: if len(drone.stack) < 1 or drone.action == Action.Shadowing: if drone.on_side or agent.conceding: push_shot(drone, agent) if len(drone.stack) < 1: drone.push(Shadow(agent.ball.location)) drone.action = Action.Shadowing
def run(self, drone: CarObject, agent: MyHivemind): if self.target is not None: if self.target_local: local_target = self.target else: local_target = drone.local((self.target - drone.location).flatten()) else: local_target = drone.local(drone.velocity.flatten()) defaultPD(drone, local_target) drone.controller.throttle = 1 t = (-drone.velocity.z - ( drone.velocity.z ** 2 + 2 * -650 * -(max(drone.location.z - 17.01, 0.01))) ** 0.5) / -650 if self.target is not None: robbies_constant = (self.target.normalize() * t * 2200 - drone.velocity * t) * 2 * t ** -2 else: robbies_constant = (drone.velocity.normalize() * t * 2200 - drone.velocity * t) * 2 * t ** -2 agent.line(drone.location, robbies_constant, color=[255, 255, 255]) robbies_boost_constant = drone.forward.normalize().dot(robbies_constant.normalize()) > 0.5 drone.controller.boost = robbies_boost_constant and self.boost and not drone.supersonic if not drone.airborne: drone.pop()
def run(self, drone: CarObject, agent: MyHivemind): target = agent.friend_goal.location + (agent.ball.location - agent.friend_goal.location) / 2 car_to_target = target - drone.location distance_remaining = car_to_target.flatten().magnitude() agent.line(target - Vector3(0, 0, 500), target + Vector3(0, 0, 500), [255, 0, 255]) if self.vector is not None: # See commends for adjustment in jump_shot or aerial for explanation side_of_vector = sign(self.vector.cross((0, 0, 1)).dot(car_to_target)) car_to_target_perp = car_to_target.cross((0, 0, side_of_vector)).normalize() adjustment = car_to_target.angle(self.vector) * distance_remaining / 3.14 final_target = target + (car_to_target_perp * adjustment) else: final_target = target # Some adjustment to the final target to ensure it's inside the field and # we don't try to drive through any goalposts to reach it if abs(drone.location[1]) > 5150: final_target[0] = cap(final_target[0], -750, 750) local_target = drone.local(final_target - drone.location) angles = defaultPD(drone, local_target, self.direction) defaultThrottle(drone, 2300, self.direction) drone.controller.boost = False drone.controller.handbrake = True if abs(angles[1]) > 2.3 else drone.controller.handbrake velocity = 1 + drone.velocity.magnitude() if distance_remaining < 350: drone.pop() elif abs(angles[1]) < 0.05 and 600 < velocity < 2150 and distance_remaining / velocity > 2.0: drone.push(Flip(local_target)) # TODO Halfflip # elif abs(angles[1]) > 2.8 and velocity < 200: # agent.push(flip(local_target, True)) elif drone.airborne: drone.push(Recovery(target))
def push_shot(drone: CarObject, agent: MyHivemind): left = Vector3(4200 * -agent.side(), agent.side() * 5120, 0) right = Vector3(4200 * agent.side(), agent.side() * 5120, 0) targets = {"goal": (agent.foe_goal.left_post, agent.foe_goal.right_post)} if not agent.conceding: drones = copy(agent.drones) drones.remove(drone) team = agent.friends + drones for teammate in team: a = teammate.location b = teammate.location + 2000 * teammate.forward local_a = drone.local(a) angle_a = math.atan2(local_a.y, local_a.x) if angle_a > 0: targets["teammate" + str(team.index(teammate))] = (b, a) else: targets["teammate" + str(team.index(teammate))] = (a, b) targets["upfield"] = (left, right) shots = find_hits(drone, agent, targets) if len(shots["goal"]) > 0: drone.clear() drone.push(shots["goal"][0]) drone.action = Action.Going elif shots.get("teammate0") is not None and len( shots.get("teammate0")) > 0: drone.clear() drone.push(shots["teammate0"][0]) drone.action = Action.Going elif shots.get("teammate1") is not None and len( shots.get("teammate1")) > 0: drone.clear() drone.push(shots["teammate1"][0]) drone.action = Action.Going elif len(shots["upfield"]) > 0: drone.clear() drone.push(shots["upfield"][0]) drone.action = Action.Going
def run_test(agent: MyHivemind): agent.debug_stack() next_state = agent.test_state if agent.test_state == TestState.Reset: agent.test_time = agent.time b_position = RLBot3(random.uniform(-1500, 1500), random.uniform(2500, 3500), random.uniform(300, 500)) b_velocity = RLBot3(random.uniform(-300, 300), random.uniform(-100, 100), random.uniform(900, 1000)) ball_state = BallState( physics=Physics(location=b_position, velocity=b_velocity, rotation=Rotator(0, 0, 0), angular_velocity=RLBot3(0, 0, 0))) # this just initializes the car and ball # to different starting points each time c_position = RLBot3(b_position.x, 0 * random.uniform(-1500, -1000), 25) # c_position = Vector3(200, -1000, 25) car_state = CarState(physics=Physics(location=c_position, velocity=RLBot3(0, 800, 0), rotation=Rotator(0, 1.6, 0), angular_velocity=RLBot3(0, 0, 0)), boost_amount=100) agent.set_game_state( GameState(ball=ball_state, cars={agent.drones[0].index: car_state})) next_state = TestState.Wait elif agent.test_state == TestState.Wait: if agent.time - agent.test_time > 0.2: next_state = TestState.Init elif agent.test_state == TestState.Init: push_shot(agent.drones[0], agent) next_state = TestState.Running elif agent.test_state == TestState.Running: if agent.time - agent.test_time > 5: next_state = TestState.Reset agent.drones[0].clear() agent.test_state = next_state
def find_hits(drone: CarObject, agent: MyHivemind, targets): # find_hits takes a dict of (left,right) target pairs and finds routines that could hit the ball # between those target pairs # find_hits is only meant for routines that require a defined intercept time/place in the future # find_hits should not be called more than once in a given tick, # as it has the potential to use an entire tick to calculate # Example Useage: # targets = {"goal":(opponent_left_post,opponent_right_post), "anywhere_but_my_net":(my_right_post,my_left_post)} # hits = find_hits(agent,targets) # print(hits) # >{"goal":[a ton of jump and aerial routines,in order from soonest to latest], # "anywhere_but_my_net":[more routines and stuff]} hits = {name: [] for name in targets} struct = agent.get_ball_prediction_struct() # Begin looking at slices 0.25s into the future # The number of slices i = 15 while i < struct.num_slices: # Gather some data about the slice intercept_time = struct.slices[i].game_seconds time_remaining = intercept_time - agent.time if time_remaining > 0: ball_location = Vector3(struct.slices[i].physics.location) ball_velocity = Vector3( struct.slices[i].physics.velocity).magnitude() if abs(ball_location[1]) > 5250: break # abandon search if ball is scored at/after this point # determine the next slice we will look at, based on ball velocity (slower ball needs fewer slices) i += 15 - cap(int(ball_velocity // 150), 0, 13) car_to_ball = ball_location - drone.location # Adding a True to a vector's normalize will have it also return the magnitude of the vector direction, distance = car_to_ball.normalize(True) # How far the car must turn in order to face the ball, for forward and reverse forward_angle = direction.angle(drone.forward) backward_angle = math.pi - forward_angle # Accounting for the average time it takes to turn and face the ball # Backward is slightly longer as typically the car is moving forward and takes time to slow down forward_time = time_remaining - (forward_angle * 0.318) backward_time = time_remaining - (backward_angle * 0.418) # If the car only had to drive in a straight line, we ensure it has enough time to reach the ball # (a few assumptions are made) forward_flag = forward_time > 0.0 and ( distance * 1.05 / forward_time) < ( 2290 if drone.boost > distance / 100 else 1400) backward_flag = distance < 1500 and backward_time > 0.0 and ( distance * 1.05 / backward_time) < 1200 # Provided everything checks out, we begin to look at the target pairs if forward_flag or backward_flag: for pair in targets: # First we correct the target coordinates to account for the ball's radius # If swapped == True, the shot isn't possible because the ball wouldn't fit between the targets left, right, swapped = post_correction( ball_location, targets[pair][0], targets[pair][1]) if not swapped: # Now we find the best direction to hit the ball in order to land it between the target points left_vector = (left - ball_location).normalize() right_vector = (right - ball_location).normalize() best_shot_vector = direction.clamp( left_vector, right_vector) # Check to make sure our approach is inside the field if in_field(ball_location - (200 * best_shot_vector), 1): # The slope represents how close the car is to the chosen vector, higher = better # A slope of 1.0 would mean the car is 45 degrees off slope = find_slope(best_shot_vector, car_to_ball) if forward_flag: if ball_location[2] <= 300 and slope > 0.0: hits[pair].append( JumpShot(ball_location, intercept_time, best_shot_vector, slope)) if 300 < ball_location[ 2] < 600 and slope > 1.0 and ( ball_location[2] - 250) * 0.14 > drone.boost: hits[pair].append( AerialShot(ball_location, intercept_time, best_shot_vector)) elif backward_flag and ball_location[ 2] <= 280 and slope > 0.25: hits[pair].append( JumpShot(ball_location, intercept_time, best_shot_vector, slope, -1)) return hits
def run(self, drone: CarObject, agent: MyHivemind): raw_time_remaining = self.intercept_time - agent.time # Capping raw_time_remaining above 0 to prevent division problems time_remaining = cap(raw_time_remaining, 0.01, 10.0) car_to_ball = self.ball_location - drone.location # whether we are to the left or right of the shot vector side_of_shot = sign(self.shot_vector.cross((0, 0, 1)).dot(car_to_ball)) car_to_intercept = self.intercept - drone.location car_to_intercept_perp = car_to_intercept.cross((0, 0, side_of_shot)) # perpendicular distance_remaining = car_to_intercept.flatten().magnitude() speed_required = distance_remaining / time_remaining # When still on the ground we pretend gravity doesn't exist, for better or worse acceleration_required = backsolve(self.intercept, drone, time_remaining, 0 if self.jump_time == 0 else 325) local_acceleration_required = drone.local(acceleration_required) # The adjustment causes the car to circle around the dodge point in an effort to line up with the shot vector # The adjustment slowly decreases to 0 as the bot nears the time to jump adjustment = car_to_intercept.angle(self.shot_vector) * distance_remaining / 1.57 # size of adjustment adjustment *= (cap(self.jump_threshold - (acceleration_required[2]), 0.0, self.jump_threshold) / self.jump_threshold) # factoring in how close to jump we are # we don't adjust the final target if we are already jumping final_target = self.intercept + ((car_to_intercept_perp.normalize() * adjustment) if self.jump_time == 0 else 0) # Some extra adjustment to the final target to ensure it's inside the field and # we don't try to drive through any goalposts to reach it if abs(drone.location[1] > 5150): final_target[0] = cap(final_target[0], -750, 750) local_final_target = drone.local(final_target - drone.location) # drawing debug lines to show the dodge point and final target (which differs due to the adjustment) agent.line(drone.location, self.intercept) agent.line(self.intercept - Vector3(0, 0, 100), self.intercept + Vector3(0, 0, 100), [255, 0, 0]) agent.line(final_target - Vector3(0, 0, 100), final_target + Vector3(0, 0, 100), [0, 255, 0]) angles = defaultPD(drone, local_final_target) if self.jump_time == 0: defaultThrottle(drone, speed_required) drone.controller.boost = False if abs(angles[1]) > 0.3 or drone.airborne else drone.controller.boost drone.controller.handbrake = True if abs(angles[1]) > 2.3 else drone.controller.handbrake if acceleration_required[2] > self.jump_threshold: # Switch into the jump when the upward acceleration required reaches our threshold, # hopefully we have aligned already... self.jump_time = agent.time else: time_since_jump = agent.time - self.jump_time # While airborne we boost if we're within 30 degrees of our local acceleration requirement if drone.airborne and local_acceleration_required.magnitude() * time_remaining > 100: angles = defaultPD(drone, local_acceleration_required) if abs(angles[0]) + abs(angles[1]) < 0.5: drone.controller.boost = True if self.counter == 0 and (time_since_jump <= 0.2 and local_acceleration_required[2] > 0): # hold the jump button up to 0.2 seconds to get the most acceleration from the first jump drone.controller.jump = True elif time_since_jump > 0.2 and self.counter < 3: # Release the jump button for 3 ticks drone.controller.jump = False self.counter += 1 elif local_acceleration_required[2] > 300 and self.counter == 3: # the acceleration from the second jump is instant, so we only do it for 1 frame drone.controller.jump = True drone.controller.pitch = 0 drone.controller.yaw = 0 drone.controller.roll = 0 self.counter += 1 if raw_time_remaining < -0.25 or not shot_valid(agent, self): drone.pop() drone.push(Recovery())
def run(self, drone: CarObject, agent: MyHivemind): raw_time_remaining = self.intercept_time - agent.time # Capping raw_time_remaining above 0 to prevent division problems time_remaining = cap(raw_time_remaining, 0.001, 10.0) car_to_ball = self.ball_location - drone.location # whether we are to the left or right of the shot vector side_of_shot = sign(self.shot_vector.cross((0, 0, 1)).dot(car_to_ball)) car_to_dodge_point = self.dodge_point - drone.location car_to_dodge_perp = car_to_dodge_point.cross((0, 0, side_of_shot)) # perpendicular distance_remaining = car_to_dodge_point.magnitude() speed_required = distance_remaining / time_remaining acceleration_required = backsolve(self.dodge_point, drone, time_remaining, 0 if not self.jumping else 650) local_acceleration_required = drone.local(acceleration_required) # The adjustment causes the car to circle around the dodge point in an effort to line up with the shot vector # The adjustment slowly decreases to 0 as the bot nears the time to jump adjustment = car_to_dodge_point.angle(self.shot_vector) * distance_remaining / 2.0 # size of adjustment adjustment *= (cap(self.jump_threshold - (acceleration_required[2]), 0.0, self.jump_threshold) / self.jump_threshold) # factoring in how close to jump we are # we don't adjust the final target if we are already jumping final_target = self.dodge_point + ( (car_to_dodge_perp.normalize() * adjustment) if not self.jumping else 0) + Vector3(0, 0, 50) # Ensuring our target isn't too close to the sides of the field, # where our car would get messed up by the radius of the curves # Some adjustment to the final target to ensure it's inside the field and # we don't try to dirve through any goalposts to reach it if abs(drone.location[1]) > 5150: final_target[0] = cap(final_target[0], -750, 750) local_final_target = drone.local(final_target - drone.location) # drawing debug lines to show the dodge point and final target (which differs due to the adjustment) agent.line(drone.location, self.dodge_point) agent.line(self.dodge_point - Vector3(0, 0, 100), self.dodge_point + Vector3(0, 0, 100), [255, 0, 0]) agent.line(final_target - Vector3(0, 0, 100), final_target + Vector3(0, 0, 100), [0, 255, 0]) # Calling our drive utils to get us going towards the final target angles = defaultPD(drone, local_final_target, self.direction) defaultThrottle(drone, speed_required, self.direction) agent.line(drone.location, drone.location + (self.shot_vector * 200), [255, 255, 255]) drone.controller.boost = False if abs(angles[1]) > 0.3 or drone.airborne else drone.controller.boost drone.controller.handbrake = True if abs( angles[1]) > 2.3 and self.direction == 1 else drone.controller.handbrake if not self.jumping: if raw_time_remaining <= 0.0 or (speed_required - 2300) * time_remaining > 45 or not shot_valid(agent, self): # If we're out of time or not fast enough to be within 45 units of target at the intercept time, we pop drone.pop() if drone.airborne: drone.push(Recovery()) elif local_acceleration_required[2] > self.jump_threshold \ and local_acceleration_required[2] > local_acceleration_required.flatten().magnitude(): # Switch into the jump when the upward acceleration required reaches our threshold, # and our lateral acceleration is negligible self.jumping = True else: if (raw_time_remaining > 0.2 and not shot_valid(agent, self, 150)) or raw_time_remaining <= -0.9 or ( not drone.airborne and self.counter > 0): drone.pop() drone.push(Recovery()) elif self.counter == 0 and local_acceleration_required[2] > 0.0 and raw_time_remaining > 0.083: # Initial jump to get airborne + we hold the jump button for extra power as required drone.controller.jump = True elif self.counter < 3: # make sure we aren't jumping for at least 3 frames drone.controller.jump = False self.counter += 1 elif 0.1 >= raw_time_remaining > -0.9: # dodge in the direction of the shot_vector drone.controller.jump = True if not self.dodging: vector = drone.local(self.shot_vector) self.p = abs(vector[0]) * -sign(vector[0]) self.y = abs(vector[1]) * sign(vector[1]) * self.direction self.dodging = True # simulating a deadzone so that the dodge is more natural drone.controller.pitch = self.p if abs(self.p) > 0.2 else 0 drone.controller.yaw = self.y if abs(self.y) > 0.3 else 0