class CollectBoost(BaseAction): action = None def get_output(self, info: Game) -> SimpleControllerState: car = info.my_car if not self.action: self.action = Drive(car) boost_pad = closest_available_boost(car.location, info.pads) if boost_pad is None: # All boost pads are inactive. return self.controls self.action.target = boost_pad.location self.action.step(info.time_delta) self.controls = self.action.controls return self.controls def get_possible(self, info: Game): return True def update_status(self, info: Game): if info.my_car.boost == 100: self.finished = True
def __init__(self, car: Car, info: Game): self.target: vec3 = None self.car: Car = car self.info = info self.hover = Hover(car) self.drive = Drive(car) self.controls: Input = Input() self.__time_spent_on_ground = 0.0
class Kickoff: def __init__(self, car: Car, info: Game): self.car: Car = car self.info: Game = info self.controls = Input() self.drive = Drive(car) def step(self, dt): self.drive.target = self.info.ball.position self.drive.speed = 1500 self.drive.step(self.info.time_delta) self.controls = self.drive.controls
def __init__(self, name, team, index): super().__init__(name, team, index) Game.set_mode("soccar") self.game = Game(index, team) self.name = name self.controls = SimpleControllerState() self.timer = 0.0 self.drive = Drive(self.game.my_car) self.dodge = None self.turn = None self.state = State.RESET
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.game.read_game_information(packet, self.get_rigid_body_tick(), self.get_field_info()) self.controls = SimpleControllerState() next_state = self.state if self.state == State.RESET: self.timer = 0.0 self.set_gamestate_straight_moving() # self.set_gamestate_angled_stationary() # self.set_gamestate_straight_moving_towards() next_state = State.WAIT if self.state == State.WAIT: if self.timer > 0.2: next_state = State.INITIALIZE if self.state == State.INITIALIZE: self.drive = Drive(self.game.my_car) self.drive.target, self.drive.speed = self.game.ball.location, 2300 next_state = State.DRIVING if self.state == State.DRIVING: self.drive.target = self.game.ball.location self.drive.step(self.game.time_delta) self.controls = self.drive.controls can_dodge, simulated_duration, simulated_target = self.simulate() if can_dodge: self.dodge = Dodge(self.game.my_car) self.turn = AerialTurn(self.game.my_car) print("============") print(simulated_duration) self.dodge.duration = simulated_duration - 0.1 self.dodge.target = simulated_target self.timer = 0 next_state = State.DODGING if self.state == State.DODGING: self.dodge.step(self.game.time_delta) self.controls = self.dodge.controls if self.game.time == packet.game_ball.latest_touch.time_seconds: print(self.timer) if self.dodge.finished and self.game.my_car.on_ground: next_state = State.RESET self.timer += self.game.time_delta self.state = next_state return self.controls
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.game.read_game_information(packet, self.get_rigid_body_tick(), self.get_field_info()) if not self.action: self.action = Drive(self.game.my_car) self.action.speed = 1400 self.action.target = self.game.ball.location self.action.step(self.game.time_delta) self.controls = self.action.controls return self.controls
def estimate_time(car: Car, target, dd=1) -> float: turning_radius = 1 / Drive.max_turning_curvature(norm(car.velocity) + 500) turning = angle_between(car.forward() * dd, direction( car, target)) * turning_radius / 1800 if turning < 0.5: turning = 0 dist = ground_distance(car, target) - 200 if dist < 0: return turning speed = dot(car.velocity, car.forward()) time = 0 result = None if car.boost > 0 and dd > 0: boost_time = car.boost / 33.33 result = BOOST.simulate_until_limit(speed, distance_limit=dist, time_limit=boost_time) dist -= result.distance_traveled time += result.time_passed speed = result.speed_reached if dist > 0 and speed < 1410: result = THROTTLE.simulate_until_limit(speed, distance_limit=dist) dist -= result.distance_traveled time += result.time_passed speed = result.speed_reached if result is None or not result.distance_limit_reached: time += dist / speed return time * 1.05 + turning
def set_steer(agent): if agent.game.my_car.on_ground: agent.step = Step.Steer target = agent.game.ball.location agent.drive = Drive(agent.game.my_car) agent.drive.target = target agent.drive.speed = 2400
def get_output(self, info: Game) -> SimpleControllerState: car = info.my_car if not self.action: self.action = Drive(car) boost_pad = closest_available_boost(car.location, info.pads) if boost_pad is None: # All boost pads are inactive. return self.controls self.action.target = boost_pad.location self.action.step(info.time_delta) self.controls = self.action.controls return self.controls
class Derevo(BaseAgent): """Main bot class""" def __init__(self, name, team, index): """Initializing all parameters of the bot""" super().__init__(name, team, index) Game.set_mode("soccar") self.game = Game(index, team) self.name = name self.team = team self.index = index self.drive = None self.dodge = None self.controls = SimpleControllerState() self.kickoff = False self.prev_kickoff = False self.kickoffStart = None self.step = None def initialize_agent(self): """Initializing all parameters which require the field info""" init_boostpads(self) self.drive = Drive(self.game.my_car) self.dodge = Dodge(self.game.my_car) def get_output(self, packet: GameTickPacket) -> SimpleControllerState: """The main method which receives the packets and outputs the controls""" self.game.read_game_information(packet, self.get_rigid_body_tick(), self.get_field_info()) update_boostpads(self, packet) self.prev_kickoff = self.kickoff self.kickoff = packet.game_info.is_kickoff_pause and norm( vec2(self.game.ball.location - vec3(0, 0, 0))) < 100 if self.kickoff and not self.prev_kickoff: init_kickoff(self) self.prev_kickoff = True elif self.kickoff or self.step is Step.Dodge_2: kick_off(self) else: self.drive.target = self.game.ball.location self.drive.speed = 1410 self.drive.step(self.game.time_delta) self.controls = self.drive.controls if not packet.game_info.is_round_active: self.controls.steer = 0 return self.controls
def __init__(self, car): self.car = car self.target = vec3(0, 0, 0) self.speed = 2300 self.controls = SimpleControllerState() self.finished = False self.rlu_drive = RLUDrive(self.car) self.update_rlu_drive() self.power_turn = True # Handbrake while reversing to turn around quickly
class Agent(BaseAgent): def __init__(self, name, team, index): self.game = Game(index, team) self.controls = SimpleControllerState() self.action = None def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.game.read_game_information(packet, self.get_rigid_body_tick(), self.get_field_info()) if not self.action: self.action = Drive(self.game.my_car) self.action.speed = 1400 self.action.target = self.game.ball.location self.action.step(self.game.time_delta) self.controls = self.action.controls return self.controls
class CustomDrive: def __init__(self, car): self.car = car self.target = vec3(0, 0, 0) self.speed = 2300 self.controls = SimpleControllerState() self.finished = False self.rlu_drive = RLUDrive(self.car) self.update_rlu_drive() self.power_turn = True # Handbrake while reversing to turn around quickly def step(self, dt: float): self.update_rlu_drive() self.rlu_drive.step(dt) self.finished = self.rlu_drive.finished car_to_target = (self.target - self.car.position) local_target = dot(car_to_target, self.car.orientation) angle = atan2(local_target[1], local_target[0]) self.controls = self.rlu_drive.controls reverse = (cos(angle) < 0) if reverse: angle = -invert_angle(angle) if self.power_turn: self.controls.throttle = (-self.controls.throttle - 1) / 2 angle *= -1 else: self.controls.throttle = -1 self.controls.steer = cap(angle * 3, -1, 1) self.controls.boost = False self.controls.handbrake = (abs(angle) > radians(70)) def update_rlu_drive(self): self.target = self.target self.rlu_drive.target = self.target self.rlu_drive.speed = self.speed
def determine_max_speed(self, local_target): low = 100 high = 2200 if self.kickoff: return high for i in range(5): mid = (low + high) / 2 radius = (1 / RLUDrive.max_turning_curvature(mid)) local_circle = vec3(0, copysign(radius, local_target[1]), 0) dist = norm(local_circle - xy(local_target)) if dist < radius: high = mid else: low = mid return high
class GetToAirPoint: """Drive towards the point, jump and start hovering when near enough.""" def __init__(self, car: Car, info: Game): self.target: vec3 = None self.car: Car = car self.info = info self.hover = Hover(car) self.drive = Drive(car) self.controls: Input = Input() self.__time_spent_on_ground = 0.0 def step(self, dt): if self.car.on_ground and norm(self.car.position + self.car.velocity - xy(self.target)) > 2500: self.drive.speed = 1000 self.drive.target = self.target self.drive.step(dt) self.controls = self.drive.controls self.controls.handbrake = angle_between(self.car.forward(), self.target - self.car.position) > 1.2 return self.hover.target = self.target self.hover.up = normalize(self.car.position * -1) self.hover.step(dt) self.controls = self.hover.controls self.controls.throttle = not self.car.on_ground self.controls.jump = (self.car.position[2] < 30 or self.car.on_ground) and self.__time_spent_on_ground > 0.1 if self.info.round_active: self.__time_spent_on_ground += dt if not self.car.on_ground: self.__time_spent_on_ground = 0.0
def align(car: Car, target: vec3, direction: vec3): turnAngleAmount = math.acos(dot(car.forward(), direction)) rotateToZeroAngle = -atan2(direction) posOffset = rotate2(target - car.position, rotateToZeroAngle) towardsZeroVector = rotate2(car.forward(), rotateToZeroAngle) turnDirection = math.copysign(1, -towardsZeroVector[1]) turnRadius = 1 / RLUDrive.max_turning_curvature(norm(car.velocity)) ANGLETERM = 10 ANGULARTERM = -0.3 CORRECTIONTERM = 1 / 40 angleP = turnDirection * min(2, ANGLETERM * turnAngleAmount) angleD = ANGULARTERM * car.angular_velocity[2] finalOffset = posOffset[1] + turnDirection * turnRadius / .85 * ( 1 - math.cos(turnAngleAmount)) offsetCorrection = CORRECTIONTERM * finalOffset return min(1, max(-1, angleP + angleD + offsetCorrection))
def init_kickoff(agent): """"Method that initializes the kickoff""" if abs(agent.game.my_car.location[0]) < 250: pad = get_closest_small_pad(agent, vec3(0, sign(agent.team) * 4608, 18)) target = vec3(pad.location[0], pad.location[1], pad.location[2]) + sign(agent.team) * vec3(20, 0, 0) agent.kickoffStart = "Center" elif abs(agent.game.my_car.location[0]) < 1000: target = vec3(0.0, sign(agent.team) * 2816.0, 70.0) + sign(agent.team) * vec3(0, 300, 0) agent.kickoffStart = "offCenter" else: target = agent.game.my_car.location + 300 * agent.game.my_car.forward() agent.kickoffStart = "Diagonal" agent.drive = Drive(agent.game.my_car) agent.drive.target = target agent.drive.speed = 2400 agent.step = Step.Drive agent.drive.step(agent.game.time_delta) agent.controls = agent.drive.controls
def initialize_agent(self): """Initializing all parameters which require the field info""" init_boostpads(self) self.drive = Drive(self.game.my_car) self.dodge = Dodge(self.game.my_car)
class MyAgent(BaseAgent): def __init__(self, name, team, index): super().__init__(name, team, index) Game.set_mode("soccar") self.game = Game(index, team) self.name = name self.controls = SimpleControllerState() self.timer = 0.0 self.drive = Drive(self.game.my_car) self.dodge = None self.turn = None self.state = State.RESET def get_output(self, packet: GameTickPacket) -> SimpleControllerState: # Update the game values and set the state self.game.read_game_information(packet, self.get_field_info()) self.controls = SimpleControllerState() next_state = self.state # Reset everything if self.state == State.RESET: self.timer = 0.0 # self.set_gamestate_straight_moving() # self.set_gamestate_straight_moving_towards() self.set_state_stationary_angled() # self.set_gamestate_angled_stationary() # self.set_state_stationary() next_state = State.WAIT # Wait so everything can settle in, mainly for ball prediction if self.state == State.WAIT: if self.timer > 0.2: next_state = State.INITIALIZE # Initialize the drive mechanic if self.state == State.INITIALIZE: self.drive = Drive(self.game.my_car) self.drive.target = self.game.ball.position self.drive.speed = 1400 next_state = State.DRIVING # Start driving towards the target and check whether a dodge is possible, if so initialize the dodge if self.state == State.DRIVING: self.drive.target = self.game.ball.position self.drive.step(self.game.time_delta) self.controls = self.drive.controls a = time.time() target = self.game.my_car.position + 1000000 * ( self.game.ball.position - self.game.my_car.position) can_dodge, simulated_duration, simulated_target = self.simulate() print(time.time() - a) if can_dodge: self.dodge = Dodge(self.game.my_car) self.turn = AerialTurn(self.game.my_car) self.dodge.duration = simulated_duration - 0.1 self.dodge.target = simulated_target self.dodge.preorientation = look_at(simulated_target, vec3(0, 0, 1)) self.timer = 0 next_state = State.DODGING # Perform the dodge if self.state == State.DODGING: self.dodge.step(self.game.time_delta) self.controls = self.dodge.controls T = self.dodge.duration - self.dodge.timer if T > 0: if self.dodge.timer < 0.2: self.controls.boost = 1 # self.controls.pitch = 1 else: xf = self.game.my_car.position + 0.5 * T * T * vec3( 0, 0, -650) + T * self.game.my_car.velocity delta_x = self.game.ball.position - xf if angle_between(vec2(self.game.my_car.forward()), self.dodge.direction) < 0.3: if norm(delta_x) > 50: self.controls.boost = 1 self.controls.throttle = 0.0 else: self.controls.boost = 0 self.controls.throttle = clip( 0.5 * (200 / 3) * T * T, 0.0, 1.0) else: self.controls.boost = 0 self.controls.throttle = 0.0 else: self.controls.boost = 0 # Great line # if self.game.time == packet.game_ball.latest_touch.time_seconds: # print(self.game.my_car.position) if self.dodge.finished and self.game.my_car.on_ground: next_state = State.RESET self.timer += self.game.time_delta self.state = next_state return self.controls # The miraculous simulate function # TODO optimize heavily in case I actually need it # Option one: estimate the time for the current height and look at that ball prediction. # If its heigher use that unless it gets unreachable and else compare with the lower one. # If duration_estimate = 0.8 and the ball is moving up there is not sense in even simulating it. # Might even lower it since the higher the duration estimate the longer the simulation takes. def simulate(self, global_target=None): lol = 0 # Initialize the ball prediction # Estimate the probable duration of the jump and round it down to the floor decimal ball_prediction = self.get_ball_prediction_struct() if self.game.my_car.boost < 6: duration_estimate = math.floor( get_time_at_height(self.game.ball.position[2]) * 10) / 10 else: adjacent = norm( vec2(self.game.my_car.position - self.game.ball.position)) opposite = (self.game.ball.position[2] - self.game.my_car.position[2]) theta = math.atan(opposite / adjacent) t = get_time_at_height_boost(self.game.ball.position[2], theta, self.game.my_car.boost) duration_estimate = (math.ceil(t * 10) / 10) # Loop for 6 frames meaning adding 0.1 to the estimated duration. Keeps the time constraint under 0.3s for i in range(6): # Copy the car object and reset the values for the hitbox car = Car(self.game.my_car) # Create a dodge object on the copied car object # Direction is from the ball to the enemy goal # Duration is estimated duration plus the time added by the for loop # preorientation is the rotation matrix from the ball to the goal # TODO make it work on both sides # Test with preorientation. Currently it still picks a low duration at a later time meaning it # wont do any of the preorientation. dodge = Dodge(car) prediction_slice = ball_prediction.slices[round( 60 * (duration_estimate + i / 60))] physics = prediction_slice.physics ball_location = vec3(physics.location.x, physics.location.y, physics.location.z) # ball_location = vec3(0, ball_y, ball_z) dodge.duration = duration_estimate + i / 60 if dodge.duration > 1.4: break if global_target is not None: dodge.direction = vec2(global_target - ball_location) target = vec3(vec2(global_target)) + vec3( 0, 0, jeroens_magic_number * ball_location[2]) dodge.preorientation = look_at(target - ball_location, vec3(0, 0, 1)) else: dodge.target = ball_location dodge.direction = vec2(ball_location) + vec2(ball_location - car.position) dodge.preorientation = look_at(ball_location, vec3(0, 0, 1)) # Loop from now till the end of the duration fps = 30 for j in range(round(fps * dodge.duration)): lol = lol + 1 # Get the ball prediction slice at this time and convert the location to RLU vec3 prediction_slice = ball_prediction.slices[round(60 * j / fps)] physics = prediction_slice.physics ball_location = vec3(physics.location.x, physics.location.y, physics.location.z) dodge.step(1 / fps) T = dodge.duration - dodge.timer if T > 0: if dodge.timer < 0.2: dodge.controls.boost = 1 dodge.controls.pitch = 1 else: xf = car.position + 0.5 * T * T * vec3( 0, 0, -650) + T * car.velocity delta_x = ball_location - xf if angle_between(vec2(car.forward()), dodge.direction) < 0.3: if norm(delta_x) > 50: dodge.controls.boost = 1 dodge.controls.throttle = 0.0 else: dodge.controls.boost = 0 dodge.controls.throttle = clip( 0.5 * (200 / 3) * T * T, 0.0, 1.0) else: dodge.controls.boost = 0 dodge.controls.throttle = 0.0 else: dodge.controls.boost = 0 car.step(dodge.controls, 1 / fps) succesfull = self.dodge_succesfull(car, ball_location, dodge) if succesfull is not None: if succesfull: return True, j / fps, ball_location else: break return False, None, None def dodge_succesfull(self, car, ball_location, dodge): batmobile = obb() batmobile.half_width = vec3(64.4098892211914, 42.335182189941406, 14.697200775146484) batmobile.center = car.position + dot(car.orientation, vec3(9.01, 0, 12.09)) batmobile.orientation = car.orientation ball = sphere(ball_location, 93.15) b_local = dot(ball.center - batmobile.center, batmobile.orientation) closest_local = vec3( min(max(b_local[0], -batmobile.half_width[0]), batmobile.half_width[0]), min(max(b_local[1], -batmobile.half_width[1]), batmobile.half_width[1]), min(max(b_local[2], -batmobile.half_width[2]), batmobile.half_width[2])) hit_location = dot(batmobile.orientation, closest_local) + batmobile.center if norm(hit_location - ball.center) > ball.radius: return None # if abs(ball_location[2] - hit_location[2]) < 25 and hit_location[2] < ball_location[2]: if abs(ball_location[2] - hit_location[2]) < 25: if closest_local[0] > 35 and -12 < closest_local[2] < 12: hit_check = True else: print("local: ", closest_local) hit_check = True else: hit_check = False # Seems to work without angle_check. No clue why though angle_car_simulation = angle_between(car.orientation, self.game.my_car.orientation) angle_simulation_target = angle_between(car.orientation, dodge.preorientation) angle_check = angle_simulation_target < angle_car_simulation or angle_simulation_target < 0.1 return hit_check """" State setting methods for various situations""" def set_gamestate_straight_moving(self): # put the car in the middle of the field car_state = CarState( physics=Physics(location=Vector3(0, -1000, 18), velocity=Vector3(0, 0, 0), rotation=Rotator(0, math.pi / 2, 0), angular_velocity=Vector3(0, 0, 0))) # put the ball in the middle of the field ball_state = BallState( physics=Physics(location=Vector3(0, 1500, 93), velocity=Vector3(200, 650, 750), rotation=Rotator(0, 0, 0), angular_velocity=Vector3(0, 0, 0))) self.set_game_state( GameState(ball=ball_state, cars={self.game.id: car_state})) def set_gamestate_straight_moving_towards(self): # put the car in the middle of the field car_state = CarState(physics=Physics( location=Vector3(0, 0, 18), velocity=Vector3(0, 0, 0), rotation=Rotator(0, math.pi / 2, 0), angular_velocity=Vector3(0, 0, 0), ), boost_amount=50) # put the ball in the middle of the field ball_state = BallState(physics=Physics( location=Vector3(0, 2500, 93), velocity=Vector3(0, -250, 500), rotation=Rotator(0, 0, 0), angular_velocity=Vector3(0, 0, 0), )) self.set_game_state( GameState(ball=ball_state, cars={self.game.id: car_state})) def set_gamestate_angled_stationary(self): # put the car in the middle of the field car_state = CarState( physics=Physics(location=Vector3(-1000, -2000, 18), velocity=Vector3(0, 0, 0), rotation=Rotator(0, math.pi / 8, 0), angular_velocity=Vector3(0, 0, 0))) # put the ball in the middle of the field ball_state = BallState( physics=Physics(location=Vector3(0, 0, 600), velocity=Vector3(0, 0, 1), rotation=Rotator(0, 0, 0), angular_velocity=Vector3(0, 0, 0))) self.set_game_state( GameState(ball=ball_state, cars={self.game.id: car_state})) def set_state_stationary(self): # put the car in the middle of the field car_state = CarState(physics=Physics( location=Vector3(0, -2500, 18), velocity=Vector3(0, 0, 0), rotation=Rotator(0, math.pi / 2, 0), angular_velocity=Vector3(0, 0, 0), ), boost_amount=100) # put the ball in the middle of the field ball_state = BallState(physics=Physics( location=Vector3(0, ball_y, ball_z), velocity=Vector3(0, 0, 0), angular_velocity=Vector3(0, 0, 0), )) self.set_game_state( GameState(ball=ball_state, cars={self.game.id: car_state})) def set_state_stationary_angled(self): # put the car in the middle of the field car_state = CarState(physics=Physics( location=Vector3(0, -2500, 18), velocity=Vector3(0, 0, 0), rotation=Rotator(0, math.pi / 2, 0), angular_velocity=Vector3(0, 0, 0), ), boost_amount=100) # put the ball in the middle of the field ball_state = BallState(physics=Physics( location=Vector3(1500, 0, 93), velocity=Vector3(0, 0, 750), angular_velocity=Vector3(0, 0, 0), )) self.set_game_state( GameState(ball=ball_state, cars={self.game.id: car_state}))
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
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: # Update the game values and set the state self.game.read_game_information(packet, self.get_field_info()) self.controls = SimpleControllerState() next_state = self.state # Reset everything if self.state == State.RESET: self.timer = 0.0 # self.set_gamestate_straight_moving() # self.set_gamestate_straight_moving_towards() self.set_state_stationary_angled() # self.set_gamestate_angled_stationary() # self.set_state_stationary() next_state = State.WAIT # Wait so everything can settle in, mainly for ball prediction if self.state == State.WAIT: if self.timer > 0.2: next_state = State.INITIALIZE # Initialize the drive mechanic if self.state == State.INITIALIZE: self.drive = Drive(self.game.my_car) self.drive.target = self.game.ball.position self.drive.speed = 1400 next_state = State.DRIVING # Start driving towards the target and check whether a dodge is possible, if so initialize the dodge if self.state == State.DRIVING: self.drive.target = self.game.ball.position self.drive.step(self.game.time_delta) self.controls = self.drive.controls a = time.time() target = self.game.my_car.position + 1000000 * ( self.game.ball.position - self.game.my_car.position) can_dodge, simulated_duration, simulated_target = self.simulate() print(time.time() - a) if can_dodge: self.dodge = Dodge(self.game.my_car) self.turn = AerialTurn(self.game.my_car) self.dodge.duration = simulated_duration - 0.1 self.dodge.target = simulated_target self.dodge.preorientation = look_at(simulated_target, vec3(0, 0, 1)) self.timer = 0 next_state = State.DODGING # Perform the dodge if self.state == State.DODGING: self.dodge.step(self.game.time_delta) self.controls = self.dodge.controls T = self.dodge.duration - self.dodge.timer if T > 0: if self.dodge.timer < 0.2: self.controls.boost = 1 # self.controls.pitch = 1 else: xf = self.game.my_car.position + 0.5 * T * T * vec3( 0, 0, -650) + T * self.game.my_car.velocity delta_x = self.game.ball.position - xf if angle_between(vec2(self.game.my_car.forward()), self.dodge.direction) < 0.3: if norm(delta_x) > 50: self.controls.boost = 1 self.controls.throttle = 0.0 else: self.controls.boost = 0 self.controls.throttle = clip( 0.5 * (200 / 3) * T * T, 0.0, 1.0) else: self.controls.boost = 0 self.controls.throttle = 0.0 else: self.controls.boost = 0 # Great line # if self.game.time == packet.game_ball.latest_touch.time_seconds: # print(self.game.my_car.position) if self.dodge.finished and self.game.my_car.on_ground: next_state = State.RESET self.timer += self.game.time_delta self.state = next_state return self.controls
def __init__(self, car: Car, info: Game): self.car: Car = car self.info: Game = info self.controls = Input() self.drive = Drive(car)
def kick_off(agent): ball = agent.game.ball car = agent.game.my_car t = distance_2d(ball.location, car.location) / 2200 batmobile_resting = 17.00 robbies_constant = (ball.location - vec3(0, 0, 92.75 - batmobile_resting) - car.location - car.velocity * t) * 2 * t**-2 robbies_boost_constant = dot(normalize(xy( car.forward())), normalize( xy(robbies_constant))) > (0.3 if car.on_ground else 0.1) """"Module that performs the kickoffs""" if agent.kickoffStart == "Diagonal": if agent.step is Step.Drive: agent.drive.step(agent.game.time_delta) agent.controls = agent.drive.controls if agent.drive.finished: ball_location = ball.location + vec3( 0, -sign(agent.team) * 500, 0) target = car.location + 250 * normalize(ball_location - car.location) agent.drive = Drive(car) agent.drive.target = target agent.drive.speed = 2400 agent.step = Step.Drive_1 if agent.step is Step.Drive_1: agent.drive.step(agent.game.time_delta) agent.controls = agent.drive.controls if agent.drive.finished: target = vec3( dot( rotation( math.radians(-sign(agent.game.team) * sign(car.location[0]) * 60)), vec2(car.forward())) * 10000) preorientation = dot( axis_to_rotation( vec3( 0, 0, math.radians(-sign(agent.game.team) * -sign(car.location[0]) * 30))), car.rotation) setup_first_dodge(agent, 0.05, 0.3, target, preorientation) elif agent.step is Step.Dodge_1: agent.timer += agent.game.time_delta if agent.timer > 0.8: lerp_var = lerp( normalize(robbies_constant), normalize(ball.location - vec3(0, 0, 92.75 - batmobile_resting) - car.location), 0.8) agent.turn.target = look_at(lerp_var, vec3(0, 0, 1)) agent.turn.step(agent.game.time_delta) agent.controls = agent.turn.controls if car.on_ground: agent.step = Step.Catching else: agent.dodge.step(agent.game.time_delta) agent.controls = agent.dodge.controls agent.controls.boost = robbies_boost_constant elif agent.kickoffStart == "Center": if agent.step is Step.Drive: agent.drive.step(agent.game.time_delta) agent.controls = agent.drive.controls if agent.drive.finished: target = vec3( dot(rotation(math.radians(-65)), vec2(car.forward())) * 10000) preorientation = dot( axis_to_rotation(vec3(0, 0, math.radians(45))), car.rotation) setup_first_dodge(agent, 0.05, 0.4, target, preorientation) elif agent.step is Step.Dodge_1: agent.timer += agent.game.time_delta if agent.timer > 0.8: agent.turn.target = look_at(xy(ball.location - car.location), vec3(0, 0, 1)) agent.turn.step(agent.game.time_delta) agent.controls = agent.turn.controls set_steer(agent) else: agent.dodge.step(agent.game.time_delta) agent.controls = agent.dodge.controls agent.controls.boost = robbies_boost_constant elif agent.step is Step.Steer: agent.drive.step(agent.game.time_delta) agent.controls = agent.drive.controls if distance_2d(car.location, ball.location) < 800: agent.step = Step.Dodge_2 agent.dodge = Dodge(car) agent.dodge.duration = 0.075 agent.dodge.target = ball.location elif agent.step is Step.Dodge_2: agent.dodge.step(agent.game.time_delta) agent.controls = agent.dodge.controls if agent.dodge.finished and car.on_ground: agent.step = Step.Catching elif agent.kickoffStart == "offCenter": if agent.step is Step.Drive: agent.drive.step(agent.game.time_delta) agent.controls = agent.drive.controls if distance_2d(car.location, agent.drive.target) < 650: target = vec3( dot( rotation( math.radians(-sign(agent.game.team) * -sign(car.location[0]) * 100)), vec2(car.forward())) * 10000) preorientation = dot( axis_to_rotation( vec3( 0, 0, math.radians(-sign(agent.game.team) * sign(car.location[0]) * 30))), car.rotation) setup_first_dodge(agent, 0.05, 0.4, target, preorientation) elif agent.step is Step.Dodge_1: agent.timer += agent.game.time_delta if agent.timer > 0.8: lerp_var = lerp( normalize(robbies_constant), normalize(ball.location - vec3(0, 0, 92.75 - batmobile_resting) - car.location), 0.25) agent.turn.target = look_at(lerp_var, vec3(0, 0, 1)) agent.turn.step(agent.game.time_delta) agent.controls = agent.turn.controls set_steer(agent) else: agent.dodge.step(agent.game.time_delta) agent.controls = agent.dodge.controls agent.controls.boost = robbies_boost_constant elif agent.step is Step.Steer: agent.drive.step(agent.game.time_delta) agent.controls = agent.drive.controls if distance_2d(ball.location, car.location) < 800: agent.step = Step.Dodge_2 agent.dodge = Dodge(car) agent.dodge.duration = 0.075 agent.dodge.target = ball.location elif agent.step is Step.Dodge_2: agent.dodge.step(agent.game.time_delta) agent.controls = agent.dodge.controls if agent.dodge.finished and car.on_ground: agent.step = Step.Catching
class MyAgent(BaseAgent): def __init__(self, name, team, index): super().__init__(name, team, index) self.game = Game(index, team) self.name = name self.controls = SimpleControllerState() self.timer = 0.0 self.drive = Drive(self.game.my_car) self.navigator = None self.dodge = None self.turn = None self.state = State.RESET def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.game.read_game_information(packet, self.get_rigid_body_tick(), self.get_field_info()) self.controls = SimpleControllerState() next_state = self.state if self.state == State.RESET: self.timer = 0.0 self.set_gamestate_straight_moving() # self.set_gamestate_angled_stationary() # self.set_gamestate_straight_moving_towards() next_state = State.WAIT if self.state == State.WAIT: if self.timer > 0.2: next_state = State.INITIALIZE if self.state == State.INITIALIZE: self.drive = Drive(self.game.my_car) self.drive.target, self.drive.speed = self.game.ball.location, 2300 next_state = State.DRIVING if self.state == State.DRIVING: self.drive.target = self.game.ball.location self.drive.step(self.game.time_delta) self.controls = self.drive.controls can_dodge, simulated_duration, simulated_target = self.simulate() if can_dodge: self.dodge = Dodge(self.game.my_car) self.turn = AerialTurn(self.game.my_car) print("============") print(simulated_duration) self.dodge.duration = simulated_duration - 0.1 self.dodge.target = simulated_target self.timer = 0 next_state = State.DODGING if self.state == State.DODGING: self.dodge.step(self.game.time_delta) self.controls = self.dodge.controls if self.game.time == packet.game_ball.latest_touch.time_seconds: print(self.timer) if self.dodge.finished and self.game.my_car.on_ground: next_state = State.RESET self.timer += self.game.time_delta self.state = next_state return self.controls def simulate(self): ball_prediction = self.get_ball_prediction_struct() duration_estimate = math.floor( get_time_at_height(self.game.ball.location[2], 0.2) * 10) / 10 for i in range(6): car = Car(self.game.my_car) ball = Ball(self.game.ball) batmobile = obb() batmobile.half_width = vec3(64.4098892211914, 42.335182189941406, 14.697200775146484) batmobile.center = car.location + dot(car.rotation, vec3(9.01, 0, 12.09)) batmobile.orientation = car.rotation dodge = Dodge(car) dodge.duration = duration_estimate + i / 60 dodge.target = ball.location for j in range(round(60 * dodge.duration)): dodge.target = ball.location dodge.step(1 / 60) car.step(dodge.controls, 1 / 60) prediction_slice = ball_prediction.slices[j] physics = prediction_slice.physics ball_location = vec3(physics.location.x, physics.location.y, physics.location.z) dodge.target = ball_location batmobile.center = car.location + dot(car.rotation, vec3(9.01, 0, 12.09)) batmobile.orientation = car.rotation if intersect(sphere(ball_location, 93.15), batmobile) and abs( ball_location[2] - car.location[2] ) < 25 and car.location[2] < ball_location[2]: return True, j / 60, ball_location return False, None, None def set_gamestate_straight_moving(self): # put the car in the middle of the field car_state = CarState( physics=Physics(location=Vector3(0, -1000, 18), velocity=Vector3(0, 0, 0), rotation=Rotator(0, math.pi / 2, 0), angular_velocity=Vector3(0, 0, 0))) # put the ball in the middle of the field ball_state = BallState( physics=Physics(location=Vector3(0, 1500, 93), velocity=Vector3(0, random.randint(-250, 800), random.randint(700, 800)), rotation=Rotator(0, 0, 0), angular_velocity=Vector3(0, 0, 0))) self.set_game_state( GameState(ball=ball_state, cars={self.game.id: car_state})) def set_gamestate_straight_moving_towards(self): # put the car in the middle of the field car_state = CarState(physics=Physics( location=Vector3(0, 0, 18), velocity=Vector3(0, 0, 0), angular_velocity=Vector3(0, 0, 0), )) # put the ball in the middle of the field ball_state = BallState(physics=Physics( location=Vector3(0, 2500, 93), velocity=Vector3(0, -250, 700), rotation=Rotator(0, 0, 0), angular_velocity=Vector3(0, 0, 0), )) self.set_game_state( GameState(ball=ball_state, cars={self.game.id: car_state})) def set_gamestate_angled_stationary(self): # put the car in the middle of the field car_state = CarState( physics=Physics(location=Vector3(-1000, -1500, 18), velocity=Vector3(0, 0, 0), rotation=Rotator(0, math.pi / 8, 0), angular_velocity=Vector3(0, 0, 0))) # put the ball in the middle of the field ball_state = BallState( physics=Physics(location=Vector3(0, 0, 750), velocity=Vector3(0, 0, 1), rotation=Rotator(0, 0, 0), angular_velocity=Vector3(0, 0, 0))) self.set_game_state( GameState(ball=ball_state, cars={self.game.id: car_state}))
class CustomDrive: def __init__(self, car): self.car = car self.target = vec3(0, 0, 0) self.speed = 2300 self.controls = SimpleControllerState() self.finished = False self.rlu_drive = RLUDrive(self.car) self.update_rlu_drive() self.power_turn = True # Handbrake while reversing to turn around quickly self.aerial_turn = AerialTurn(car) self.kickoff = False def step(self, dt: float): self.speed = abs(self.speed) car_to_target = (self.target - self.car.location) local_target = dot(car_to_target, self.car.rotation) angle = atan2(local_target[1], local_target[0]) vel = norm(self.car.velocity) in_air = (not self.car.on_ground) on_wall = (self.car.location[2] > 250 and not in_air) reverse = (cos(angle) < 0 and not (on_wall or in_air or self.kickoff)) get_off_wall = (on_wall and local_target[2] > 450) if get_off_wall: car_to_target[2] = -self.car.location[2] local_target = dot(car_to_target, self.car.rotation) angle = atan2(local_target[1], local_target[0]) max_speed = self.determine_max_speed(local_target) self.update_rlu_drive(reverse, max_speed) self.rlu_drive.step(dt) self.finished = self.rlu_drive.finished self.controls = self.rlu_drive.controls self.controls.handbrake = False if reverse: angle = -invert_angle(angle) if self.power_turn and not on_wall: angle *= -1 self.controls.handbrake = (vel > 200) self.controls.steer = cap(angle * 3, -1, 1) self.controls.boost = False if not self.controls.handbrake: self.controls.handbrake = (abs(angle) > radians(70) and vel > 500 and not on_wall) if self.controls.handbrake: self.controls.handbrake = (dot(self.car.velocity, car_to_target) > -150) if in_air: self.aerial_turn.target = look_at(xy(car_to_target), vec3(0, 0, 1)) self.aerial_turn.step(dt) aerial_turn_controls = self.aerial_turn.controls self.controls.pitch = aerial_turn_controls.pitch self.controls.yaw = aerial_turn_controls.yaw self.controls.roll = aerial_turn_controls.roll self.controls.boost = False def update_rlu_drive(self, reverse: bool = False, max_speed: float = 2200): self.target = self.target self.rlu_drive.target = self.target self.rlu_drive.speed = cap(self.speed * (-1 if reverse else 1), -max_speed, max_speed) def determine_max_speed(self, local_target): low = 100 high = 2200 if self.kickoff: return high for i in range(5): mid = (low + high) / 2 radius = (1 / RLUDrive.max_turning_curvature(mid)) local_circle = vec3(0, copysign(radius, local_target[1]), 0) dist = norm(local_circle - xy(local_target)) if dist < radius: high = mid else: low = mid return high