def step(self, dt): delta_target = self.target - self.car.position target_direction = normalize( vec3((delta_target[0]) * self.P - self.car.velocity[0] * self.D, (delta_target[1]) * self.P - self.car.velocity[1] * self.D, 1000)) self.turn.target = look_at(target_direction, self.up) self.turn.step(dt) self.controls = self.turn.controls self.controls.boost = 0 # tap boost to keep height if (delta_target[2] - self.car.velocity[2] * 0.5) > 0: self.controls.boost = 1
def nearest_point(box, point, local=False): ''' Takes in an RLU oriented bounding box (obb) object and an RLU vec3. Returns an RLU vec3 for the closest point on box to point. local = True returns the vec3 in box's local coordinates ''' point_local = dot(point - box.center, box.orientation) closest_point_local = vec3( min(max(point_local[0], -box.half_width[0]), box.half_width[0]), min(max(point_local[1], -box.half_width[1]), box.half_width[1]), min(max(point_local[2], -box.half_width[2]), box.half_width[2])) if local: return closest_point_local return dot(box.orientation, closest_point_local) + box.center
def step(self, dt: float): if self.phase == 1 and norm(self.car.velocity) > 600: self.action = HalfFlip(self.car) self.phase = 2 if self.phase == 2 and self.action.finished: self.drive.target_pos = vec3(0, self.car.position[1], 0) self.drive.backwards = False self.action = self.drive self.phase = 3 if self.phase == 3 and norm(self.car.velocity) > 1300: self.finished = True self.action.step(dt) self.controls = self.action.controls
def step(self, packet: GameTickPacket, drone: Drone, index: int): angle = (2 * math.pi / 64) * index target = vec3(dot(rotation(angle), vec2(self.radius, 0))) target[2] = self.height # Hover controls drone.hover.target = target drone.hover.up = normalize(drone.position) drone.hover.step(self.dt) drone.controls = drone.hover.controls # If any bot got lost, now they have a chance to recover. if drone.on_ground: drone.controls.jump = True else: drone.controls.jump = False
def step(self, packet: GameTickPacket, drone: Drone, index: int): # you have access to these class attributes self.time # the current game time self.start_time # game time when this step started self.time_since_start # for how long this step has been running self.dt # time since last frame self.finished = False # set to True if you want to finish this step early # lets make the drones fly up with the Aerial mechanic drone.aerial.target = drone.position + vec3(0, 0, 500) drone.aerial.up = normalize(drone.position) # aerial.up is where the car should orient its roof when flying drone.aerial.arrival_time = self.time + 0.5 # some time in the near future so it flies fast drone.aerial.step(self.dt) drone.controls = drone.aerial.controls drone.controls.jump = True # you can modify the controls
def arc(self, pos: vec3, radius: float, start: float, end: float, segments: int = 50): step = (end - start) / segments points = [] for i in range(segments): angle = start + step * i points.append( pos + vec3(math.cos(angle) * radius, math.sin(angle) * radius, 0)) self.cyclic_polyline(points)
def step(self, packet: GameTickPacket, drones: List[Drone]): for drone in drones: drone.hover.up = normalize(drone.position) for i, layer in enumerate(self.layers): if drone.id in layer: # Calculate radius if self.time_since_start < self.radius_shrink_start: radius = 2000 elif self.time_since_start < self.radius_shrink_start + self.radius_shrink_duration: diff = 2000 - self.radii[i] radius = 2000 - diff * ( (self.time_since_start - self.radius_shrink_start) / self.radius_shrink_duration) else: radius = self.radii[i] # Calculate xy position if self.time_since_start > self.recirculation_start: a = layer.index(drone.id) angle = a * math.pi * 2 / len(layer) rot = rotation(angle) pos_xy = vec3(dot(rot, vec2(1, 0))) else: pos_xy = xy(drone.position) # Combine xy and radius drone.hover.target = normalize(pos_xy) * radius # Get height if self.time_since_start < self.separation_duration: diff = 1000 - self.heights[i] height = 1000 - diff * (self.time_since_start / self.separation_duration) else: height = self.heights[i] drone.hover.target[2] = height break drone.hover.step(self.dt) drone.controls = drone.hover.controls
def step(self, packet: GameTickPacket, drone: Drone, index: int): if self.time_since_start > self.unwind_start_time: f = self.max_frequency - (self.time_since_start - self.unwind_start_time) else: f = self.max_frequency z = (index - 31.5) / 32 # For 64 bots :^) x = math.sqrt(1 - z**2) * math.cos(z * f) y = math.sqrt(1 - z**2) * math.sin(z * f) target = vec3(x, y, z) * self.radius target[2] += self.height drone.hover.up = normalize(drone.position) drone.hover.target = target drone.hover.step(self.dt) drone.controls = drone.hover.controls
class FormATriangle(StateSettingStep): center = vec3(0, -4000, 0) def set_drone_states(self, drones: List[Drone]): for i, drone in enumerate(drones): angle = math.pi / 2 rot = rotation(angle) v = vec3(dot(rot, vec2(1, 0))) v[2] = 0.25 if i % 2 == 0: drone.position = self.center + vec3( math.floor(i / 2) * 250, math.floor(i / 2) * -250, 1750) else: drone.position = self.center + vec3( (1 + math.floor(i / 2)) * -250, (1 + math.floor(i / 2)) * -250, 1750) print(i, drone.position)
def followTick(self, packet: GameTickPacket) -> bool: isBallInCenter = packet.game_ball.physics.location.x == 0 and packet.game_ball.physics.location.y == 0 if not isBallInCenter: return False self.controller.boost = False self.controller.throttle = 1 target = self.agent.game.ball.position if self.agent.car.boost < 40 and abs( self.agent.car.position[1]) > 2816 and norm( self.agent.car.position - self.agent.game.cars[self.kickoffCar].position) > 1500: target = vec3(math.copysign(144 - 10, self.agent.car.position[0]), 2816, 0) self.controller.steer = PID.toPoint(self.agent.car, target) return True
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.info.read_game_information(packet, self.get_field_info()) if self.info.kickoff_pause and dist(self.info.ball.position, self.car.position) < 4000: self.kickoff.step(self.info.time_delta) self.controls = self.kickoff.controls return self.controls ball_prediction = self.get_ball_prediction_struct() future_goal = self.find_future_goal(ball_prediction) if future_goal and not packet.game_info.is_kickoff_pause: target = future_goal # the ball prediction is slightly wrong, the ball will be closer to the center of the goal target[0] *= 0.9 target[2] *= 0.8 target[2] += 50 else: is_seeking_our_goal = sign(self.info.ball.velocity[1]) == sign( self.car.position[1]) # even if the ball prediction didn't end up in our goal, move to the side of the ball to be ready idle_x = clip(self.info.ball.position[0], -700, 700) if is_seeking_our_goal else 0 target = vec3(idle_x, HOVER_IDLE_Y * self.sign, HOVER_IDLE_HEIGHT) # render target and ball prediction polyline = [ ball_prediction.slices[i].physics.location for i in range(0, 100, 5) ] self.renderer.draw_polyline_3d(polyline, self.renderer.yellow()) self.renderer.draw_rect_3d(target, 10, 10, True, self.renderer.cyan()) self.renderer.draw_line_3d(self.car.position, target, self.renderer.lime()) # update controls self.hover.target = target self.hover.step(self.info.time_delta) self.controls = self.hover.controls return self.controls
def simulate_flight(car: Car, aerial: Aerial, flight_path: List[vec3] = None) -> Car: test_car = Car(car) test_aerial = Aerial(test_car) test_aerial.target = aerial.target test_aerial.arrival_time = aerial.arrival_time test_aerial.angle_threshold = aerial.angle_threshold test_aerial.up = aerial.up test_aerial.single_jump = aerial.single_jump if flight_path: flight_path.clear() while not test_aerial.finished: test_aerial.step(1 / 120) test_car.boost = 100 # TODO: fix boost depletion in RLU car sim test_car.step(test_aerial.controls, 1 / 120) if flight_path: flight_path.append(vec3(test_car.position)) return test_car
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.game.read_game_information(packet, self.get_rigid_body_tick(), self.get_field_info()) # make a copy of the ball's info that we can change b = Ball(self.game.ball) ball_predictions = [] for i in range(360): # simulate the forces acting on the ball for 1 frame b.step(1.0 / 120.0) # and add a copy of new ball position to the list of predictions ball_predictions.append(vec3(b.location)) self.renderer.begin_rendering() red = self.renderer.create_color(255, 255, 30, 30) self.renderer.draw_polyline_3d(ball_predictions, red) self.renderer.end_rendering() return controller.get_output()
def simulate_flight(self, car: Car, write_to_flight_path=True) -> Car: test_car = Car(car) test_aerial = Aerial(test_car) test_aerial.target = self.aerial.target test_aerial.arrival_time = self.aerial.arrival_time test_aerial.angle_threshold = self.aerial.angle_threshold test_aerial.up = self.aerial.up test_aerial.single_jump = self.aerial.single_jump if write_to_flight_path: self._flight_path.clear() while not test_aerial.finished: test_aerial.step(1 / 120) test_car.boost = 100 # TODO: fix boost depletion in RLU car sim test_car.step(test_aerial.controls, 1 / 120) if write_to_flight_path: self._flight_path.append(vec3(test_car.position)) return test_car
def __init__(self, car: Car, target: vec3 = vec3(0, 0, 0), waste_boost=False): super().__init__(car) self.target = Arena.clamp(ground(target), 100) self.waste_boost = waste_boost self.finish_distance = 500 self._time_on_ground = 0 self.driving = True # decide whether to start driving backwards and halfflip later forward_estimate = estimate_time(car, self.target) backwards_estimate = estimate_time(car, self.target, -1) + 0.5 backwards = ( dot(car.velocity, car.forward()) < 500 and backwards_estimate < forward_estimate and (distance(car, self.target) > 3000 or distance(car, self.target) < 300) and car.position[2] < 200 ) self.drive = Drive(car, self.target, 2300, backwards) self.action = self.drive
def step(self, dt): ball = Ball(self.ball) car = self.car # simulate ball until it gets near the floor while (ball.position[2] > 120 or ball.velocity[2] > 0) and ball.time < car.time + 10: ball.step(1 / 60) ball_local = local(car, ground(ball.position)) target = local(car, self.target) shift = ground(direction(ball_local, target)) shift[1] *= 1.8 shift = normalize(shift) max_turn = clamp(norm(car.velocity) / 800, 0, 1) max_shift = normalize(vec3(1 - max_turn, max_turn * sign(shift[1]), 0)) if abs(shift[1]) > abs(max_shift[1]) or shift[0] < 0: shift = max_shift shift *= clamp(car.boost, 30, 50) shift[1] *= clamp(norm(car.velocity) / 1000, 1, 2) self._shift_direction = normalize(world(car, shift) - car.position) target = world(car, ball_local - shift) speed = distance(car.position, target) / max(0.001, ball.time - car.time) self.drive.target_speed = speed self.drive.target_pos = target self.drive.step(dt) self.controls = self.drive.controls self.finished = self.ball.position[2] < 100 or ground_distance( self.ball, self.car) > 2000
def shooting_target(agent): """"Method that gives the target for the shooting strategy""" ball = agent.info.ball car = agent.info.my_car ball_target = ball.position + 200 * normalize(vec3(vec2(agent.their_goal.center - vec3(0, 5120, 0)))) car_to_ball = ball_target - car.position backline_intersect = line_backline_intersect( agent.their_goal.center[1], vec2(car.position), vec2(car_to_ball)) if abs(backline_intersect) < 700: goal_to_ball = normalize(car.position - ball_target) error = 0 else: # Right of the ball if -500 > backline_intersect: target = agent.their_goal.corners[3] + vec3(400, 0, 0) # Left of the ball elif backline_intersect > 500: target = agent.their_goal.corners[2] - vec3(400, 0, 0) goal_to_ball = normalize(ball_target - target) # Subtract the goal to car vector difference = goal_to_ball - normalize(car.position - target) error = cap(abs(difference[0]) + abs(difference[1]), 0, 5) goal_to_ball_2d = vec2(goal_to_ball[0], goal_to_ball[1]) test_vector_2d = dot(rotation(0.5 * math.pi), goal_to_ball_2d) test_vector = vec3(test_vector_2d[0], test_vector_2d[1], 0) distance = cap((40 + distance_2d(ball_target, car.position) * (error ** 2)) / 1.8, 0, 4000) location = ball_target + vec3((goal_to_ball[0] * distance), goal_to_ball[1] * distance, 0) # this adjusts the target based on the ball velocity perpendicular # to the direction we're trying to hit it multiplier = cap(distance_2d(car.position, location) / 1500, 0, 2) distance_modifier = cap(dot(test_vector, ball.velocity) * multiplier, -1000, 1000) location += vec3( test_vector[0] * distance_modifier, test_vector[1] * distance_modifier, 0) # another target adjustment that applies if the ball is close to the wall extra = 3850 - abs(location[0]) if extra < 0: location[0] = cap(location[0], -3850, 3850) location[1] = location[1] + (-sign(agent.team) * cap(extra, -800, 800)) return location
def set_drone_states(self, drones: List[Drone]): for i, drone in enumerate(drones): if i in range(0, 16): pos = vec3(-self.radius, -self.offset, 20) angle = 0 elif i in range(16, 32): pos = vec3(self.offset, -self.radius, 20) angle = math.pi / 2 elif i in range(32, 48): pos = vec3(self.radius, self.offset, 20) angle = math.pi elif i in range(48, 64): pos = vec3(-self.offset, self.radius, 20) angle = 3 * math.pi / 2 drone.position = pos drone.position[2] += (i % 16) * self.height drone.orientation = euler_to_rotation(vec3(0, angle, 0)) drone.velocity = vec3(0, 0, 0) drone.angular_velocity = vec3(0, 0, 0)
def toRLU(self) -> vec3: return vec3(self.x, self.y, self.z)
def tick(self, packet: GameTickPacket) -> bool: if self.agent.currentTick - self.startTime > 2.8 * 120: print("Kickoff stuck!!") return False # TODO: if 2 of these bots in same team and on diagonal positions: # both do kickoff initially until it can be decided which of the enemies does the kickoff. # if self.first: # self.first = False # self.agent.set_game_state(GameState(ball=BallState(Physics(position=Vector3(0, 5300, 100))))) myTeam = self.agent.car.team self.full_kickoff_strength = True if not TOURNAMENT_MODE: for i, car in enumerate( self.agent.packet.game_cars[:self.agent.game.num_cars]): if car.team != myTeam and car.is_bot: self.full_kickoff_strength = False self.riskyStrat = False myGoalPosition = None for goal in self.agent.get_field_info().goals: if goal.team_num == myTeam: myGoalPosition = vec3(goal.location.x, goal.location.y, goal.location.z) break isBallInCenter = packet.game_ball.physics.location.x == 0 and packet.game_ball.physics.location.y == 0 # TODO: wait for https://github.com/RLBot/RLBot/issues/486 if not packet.game_info.is_round_active and isBallInCenter: if self.closestKickoffCars == None: # figure out who does what lowestKickoffDistance = math.inf for i, car in enumerate( self.agent.game.cars[:self.agent.game.num_cars]): if car.team == myTeam: kickoffDistance = round( norm(car.position - self.agent.game.ball.position)) if kickoffDistance + 5 <= lowestKickoffDistance: lowestKickoffDistance = kickoffDistance self.closestKickoffCars = [i] elif abs(kickoffDistance - lowestKickoffDistance) <= 5: self.closestKickoffCars.append(i) if len(self.closestKickoffCars) == 1: self.kickoffCar = self.closestKickoffCars[0] print( f"[{self.agent.index}] kickoff car is {self.kickoffCar}, alone" ) else: self.kickoffCar = self.closestKickoffCars[0] print( f"[{self.agent.index}] kickoff car is {self.kickoffCar} of {self.closestKickoffCars}" ) if self.agent.index == self.kickoffCar: self.agent.send_quick_chat( QuickChats.CHAT_TEAM_ONLY, QuickChats.Information_IGotIt) else: self.agent.send_quick_chat( QuickChats.CHAT_TEAM_ONLY, QuickChats.Information_TakeTheShot) # TODO: implement quick chat based kickoff, see https://github.com/RLBot/RLBot/issues/486 # # print("multiple kickoff cars:", self.closestKickoffCars, self.agent.index) # if self.agent.index in self.closestKickoffCars: # print(f"[{self.agent.index}] send quickchat: {QuickChats.Information_IGotIt}") # self.agent.send_quick_chat(QuickChats.CHAT_TEAM_ONLY, QuickChats.Information_IGotIt) if self.kickoffCar != None and self.furthestDefendCars == None: highestDefendDistance = 0 for i, car in enumerate( self.agent.game.cars[:self.agent.game.num_cars]): if car.team == myTeam and i != self.kickoffCar: defendDistance = round( norm(car.position - myGoalPosition)) if defendDistance - 5 >= highestDefendDistance: highestDefendDistance = defendDistance self.furthestDefendCars = [i] elif abs(defendDistance - highestDefendDistance) <= 5: self.furthestDefendCars.append(i) if self.furthestDefendCars == None: self.furthestDefendCars = [] print(f"[{self.agent.index}] no followup cars") elif len(self.furthestDefendCars) == 1: self.followCar = self.furthestDefendCars[0] print( f"[{self.agent.index}] followup car is {self.followCar}, alone" ) else: self.followCar = self.furthestDefendCars[0] print( f"[{self.agent.index}] followup car is {self.followCar} of {self.furthestDefendCars}" ) if self.agent.index == self.followCar: self.agent.send_quick_chat( QuickChats.CHAT_TEAM_ONLY, QuickChats.Information_InPosition) else: self.agent.send_quick_chat( QuickChats.CHAT_TEAM_ONLY, QuickChats.Information_Defending) # TODO: implement quick chat based kickoff, see https://github.com/RLBot/RLBot/issues/486 # if self.agent.index in self.furthestDefendCars: # print(f"[{self.agent.index}] send quickchat: {QuickChats.Information_Defending}") # self.agent.send_quick_chat(QuickChats.CHAT_TEAM_ONLY, QuickChats.Information_Defending) if self.agent.index == self.kickoffCar: return self.kickoffTick(packet) elif self.agent.index == self.followCar: return self.followTick(packet) else: return self.defendTick(packet, myGoalPosition)
def defendTick(self, packet: GameTickPacket, myGoalPosition: vec3) -> bool: speed = norm(self.agent.car.velocity) if speed < 10: self.startTime = self.agent.currentTick self.isDiagonalKickoff = abs(self.agent.car.position[0]) >= 1024 self.isSemiDiagonalKickoff = abs( abs(self.agent.car.position[0]) - 256) < 128 self.isStraightKickoff = not self.isDiagonalKickoff and not self.isSemiDiagonalKickoff self.controller.boost = False self.controller.pitch = 0 self.controller.yaw = 0 self.controller.roll = 0 if self.isStraightKickoff: self.controller.steer = 0 if abs(self.agent.car.position[1] ) > 4444 and self.controller.throttle != -1: self.controller.throttle = 1 else: if self.agent.car.velocity[1] * ( 2 * self.agent.car.team - 1) < 0 or abs( self.agent.car.position[1]) < 4466: self.controller.throttle = -1 else: return self.agent.stateMachine.changeStateAndContinueTick( HalfFlip, packet, True, True) elif self.isSemiDiagonalKickoff: self.controller.throttle = -1 if abs(self.agent.car.position[1]) < 3940: self.controller.steer = -math.copysign( 1, self.agent.car.position[0]) * (2 * self.agent.car.team - 1) else: self.controller.steer = PID.toPointReverse( self.agent.car, vec3(0, math.copysign(5400, self.agent.car.position[1]), 0)) if abs(self.agent.car.position[1] ) > 3990 and self.controller.steer * math.copysign( 1, self.agent.car.position[0]) * ( 2 * self.agent.car.team - 1) < 0.1: return self.agent.stateMachine.changeStateAndContinueTick( HalfFlip, packet, True, True) return True else: self.controller.throttle = -1 self.controller.steer = PID.toPointReverse( self.agent.car, vec3(math.copysign(3072, self.agent.car.position[0]), math.copysign(4096, self.agent.car.position[1]), 0)) if abs(self.agent.car.position[1] ) < 2650 or self.controller.steer * math.copysign( 1, self.agent.car.position[0]) * ( 2 * self.agent.car.team - 1) < -0.1: return True return self.agent.stateMachine.changeStateAndContinueTick( HalfFlip, packet) return True
def kickoffTick(self, packet: GameTickPacket) -> bool: isBallInCenter = packet.game_ball.physics.location.x == 0 and packet.game_ball.physics.location.y == 0 carToBallDistance = norm(self.agent.car.position - self.agent.game.ball.position) # if at any point something goes wrong and i'll be way too late, cancel the kickoff early. if isBallInCenter: for car in self.agent.game.cars[:self.agent.game.num_cars]: if car.team != self.agent.car.team and norm( car.position - self.agent.game.ball.position ) + 200 < carToBallDistance: print("kickoff failed!! big problems..") self.agent.send_quick_chat(QuickChats.CHAT_EVERYONE, QuickChats.Apologies_Cursing) return False speed = norm(self.agent.car.velocity) self.controller.throttle = 1 self.controller.boost = speed < 2300 - 991.666 / 120 * ( 1 if self.controller.boost else 10) and self.agent.car.boost > 0 if speed < 10: self.startTime = self.agent.currentTick self.isDiagonalKickoff = abs(self.agent.car.position[0]) >= 1024 self.isSemiDiagonalKickoff = abs( abs(self.agent.car.position[0]) - 256) < 128 self.isStraightKickoff = not self.isDiagonalKickoff and not self.isSemiDiagonalKickoff if self.isSemiDiagonalKickoff: # force speedflip direction to pick up boost self.speedFlipDirection = math.copysign( 1, self.agent.car.position[0] * (2 * self.agent.car.team - 1)) self.speedFlipMaxTurnTicks = 15 self.riskyStrat = self.full_kickoff_strength if self.speedFlipState == 0 and self.isDiagonalKickoff and self.alternateDiagonalKickoff: self.alternateDiagonalKickoff = False for car in self.agent.game.cars[:self.agent.game.num_cars]: if car.team != self.agent.car.team: if norm(car.position - self.agent.game.ball.position ) < carToBallDistance + 150: try: crossWithYAxis = ( car.position[1] - self.agent.game.ball.position[1] - (car.position[0] - self.agent.game.ball.position[0]) / car.velocity[0] * car.velocity[1]) * ( 2 * car.team - 1) if crossWithYAxis > 300: self.alternateDiagonalKickoff = True break except ZeroDivisionError: self.alternateDiagonalKickoff = True break target = self.agent.game.ball.position # LOGIC TO CANCEL THE RISKY STRAT if self.riskyStrat: if self.isDiagonalKickoff: # check if enemy car is also doing a fast strat if carToBallDistance < 1500: for car in self.agent.game.cars[:self.agent.game.num_cars]: if car.team != self.agent.car.team: try: enemyTime = norm(car.position - self.agent.game.ball.position ) / norm(car.velocity) myTime = carToBallDistance / norm( self.agent.car.velocity) lead = enemyTime - myTime if lead < 0.09: self.riskyStrat = False break except ZeroDivisionError: pass else: for car in self.agent.game.cars[:self.agent.game.num_cars]: if car.team != self.agent.car.team: # if the enemy car jumped, fall back to safe strat immediately if norm(car.position - self.agent.game.ball.position ) < 1500 and car.position[2] > 20: self.riskyStrat = False break lead = norm( car.position - self.agent.game.ball.position) - carToBallDistance # if enemy car is too far, it also has to fall back to safe strat instead of reacting to it if lead < 1500 and ( norm(target - self.agent.car.position) < 525 and lead > 460): self.riskyStrat = False break # LOGIC TO ADJUST THE TARGET TO AIM THE BALL if self.riskyStrat: if self.isDiagonalKickoff: if self.alternateDiagonalKickoff: # aim for the wall to make a pass target += vec3( math.copysign(100, self.agent.car.position[0]), 0, 0) else: # aim straight for the goal if carToBallDistance > 1000: target += vec3(0, 190 * (self.agent.car.team * 2 - 1), 0) else: target += vec3( math.copysign(3, self.agent.car.position[0]), 140 * (self.agent.car.team * 2 - 1), 0) else: if self.isSemiDiagonalKickoff: offset = 29 else: offset = 26 target += vec3( math.copysign(offset, self.agent.car.position[0]), 0, 0) else: target += vec3(0, 90 * (self.agent.car.team * 2 - 1), 0) # force to pick up boost if self.isSemiDiagonalKickoff and abs( self.agent.car.position[1]) > 1700: self.speedFlipTarget = vec3(0, 1800 * (2 * self.agent.car.team - 1), 70) else: self.speedFlipTarget = target # always drive straight at start if speed < 550: self.controller.steer = 0 self.controller.jump = False return True else: carAngle = -atan2(self.agent.car.forward()) self.controller.steer = min( 1, max( -1, 5 * atan2( rotate2(target - self.agent.car.position, carAngle)))) if super().tick(packet): return True else: if self.riskyStrat: if self.isDiagonalKickoff: maxJumpDistance = 210 elif self.isSemiDiagonalKickoff: maxJumpDistance = 230 else: maxJumpDistance = 242 self.controller.jump = carToBallDistance < maxJumpDistance return isBallInCenter else: self.agent.reorientML.target_orientation = look_at( self.agent.game.ball.position - self.agent.car.position, vec3(0, 0, 1)) self.agent.reorientML.step(1 / self.agent.FPS) self.controller.yaw = self.agent.reorientML.controls.yaw self.controller.pitch = self.agent.reorientML.controls.pitch self.controller.roll = self.agent.reorientML.controls.roll if isBallInCenter: if norm(target - self.agent.car.position) < 525: if not self.hasUnJumped: jump = self.agent.game.ball.position[ 2] > self.agent.car.position[2] if self.controller.jump and not jump: self.hasUnJumped = True self.controller.jump = jump else: if speed < 2200: for car in self.agent.game.cars[:self.agent.game. num_cars]: if car.team != self.agent.car.team: if norm(car.position - self.agent.game.ball.position ) < 1000: if not self.hasUnJumped: self.controller.jump = False self.hasUnJumped = True elif self.controller.jump == False: self.controller.jump = True self.controller.pitch = -1 self.controller.yaw = 0 else: return False break # if no enemies have been found nearby, no need to dodge into the ball again return False return True
def spherical(vec: vec3) -> vec3: """Converts from cartesian to spherical coordinates.""" radius = norm(vec) + 1e-9 inclination = math.acos(vec[2] / radius) azimuth = math.atan2(vec[1], vec[0]) return vec3(radius, Range180(PI / 2 - inclination), azimuth)
def tick(self, packet: GameTickPacket) -> bool: #self.agent.renderer.begin_rendering() kickoff = packet.game_info.is_round_active and packet.game_info.is_kickoff_pause carAccelerations = [] for i in range(packet.num_cars): car = packet.game_cars[i] velocity = vec3(car.physics.velocity.x, car.physics.velocity.y, car.physics.velocity.z) carAccelerations.append((velocity - self.lastVelocities[i]) / self.agent.ticksThisPacket * 120) self.lastVelocities[i] = velocity # car info myCar = packet.game_cars[self.agent.index] carLocation = Vec3(myCar.physics.location) carVelocity = Vec3(myCar.physics.velocity) carSpeed = carVelocity.length() # ball info realBallLocation = ballLocation = Vec3( packet.game_ball.physics.location) ballVelocity = Vec3(packet.game_ball.physics.velocity) if ballLocation.z < 100: self.balanceTime = 0 #return False action_display = f"Air time: {self.balanceTime}" self.balanceTime += 1 # unstuck goal hack if abs(carLocation.y) > 5100: ballLocation.x = 0 # target ball info #targetBallLocation, targetBallVelocity, targetAngle = getTargetBall(self.agent, packet, carLocation) teamDirection = 1 if packet.game_cars[ self.agent.index].team == 0 else -1 sidewaysDiff = abs(carLocation.x) - 893 + 100 isInCorner = sidewaysDiff > 0 if isInCorner: sidewaysDiff = max( 0, sidewaysDiff + 0.4 * (carLocation.y * teamDirection - (5120 - 100))) inTriangleAmount = max(0, min(1, sidewaysDiff / 4500)) scale = 0.55 else: scale = 2 inTriangleAmount = 0 targetBallLocation = Vec3(0, (5120 + 100 - sidewaysDiff * scale) * teamDirection, 0) ## if teammate is closer to the ball, go to defend position. ballToCarAbsoluteLocation = (ballLocation - carLocation).flat() ballToCarDistance = ballToCarAbsoluteLocation.length() futurePositionScoreVector = ballLocation + 1 * ballVelocity - carLocation positionScore = Vec3(futurePositionScoreVector.x, futurePositionScoreVector.y * (1 if futurePositionScoreVector.y * (2*self.agent.team-1) < 0 else 3), 0).length()\ + Vec3(ballToCarAbsoluteLocation.x, ballToCarAbsoluteLocation.y * (1 if ballToCarAbsoluteLocation.y * (2*self.agent.team-1) < 0 else 3), 0).length() beAnnoying = False for carIndex in range(packet.num_cars): car = packet.game_cars[carIndex] if car.team == self.agent.team and carIndex != self.agent.index and not car.is_demolished: OtherCarToBall = ballLocation - Vec3(car.physics.location) OtherFutureCarToBall = ballLocation + 1 * ballVelocity - Vec3( car.physics.location) otherPositionScore = Vec3(OtherCarToBall.x , OtherCarToBall.y * (1 if OtherCarToBall.y * (2*self.agent.team-1) < 0 else 3), 0).length()\ + Vec3(OtherFutureCarToBall.x, OtherFutureCarToBall.y * (1 if OtherFutureCarToBall.y * (2*self.agent.team-1) < 0 else 3), 0).length() # print(f"[{self.agent.index} {round(positionScore)}] {carIndex}: {round(otherPositionScore)}!") if otherPositionScore + math.copysign( 5, carIndex - self.agent.index) < positionScore: # print(f"{self.agent.index} other one is closer!") teamClosestDistance = math.inf enemyClosestDistance = math.inf for carIndex in range(packet.num_cars): car = packet.game_cars[carIndex] distance = ( ballLocation - Vec3(car.physics.location)).flat().length() if car.team == self.agent.team: teamClosestDistance = min(teamClosestDistance, distance) else: enemyClosestDistance = min(enemyClosestDistance, distance) teamHasBallControl = teamClosestDistance - 500 < enemyClosestDistance targetScore = math.inf target = None for carIndex in range(packet.num_cars): car = packet.game_cars[carIndex] # print(f"[{self.agent.index} {self.agent.team}] {carIndex} {car.team}") if car.team != self.agent.team: score = ( ballLocation - Vec3(car.physics.location) ).flat().length() + teamHasBallControl * ( Vec3(0, 5120 * (2 * car.team - 1), 0) - Vec3(car.physics.location)).flat().length() # print(f"[{self.agent.index}] considering car {carIndex}") if score < targetScore: targetScore = score target = car if target != None: beAnnoying = True huntLocation = Vec3(target.physics.location) for _ in range(20): time = min( .6, 900 / max(1, Vec3(target.physics.velocity).length()), (carLocation - huntLocation).length() / max(carSpeed, 1)) huntLocation = Vec3( target.physics.location) + time * Vec3( target.physics.velocity) ballLocation = huntLocation ballVelocity = Vec3(0, 0, 0) #Vec3(target.physics.velocity) ballToCarAbsoluteLocation = (ballLocation - carLocation).flat() ballToCarDistance = ballToCarAbsoluteLocation.length() break ## if convenient, change ball location to nearby boost pad. fieldInfo = self.agent.get_field_info() carFutureLocation = carLocation + 0.2 * carVelocity ballToFutureCarAbsoluteLocation = (ballLocation - carFutureLocation).flat() ballToFutureCarDistance = ballToFutureCarAbsoluteLocation.length() goingForBoost = False if ballToCarDistance > 250 and myCar.boost < 88: convenientBoostPads = [] costs = [] for i in range(fieldInfo.num_boosts): if not packet.game_boosts[i].is_active: continue boostPad = fieldInfo.boost_pads[i] boostLocation = Vec3(boostPad.location) maxOffset = (208 if boostPad.is_full_boost else 144) - 20 orth = (boostLocation - carLocation).orthogonalize(ballToCarAbsoluteLocation) boostLocation -= orth.normalized() * min( orth.length(), maxOffset) carToBoostLength = (boostLocation - carFutureLocation).length() detourLength = (ballLocation - boostLocation).length() + carToBoostLength cost = (detourLength - ballToFutureCarDistance) / ( 1450 if boostPad.is_full_boost else 250) costs.append(cost) if cost < ((100 - myCar.boost) / 100)**1.5: convenientBoostPads.append( (i, carToBoostLength * cost, boostLocation)) #self.agent.renderer.draw_line_3d(boostLocation, boostLocation + Vec3(0, 0, 100), self.agent.renderer.pink()) #print(round(min(costs), 1)) if len(convenientBoostPads) > 0: convenientBoostPads.sort(key=lambda b: b[1], reverse=False) boostPad = fieldInfo.boost_pads[convenientBoostPads[0][0]] boostLocation = convenientBoostPads[0][2] #self.agent.renderer.draw_line_3d(boostLocation, boostLocation + Vec3(0, 0, 400), self.agent.renderer.pink()) ballLocation = boostLocation ballVelocity = Vec3(0, 0, 0) ballToCarAbsoluteLocation = (ballLocation - carLocation).flat() ballToCarDistance = ballToCarAbsoluteLocation.length() goingForBoost = True ## time to next bounce if not goingForBoost: pass ## calculate angles ballDirection = math.atan2(ballVelocity.y, -ballVelocity.x) carDirection = -myCar.physics.rotation.yaw carToBallAngle = math.atan2( ballToCarAbsoluteLocation.y, -ballToCarAbsoluteLocation.x) - carDirection if abs(carToBallAngle) > math.pi: if carToBallAngle > 0: carToBallAngle -= 2 * math.pi else: carToBallAngle += 2 * math.pi ballToTargetAbsoluteLocation = (ballLocation - targetBallLocation).flat() carToTargetAngle = math.atan2( ballToTargetAbsoluteLocation.y, -ballToTargetAbsoluteLocation.x) - carDirection if abs(carToTargetAngle) > math.pi: if carToTargetAngle > 0: carToTargetAngle -= 2 * math.pi else: carToTargetAngle += 2 * math.pi carToTargetAbsoluteLocation = (carLocation - targetBallLocation).flat() ## separate into steering and throttle components ballToCarLocation = ballToCarAbsoluteLocation.rotate_2D(carDirection) ballToTargetLocation = ballToTargetAbsoluteLocation.rotate_2D( carDirection) carToTargetLocation = carToTargetAbsoluteLocation.rotate_2D( carDirection) ballToCarVelocity = (ballVelocity - carVelocity).flat().rotate_2D(carDirection) #carToTargetVelocity = (carVelocity - targetBallVelocity).flat().rotate_2D(carDirection) maxSpeed = max(1410, min(2300, 1410 + (2300 - 1410) / 33 * myCar.boost)) carToMaxSpeed = carVelocity.flat().length() - maxSpeed desiredSpeed = 1200 if ballToTargetLocation.y < 500: self.carToTargetIntegral += ballToTargetLocation else: self.carToTargetIntegral = Vec3() canYeet = myCar.has_wheel_contact \ and (not goingForBoost) \ and ballToCarLocation.length() < 275 \ and ballLocation.z > 100 \ and ballLocation.z < 275 \ and packet.game_info.seconds_elapsed - packet.game_ball.latest_touch.time_seconds < 0.1 teamDirection = 1 if packet.game_cars[ self.agent.index].team == 0 else -1 inCornerDegree = math.atan( (max(abs(carLocation.x), 893) - 893) / max(5120 - carLocation.y * teamDirection, 1)) shouldYeet = ((ballLocation + 1 * ballVelocity).flat() * teamDirection - Vec3(0, 5120+100, 0)).length() < 1500 \ and inCornerDegree < math.pi * 2 / 6 \ and 4200 - abs(ballLocation.y) < 0.7 * abs(ballVelocity.y) #print(f"{canYeet}\t{shouldYeet}\t{round(4200 - abs(ballLocation.y))}\t{round(0.7 * abs(ballVelocity.y))}") #action_display = f"{round((ballLocation.flat() - Vec3(0, 5120+100 * teamDirection, 0)).length())}" carlocs = [] if canYeet and shouldYeet: inComingCar = False for i in range(packet.num_cars): if i == self.agent.index: continue car = packet.game_cars[i] if car.team == myCar.team or car.is_demolished: continue #print(round(0.1 + norm(carAccelerations[i]) / RLUDrive.throttle_accel(Vec3(car.physics.velocity).length()), 2)) for throttle in ( 0, min( 1, 0.1 + norm(carAccelerations[i]) / RLUDrive.throttle_accel( Vec3(car.physics.velocity).length()))): carBoost = car.boost attackerCarLocation = Vec3(car.physics.location) # divide by 120, to go from per second to per frame STEPSIZE = 120 gravity = packet.game_info.world_gravity_z / STEPSIZE**2 attackerCarVelocity = vec3( car.physics.velocity.x, car.physics.velocity.y, car.physics.velocity.z) / STEPSIZE attackerCarAngular = axis_to_rotation( vec3(car.physics.angular_velocity.x, car.physics.angular_velocity.y, car.physics.angular_velocity.z) / STEPSIZE) ballV = ballVelocity / STEPSIZE for j in range(round(STEPSIZE * 0.7)): # simulate 40 ticks forwards attackerCarLocation += Vec3(attackerCarVelocity[0], attackerCarVelocity[1], attackerCarVelocity[2]) if car.has_wheel_contact: attackerCarVelocity = dot(attackerCarAngular, attackerCarVelocity) attackerCarVelocity += vec3(0, 0, gravity) if throttle == 0: attackerCarVelocity -= vec3( math.copysign( min(525 / STEPSIZE, abs(attackerCarVelocity[0])), attackerCarVelocity[0]), 0, 0) else: acceleration = (991.667 * (carBoost > 0) + RLUDrive.throttle_accel( norm(attackerCarVelocity))) attackerCarVelocity += normalize( attackerCarVelocity) * acceleration / STEPSIZE if attackerCarLocation.z < ballLocation.z: attackerCarLocation.z = ballLocation.z carlocs.append(attackerCarLocation) if (attackerCarLocation - ballLocation + j * ballV).flat().length( ) < 750: # longest car has a diagonal of 157uu inComingCar = True break #print(f"{j}\t{ (attackerCarLocation - ballLocation + j * ballV).flat().length()}") if inComingCar: break carBoost -= 1 / 3 / STEPSIZE if inComingCar: self.agent.stateMachine.changeStateMidTick(Yeet) return inComingCar if kickoff and (carLocation - realBallLocation ).length() < 800 and myCar.has_wheel_contact: self.agent.stateMachine.changeStateMidTick(Frontflip) return True ## STEERING steer = 0 steerBias = 0 # ball to car proportional #print(f"{round(min(15, max(-15, 0.02 * ballToCarLocation.y)), 2)}\t{round(0.003 * ballToCarVelocity.y, 2)}") steer += min(15, max(-15, 0.02 * ballToCarLocation.y)) if not goingForBoost: # ball to car derivative steer += 0.005 * ballToCarVelocity.y #print(f"pos: {round(min(15, max(-15, 0.02 * ballToCarLocation.y)), 2)}\tvel: {round(0.009 * ballToCarVelocity.y,2)}") # ball to target proportional targetSteer = ballToTargetLocation.y #action_display = f"{round(carToTargetLocation.x)}" if carToTargetLocation.x > 300: targetSteer = math.copysign(100000, targetSteer) steerBias += 0.005 * targetSteer # ball to target derivative #steerBias += 0.002 * carToTargetVelocity.y # ball to target integral #steerBias += 0.000001 * self.carToTargetIntegral.y #print(f"{round(steerBias, 1)}\t{round(0.008 * carToTargetVelocity.y, 1)}") applySpeedLimit = True if kickoff or beAnnoying: self.steerBiasLimit = 0 if abs(carLocation.x) < 930 and abs( carLocation.y) > 5120 - 550 and ballLocation.z > 500: self.steerBiasLimit = 2.5 applySpeedLimit = False if ballLocation.z > 160 or ballToCarLocation.length() > 800: self.steerBiasLimit = max(0.5, self.steerBiasLimit - 0.1) elif ballLocation.z < 100: self.steerBiasLimit = max(0.5, self.steerBiasLimit - 0.1) else: self.steerBiasLimit = min( 2.5, 1 + 1 * max(0, carSpeed - 600) / 1800, self.steerBiasLimit + 0.065) if applySpeedLimit and ballToCarLocation.length() < 180: self.steerBiasLimit = min( self.steerBiasLimit, 1.3 + (1400 - carVelocity.flat().length()) / 800) steer += min(self.steerBiasLimit, max(-self.steerBiasLimit, steerBias)) action_display = f"SBL {round(self.steerBiasLimit, 1)} SB: {round(min(self.steerBiasLimit, max(-self.steerBiasLimit, steerBias)), 1)}" #action_display = f"{round(ballToTargetLocation.x)}" ## THROTTLE throttle = 0 # ball to car proportional throttle += 0.07 * ballToCarLocation.x # ball to car derivative throttle += 0.015 * ballToCarVelocity.x #print(ballVelocity.length()) if ( ballToCarLocation.length() < 300 and not (abs(ballToCarLocation.y) > 100 and ballVelocity.length() < 500) ) and not beAnnoying: # if the ball is too far from the car, use speed to drive car to ball throttleBias = 0 ## NORMAL TARGET BIAS #ball to target proportional #throttleBias += 0.004 * ballToTargetLocation.x # ball to target derivative if ballLocation.z > 100: #action_display = f"triangle: {round((1 - inTriangleAmount), 1)}\ttargetangle: {round(0.8*math.cos(carToTargetAngle/2), 1)}" carToDesiredSpeed = carVelocity.flat().length( ) - desiredSpeed * max(0.2, (1 - inTriangleAmount)) throttleBias += 0.005 * carToDesiredSpeed # ball to target integral #throttleBias += 0.00001 * self.carToTargetIntegral.x ## STEERING HELP BIAS WHEN FAR AWAY #targetSteeringSpeed = 400 + 3000 * math.pow(math.cos(carToTargetAngle/2), 16) #throttleSteeringBias = max(-1, 3 * (carSpeed - targetSteeringSpeed) / 1400) # alpha = max(0, min(1, (ballToTargetLocation.length() - 1000) / 3000)) # throttleBias = throttleSteeringBias * alpha + throttleBias * (1 - alpha) throttle += min(2, max(-0.9, throttleBias)) #action_display = f"TB: {round(throttleBias, 1)}\tT: {round(throttle, 1)}" else: throttle = 1 - 0.8 * math.cos(carToBallAngle) #print(action_display) if goingForBoost: throttle = max(throttle, 1) ## set controller state self.controller.steer = min(1, max(-1, steer)) self.controller.throttle = min(1, max(-1, throttle)) if myCar.has_wheel_contact and throttle > 1.7 and carLocation.z < 100 and realBallLocation.z < 500: self.controller.boost = carSpeed < 2300 - 991.667 / 120 * ( 1 if self.controller.boost else 10) else: self.controller.boost = False ## test if forward dodge is needed if abs( steer ) < 0.5 and not kickoff and carSpeed > 1400 and carSpeed < 2200 and ( myCar.boost == 0 or carSpeed > 2300 - 20 - 500): try: angleCoeff = carVelocity.normalized().dot( ballVelocity.normalized()) except: angleCoeff = -1 if angleCoeff > 0.95: dist = (realBallLocation - carLocation).length() vel = (carSpeed + 500 - ballVelocity.length()) time = dist / vel ballAfterLocation = realBallLocation + time * ballVelocity isStillInMap = abs( ballAfterLocation.x) < 4096 + 500 and abs( ballAfterLocation.y) < 5120 + 500 if time > 1.5: self.agent.stateMachine.changeStateMidTick(Frontflip) return True # print(self.ballToTargetIntegral) # action_display = f"steer: {round(ballToTargetLocation.y)}" # action_display = f"distance: {round(ballToTargetLocation.x)}" # # Find the direction of our car using the Orientation class #car_orientation = Orientation(myCar.physics.rotation).forward #car_direction = car_orientation.forward # steer_correction_radians = find_correction(car_direction, ballToCarLocation) # turnProportional = max(-1, min(1, steer_correction_radians * 4)) # #action_display = f"turn {round(turn, 2)}" # self.controller.steer = turnProportional # throttleProportional = 10 # speed = Vec3.length(myCar.physics.velocity) # targetSpeed = min(boostSpeed, Vec3.dist(ballLocation, carLocation) * 5 * math.cos(steer_correction_radians)) # self.controller.throttle = max(-1, min(1, (targetSpeed - speed) * 1000)) # self.controller.steer = turnProportional # self.controller.boost = speed < targetSpeed if self.controller.boost or (abs(turnProportional) < 1 and targetSpeed > normalSpeed) else (abs(turnProportional) < 1 and speed < targetSpeed - 400) # targetBallLocation.z = 150 #draw_debug(self.agent, myCar, packet.game_ball, action_display, targetBallLocation, carlocs) return True
from rlutilities.simulation import Car, Navigator from rlutilities.mechanics import FollowPath from rlutilities.linear_algebra import vec3, normalize c = Car() c.time = 0.0 c.position = vec3(0, 0, 0) c.velocity = vec3(1000, 0, 0) c.angular_velocity = vec3(0.1, -2.0, 1.2) c.on_ground = False navigator = Navigator(c) drive = FollowPath(c) drive.arrival_speed = 1234 drive.path = navigator.path_to(vec3(1000, 0, 0), vec3(1, 0, 0), 1000) for p in drive.path.points: print(p)
def Vec3_to_vec3(vector, team_sign): return vec3(team_sign * vector.x, team_sign * vector.y, vector.z)
def set_ball_state(self, ball: Ball): ball.position = vec3(0, 0, 3000) ball.velocity = vec3(0, 0, 0) ball.angular_velocity = vec3(0, 0, 0)
def vector3_to_vec3(v: Vector3) -> vec3: return vec3(v.x, v.y, v.z)
def center(self): return vec3(0, -self.sign * Goal.DISTANCE, Goal.HEIGHT / 2.0)
def rotator_to_mat3(r: Rotator) -> mat3: return euler_to_rotation(vec3(r.pitch, r.yaw, r.roll))