def tick(self, packet: GameTickPacket) -> bool: myCar = packet.game_cars[self.agent.index] if self.agent.spikeWatcher.carrying_car != myCar: return False targetSide = 1 - 2*myCar.team self.controller.boost = False self.controller.use_item = False self.controller.jump = False self.controller.pitch = 0 self.controller.roll = 0 self.controller.yaw = 0 targetSpeed = 2400 carOrientation = Orientation(myCar.physics.rotation) relativeBallPosition = carOrientation.relative_location(Vec3(myCar.physics.location), Vec3(packet.game_ball.physics.location)) onCurvedWallSection = abs(myCar.physics.location.x + .3 * myCar.physics.velocity.x) + abs(myCar.physics.location.y + .3 * myCar.physics.velocity.y) > 7850 and abs(abs(myCar.physics.location.x + .3 * myCar.physics.velocity.x) - 4096 - (abs(myCar.physics.location.y + .3 * myCar.physics.velocity.y) - 5120)) > 925 if self.dunkedTicks > 0: self.dunkedTicks += 1 if self.dunkedTicks > 120 * .65: print("Dunk failed") self.recoveryActive = True if super().tick(packet): return True self.agent.stateMachine.restartStateAndContinueTick(packet) ballTowardsGoalVelocity = packet.game_ball.physics.velocity.y * targetSide if ballTowardsGoalVelocity < self.lastBalltowardsGoalVelocity and self.lastBalltowardsGoalVelocity > 10: self.releaseFrames += 1 if self.releaseFrames > 5: self.controller.use_item = True self.lastBalltowardsGoalVelocity = ballTowardsGoalVelocity return True else: self.releaseFrames = 0 if relativeBallPosition.z < 93.15 - 17 - 20: self.stuckFrames += 1 if self.stuckFrames > 5: print("ball is attached to the bottom") self.controller.use_item = True return True else: self.stuckFrames = 0 if self.readyToDunkTicks > 0: self.readyToDunkTicks += 1 if self.readyToDunkTicks > 120 * .65: print("predunk failed") self.recoveryActive = True if super().tick(packet): return True self.agent.stateMachine.restartStateAndContinueTick(packet) spinX = relativeBallPosition.x if Vec3(0, relativeBallPosition.y, relativeBallPosition.z).length() > 125: spinX = max(0, spinX) if not myCar.has_wheel_contact and self.wallChainDashFrames == 0 and abs(packet.game_ball.physics.location.x) < 920 - max(0, min(120, 2 * spinX)): print("dunking") self.controller.yaw = 0 spinDirection = Vec3(-spinX, relativeBallPosition.y + math.copysign(20, relativeBallPosition.y + math.copysign(20, carOrientation.forward.x * carOrientation.up.y)), 0).normalized() self.controller.pitch = spinDirection.x self.controller.roll = spinDirection.y self.controller.jump = True self.dunkedTicks = 1 return True # todo: check if opponents are near and perform shot if so wallDriveHeight = WALLDRIVEHEIGHT + min(0, myCar.physics.location.z - packet.game_ball.physics.location.z) targetPosition = None targetDirection = None onWall = myCar.physics.location.z > 75 and (myCar.has_wheel_contact or self.wallChainDashFrames > 0) if onWall: allowChainWallDash = True if self.wallChainDashFrames > 0: self.wallChainDashFrames -= 1 if self.wallChainDashFrames > WALLCHAINDASHSECOND: self.controller.pitch = 1 elif self.wallChainDashFrames == WALLCHAINDASHSECOND: # print("second chain") self.controller.jump = True self.controller.pitch = -.75 self.controller.roll = math.copysign(1, carOrientation.right.z) if math.fabs(myCar.physics.location.y) - math.fabs(myCar.physics.location.x) > 5120 - 4096: if myCar.physics.location.y * targetSide > 0: # opponent back wall maxHorizontal = 850 + min(0, (myCar.physics.location.y - packet.game_ball.physics.location.y) * math.copysign(1, myCar.physics.location.y)) targetX = min(maxHorizontal, max(-maxHorizontal, packet.game_ball.physics.location.x)) if math.fabs(myCar.physics.location.x) > 2750: targetPosition = Vec3(targetX, myCar.physics.location.y, wallDriveHeight) else: originalTargetPosition = Vec3(targetX, myCar.physics.location.y, 640 - 90 + min(0, myCar.physics.location.z - packet.game_ball.physics.location.z)) targetPosition = Vec3(originalTargetPosition) foundGap = False try: while True: arrivalTime = (Vec3(myCar.physics.location) - targetPosition).length() / Vec3(myCar.physics.velocity).length() foundGap = True for carIndex in range(packet.num_cars): car = packet.game_cars[carIndex] if car.team != myCar.team: carFuturePos = Vec3(car.physics.location) + arrivalTime * Vec3(car.physics.velocity) # print(round((carFuturePos - targetPosition).length())) if (carFuturePos - targetPosition).length() < 500: foundGap = False break if foundGap: break else: # print(targetPosition.x, "is occupied") targetPosition.x += math.copysign(100, carOrientation.forward.x) # scan step #exclude last node on other side if targetPosition.x + math.copysign(100, carOrientation.forward.x) != min(maxHorizontal, max(-maxHorizontal, targetPosition.x + math.copysign(100, carOrientation.forward.x))): break except ZeroDivisionError: foundGap = False if not foundGap: # print("scan failed") targetPosition = originalTargetPosition # print("going for ", targetPosition.x) enemyGoalDistance = (Vec3(myCar.physics.location) - Vec3(targetX, 5120 * targetSide, min(643, packet.game_ball.physics.location.z))).length() if enemyGoalDistance < 250: if self.readyToDunkTicks == 0: self.readyToDunkTicks = 1 else: self.readyToDunkTicks = 0 allowChainWallDash = enemyGoalDistance > 1000 targetSpeed = min(2400, 1100 - max(0, min(300, relativeBallPosition.x)) + max(0, enemyGoalDistance - 300) / 250 * 1100) else: # self back wall targetDirection = Vec3(math.copysign(1, myCar.physics.location.x), 0, 0) else: targetDirection = Vec3(0, targetSide, 0) invertDirection = 1 if targetDirection is not None: invertDirection = math.copysign(1, carOrientation.relative_direction(targetDirection).x) targetHeight = wallDriveHeight - 500 if invertDirection < 0 else wallDriveHeight targetDirection += Vec3(0, 0, min(.5, max(-.5, (targetHeight - myCar.physics.location.z) / 200 - myCar.physics.velocity.z / 1000))) else: #print("off wall") self.wallChainDashFrames = 0 if myCar.has_wheel_contact: self.readyToDunkTicks = 0 targetDirection = Vec3(math.copysign(1, myCar.physics.location.x), .55 * targetSide, 0) if targetPosition is not None: targetDirection = (targetPosition - Vec3(myCar.physics.location)).normalized() * 2.5 relativeTargetDirection = carOrientation.relative_direction(targetDirection) if relativeTargetDirection.x < 0: relativeTargetDirection.y = math.copysign(1, relativeTargetDirection.y) self.controller.steer = min(1, max(-1, relativeTargetDirection.y)) if onWall and\ allowChainWallDash and\ myCar.has_wheel_contact and\ invertDirection == 1 and\ self.wallChainDashFrames == 0 and\ abs(self.controller.steer) < .2 and\ carOrientation.forward.z < .6 and\ not onCurvedWallSection and\ Vec3(myCar.physics.velocity).length() < 2200: # print("chain") self.controller.jump = True self.wallChainDashFrames = WALLCHAINDASHSECOND + WALLCHAINDASHFIRST self.controller.throttle = min(1, max(-1, (targetSpeed - Vec3(myCar.physics.velocity).length()) / 100)) return True
def test_on_target_2d(self): pos = Vec3(100, 100, 0) vel = Vec3(10, 10, 73) left = Vec3(230, 200, 0) right = Vec3(190, 200, 0) self.assertTrue(util.is_on_target_2d(pos, vel, left, right))
def __init__(self, car, info: MyInfo): self.car = car self.info = info #self.calc_best_ttb(car, ball_path) self.should_go = True self.challenge_loc = Vec3(info.ball_path[0].physics.location)
def fromRLU(v: 'vec3') -> 'Vec3': return Vec3(v[0], v[1], v[2])
def __init__(self, ball_center_offset: Vec3 = Vec3(0, 150, 0)): super().__init__() self.ball_center_offset = ball_center_offset self.state = "kickoff" print("kickoff locked and loaded")
def preprocessing(self, packet): # Let's get some data, boys! # First, our car! self.OurData.position = Vec3( packet.game_cars[self.index].physics.location) self.OurData.rotation = packet.game_cars[self.index].physics.rotation self.OurData.velocity = Vec3( packet.game_cars[self.index].physics.velocity) self.OurData.angular_velocity = Vec3( packet.game_cars[self.index].physics.angular_velocity) self.OurData.boost_value = packet.game_cars[self.index].boost self.OurData.has_wheel_contact = packet.game_cars[ self.index].has_wheel_contact self.OurData.is_supersonic = packet.game_cars[ self.index].is_super_sonic #What's the other guy's index? opponent_index = -1 for x in range(packet.num_cars): if x != self.index: opponent_index = x #Now let's find out some stuff about him. self.OpponentData.position = Vec3( packet.game_cars[opponent_index].physics.location) self.OpponentData.rotation = packet.game_cars[ opponent_index].physics.rotation self.OpponentData.velocity = Vec3( packet.game_cars[opponent_index].physics.velocity) self.OpponentData.angular_velocity = Vec3( packet.game_cars[opponent_index].physics.angular_velocity) self.OpponentData.boost_value = packet.game_cars[opponent_index].boost self.OpponentData.has_wheel_contact = packet.game_cars[ opponent_index].has_wheel_contact # Now the ball. self.BallDataAgent.position = Vec3(packet.game_ball.physics.location) self.BallDataAgent.rotation = packet.game_ball.physics.rotation self.BallDataAgent.velocity = Vec3(packet.game_ball.physics.velocity) self.BallDataAgent.angular_velocity = Vec3( packet.game_ball.physics.angular_velocity) #Who has the ball right now? self.OurData.has_the_ball = (self.BallDataAgent.position - self.OurData.position).length() < 200 self.OpponentData.has_the_ball = ( self.BallDataAgent.position - self.OpponentData.position).length() < 200 # Where's their goal? self.TheirGoal.centre = Vec3(0, -sign(self.team) * (5120 + 200), 0) # Finally, get me a list of every single active boost pad on the field right now. self.FullBoostPads.clear() # print(len(self.fullBoostPads)) field_info = self.get_field_info() for i in range(field_info.num_boosts): pad = field_info.boost_pads[i] packetPad = packet.game_boosts[i] if packetPad.timer <= 0: self.FullBoostPads.append(Vec3(pad.location)) # experimental ball path prediction stuff self.BallPredictionData = self.get_ball_prediction_struct() self.BouncePoints.clear() for i in range(0, self.BallPredictionData.num_slices - 1): currentSlice = self.BallPredictionData.slices[i] nextSlice = self.BallPredictionData.slices[i + 1] if (currentSlice.physics.velocity.z <= 0 and 0 < nextSlice.physics.velocity.z): self.BouncePoints.append(currentSlice)
def start(self): if TWITCH_CHAT_INTERACTION: port = find_usable_port(9097) Thread(target=run_action_server, args=(port, ), daemon=True).start() set_bot_action_broker( self.action_broker ) # This seems to only work after the bot hot reloads once, weird. Thread(target=self.heartbeat_connection_attempts_to_twitch_broker, args=(port, ), daemon=True).start() while True: sleep(0) packet = self.wait_game_tick_packet() raw_players = [ self.game_tick_packet.game_cars[i] for i in range(packet.num_cars) ] self.known_players = [p for p in raw_players if p.name] if self.last_seconds_elapsed == packet.game_info.seconds_elapsed: continue elapsed_now = packet.game_info.seconds_elapsed - self.last_seconds_elapsed self.last_seconds_elapsed = packet.game_info.seconds_elapsed ball_pos = Vec3(packet.game_ball.physics.location) ball_vel = Vec3(packet.game_ball.physics.velocity) ball_ang = Vec3(packet.game_ball.physics.angular_velocity) self.ticksThisSecond += 1 if int(packet.game_info.seconds_elapsed) != self.lastFullSecond: print("ticks this second:", self.ticksThisSecond) self.ticksThisSecond = 0 self.lastFullSecond = int(packet.game_info.seconds_elapsed) if TWITCH_CHAT_INTERACTION: self.car_lasers = { k: v for k, v in self.car_lasers.items() if v.time_remaining >= 0 } else: self.car_lasers = {} for i in range(packet.num_cars): self.car_lasers[i] = Laser(0, math.inf) if packet.teams[0].score - packet.teams[1].score != self.lastScore: self.isPaused = True self.lastScore = packet.teams[0].score - packet.teams[1].score self.isKickoff = 0 elif packet.game_ball.physics.location.x == 0 and packet.game_ball.physics.location.y == 0 and packet.game_ball.physics.velocity.x == 0 and packet.game_ball.physics.velocity.y == 0: self.isKickoff += elapsed_now if self.isKickoff >= 4: self.isPaused = False ballTouchers = [] random.seed(a=int(packet.game_info.seconds_elapsed / .14)) if DURING_BOOST_ONLY: boosting = {} boostContent = {} for i in range(packet.num_cars): car = packet.game_cars[i] boosting[i] = i in self.boostContent and ( 6 if self.boostContent[i] > car.boost or (self.boostContent[i] < car.boost and self.boosting[i]) else max(0, self.boosting[i] - 1)) boostContent[i] = car.boost self.boosting = boosting self.boostContent = boostContent for index in range(packet.num_cars): car = packet.game_cars[index] car_pos = Vec3(car.physics.location) car_ori = Orientation(car.physics.rotation) self.renderer.begin_rendering(str(index) + "Lb") if index in self.car_lasers: laser = self.car_lasers[index] if not packet.game_cars[index].is_demolished and ( not DURING_BOOST_ONLY or self.boosting[index]): # and not self.isPaused: if not self.isPaused: laser.time_remaining -= elapsed_now if laser.time_remaining >= 0: for leftRight in (-1, 1): startPoint = car_pos + car_ori.forward * 63 - leftRight * car_ori.right * 26 + car_ori.up * 3 direction = car_ori.forward.orthogonalize( Vec3(0, 0, 1)).normalized( ) if car.has_wheel_contact and abs( car_ori.up.dot(Vec3(0, 0, 1)) ) > 0.999 else car_ori.forward for bounce in range(BOUNCE_SEGMENTS): closest = math.inf closestTarget = None toBall = Vec3(packet.game_ball.physics. location) - car_pos toBallProj = toBall.project(direction) toBallOrth = toBall - toBallProj toCollisionOrth = toBallOrth endVector = direction if toBallOrth.length( ) <= BALL_RADIUS and toBallProj.dot( direction) > 0: closestTarget = -1 closest = toBallProj.length( ) - math.sqrt(BALL_RADIUS**2 - toBallOrth.length()**2) ballTouchers.append(index) for otherIndex in range(packet.num_cars): if otherIndex == index: continue other_car = packet.game_cars[ otherIndex] other_car_pos = Vec3( other_car.physics.location) other_car_ori = Orientation( other_car.physics.rotation) v_local = other_car_ori.dot2( startPoint - (other_car_pos + other_car_ori.dot1(HITBOX_OFFSET) ) + 15 * other_car_ori.up) d_local = other_car_ori.dot2(direction) def lineFaceCollision(i): offset = Vec3(0, 0, 0) offset[i] = math.copysign( HITBOX_HALF_WIDTHS[i], -d_local[i]) collisionPoint = v_local - offset try: distance = -collisionPoint[ i] / d_local[i] except ZeroDivisionError: return None if distance < 0: return None collisionPoint += d_local * distance for j in range( i == 0, 3 - (i == 2), 1 + (i == 1)): if abs( collisionPoint[j] ) > HITBOX_HALF_WIDTHS[j]: return None collisionPoint[i] = offset[i] return distance distance = lineFaceCollision( 0) or lineFaceCollision( 1) or lineFaceCollision(2) if distance is not None: collisionPoint = startPoint + distance * direction toCollisionOrth = ( collisionPoint - startPoint ).orthogonalize(direction) if distance < closest: closest = distance closestTarget = otherIndex if closestTarget is not None: if closestTarget not in self.forces: self.forces[closestTarget] = Push() self.forces[ closestTarget].velocity += direction * elapsed_now try: self.forces[ closestTarget].angular_velocity += toCollisionOrth * -1 * direction / toCollisionOrth.length( )**2 * elapsed_now except ZeroDivisionError: pass pass else: # simulate raycast closest length = 100000 startPointRLU = toRLU(startPoint) directionRLU = toRLU(direction) ray = Ray(startPointRLU, directionRLU * length) while closest >= length + .2: closest = length newStartPointRLU, mirrorDirectionRLU = ray.start, ray.direction ray = Field.raycast_any( Ray( startPointRLU, directionRLU * (length - .1))) length = norm(ray.start - startPointRLU) mirrorDirection = fromRLU( mirrorDirectionRLU) newStartPoint = fromRLU( newStartPointRLU) newDirection = direction - 2 * direction.dot( mirrorDirection) * mirrorDirection endVector = direction * 0.6 - mirrorDirection * 0.4 R = 4 COLORSPIN = 2 SCATTERSPIN = 0.75 dir_ori = look_at_orientation( direction, Vec3(0, 0, 1)) dir_ori.right *= R dir_ori.up *= R end_ori = look_at_orientation( endVector, Vec3(0, 0, 1)) scatterStartFirst = startPoint + closest * direction for i in range(LASERLINES): i = i / LASERLINES * 2 * math.pi offset = dir_ori.right * math.sin( i) + dir_ori.up * math.cos(i) color = self.renderer.create_color( 255, int(255 * (0.5 + 0.5 * math.sin( car.physics.rotation.roll + leftRight * i + (COLORSPIN * packet.game_info. seconds_elapsed)))), int(255 * (0.5 + 0.5 * math.sin( car.physics.rotation.roll + leftRight * i + (COLORSPIN * packet.game_info. seconds_elapsed + 2 / 3 * math.pi)))), int(255 * (0.5 + 0.5 * math.sin( car.physics.rotation.roll + leftRight * i + (COLORSPIN * packet.game_info. seconds_elapsed + 4 / 3 * math.pi))))) self.renderer.native_draw_line_3d( self.renderer.builder, color, toDrawVector3(startPoint + offset), toDrawVector3(scatterStartFirst + offset)) for _ in range(SCATTERLINES): r = random.uniform(0, 2 * math.pi) c = leftRight * r - ( SCATTERSPIN - COLORSPIN ) * packet.game_info.seconds_elapsed i = car.physics.rotation.roll + r - leftRight * ( SCATTERSPIN ) * packet.game_info.seconds_elapsed # c = random.uniform(0, 2 * math.pi) color = self.renderer.create_color( 255, int(255 * (0.5 + 0.5 * math.sin(c))), int(255 * (0.5 + 0.5 * math.sin(c + 2 / 3 * math.pi)) ), int(255 * (0.5 + 0.5 * math.sin(c + 4 / 3 * math.pi)) )) length = 15 * random.expovariate(1) scatterStart = scatterStartFirst + dir_ori.right * math.sin( i) + dir_ori.up * math.cos(i) scatterEnd = scatterStart + end_ori.dot1( Vec3(-length, length * math.sin(i), length * math.cos(i))) self.renderer.native_draw_line_3d( self.renderer.builder, color, toDrawVector3(scatterStart), toDrawVector3(scatterEnd)) if closestTarget is not None: break else: startPoint, direction = newStartPoint + 0.1 * newDirection, newDirection self.renderer.end_rendering() ballState = None if -1 in self.forces: if not self.isPaused: ballState = BallState( # latest_touch=Touch(player_name=packet.game_cars[random.choice(ballTouchers)].name), physics=Physics(velocity=toVector3( ball_vel + self.forces[-1].velocity * PUSH_STRENGTH_BALL), angular_velocity=toVector3( ball_ang + self.forces[-1].angular_velocity * PUSH_STRENGTH_BALL_ANGULAR))) del self.forces[-1] carStates = {} for i, force in self.forces.items(): carStates[i] = CarState(physics=Physics( velocity=toVector3( Vec3(packet.game_cars[i].physics.velocity) + self.forces[i].velocity * PUSH_STRENGTH_CAR), angular_velocity=toVector3( Vec3(packet.game_cars[i].physics.angular_velocity) + self.forces[i].angular_velocity * PUSH_STRENGTH_CAR_ANGULAR))) self.forces.clear() self.set_game_state(GameState(cars=carStates, ball=ballState))
def steer_toward_target(car: PlayerInfo, target: Vec3) -> float: relative = relative_location(Vec3(car.physics.location), Orientation(car.physics.rotation), target) angle = math.atan2(relative.y, relative.x) return limit_to_safe_range(angle * 5)
def line(self, start, end, color=None, alpha=255): color = color if color is not None else [255, 255, 255] self.renderer.draw_line_3d(Vec3(start), Vec3(end), self.renderer.create_color(alpha, *color))
def defend(): if not in_goal: target_ball = predict_ball(earliest_intersection) target_location = Vec3(target_ball.physics.location) nearest_surface = (target_location - car_location) / ( target_location - car_location).length() * 140 nearest_surface_r = Vec3(-nearest_surface.y, nearest_surface.x, nearest_surface.z) nearest_surface_l = Vec3(nearest_surface.y, -nearest_surface.x, nearest_surface.z) if False: side = -sign((nearest_surface_l - send_location).length() - (nearest_surface_r - send_location).length()) else: side = -sign(((nearest_surface_l - ball_location) - ball_velocity).length() - ((nearest_surface_r - ball_location) - ball_velocity).length()) nearest_surface = Vec3(-nearest_surface.y * side, nearest_surface.x * side, nearest_surface.z) if abs(target_location.x + nearest_surface.x) >= self.map_size[1] or get_angle( car_location - send_location, car_location - target_location ) > math.pi / 3 * 2 or ball_location.z > 130: target_location = -send_location else: target_location = target_location + nearest_surface / 140 * car_velocity.length( ) controls.boost = enemy_time < 1 and abs( steer_toward_target(my_car, target_location)) <= 0.1 # Jump h = (Vec3(target_ball.physics.location) - distance_from_surface(Vec3( target_ball.physics.location))).length() if jump_ready( h) >= earliest_intersection and steer_toward_target( my_car, target_location) < 1: controls.pitch = 0 controls.yaw = 0 controls.roll = 0 controls.jump = True controls.boost = False self.jumping = 0 else: jump_attack() # Draw self.renderer.draw_line_3d(car_location, ball_location + nearest_surface, self.renderer.cyan()) self.renderer.draw_rect_3d(ball_location + nearest_surface, 8, 8, True, self.renderer.cyan(), centered=True) controls.throttle = 1 else: target_location = -send_location controls.throttle = 1 return target_location
def __init__(self, boost_pad: BoostPad, index: int): self.index = index self.location = Vec3(boost_pad.location) self.is_full_boost = boost_pad.is_full_boost self.is_active = False self.timer: float = 0.0
def attack(): # Target target_ball = predict_ball(earliest_intersection) target_location = Vec3(target_ball.physics.location) target_location = target_location + ( target_location - send_location) / ( target_location - send_location).length() * 92.75 # Smoother wall transitions target_location = move_target_for_walls(car_location, target_location) # Manage speed if velocity_from_surface( Vec3(target_ball.physics.location), Vec3(target_ball.physics.velocity)) >= 0 or (Vec3( target_ball.physics.location) - distance_from_surface( Vec3(target_ball.physics.location))).length( ) <= 300 or target_ball.physics.location.y * sign( send_location.y) <= -self.map_size[0]: controls.throttle = 1 else: controls.throttle = 0 # Boost controls.boost = abs( steer_toward_target(my_car, target_location) ) <= 0.1 and car_velocity.length() < 2300 and ( (Vec3(target_ball.physics.location) - distance_from_surface( Vec3(target_ball.physics.location))).length() <= 300 or velocity_from_surface(Vec3(target_ball.physics.location), Vec3(target_ball.physics.velocity)) >= 0) and nearest_car == 0 # Jump h = (Vec3(target_ball.physics.location) - distance_from_surface( Vec3(target_ball.physics.location))).length() if jump_ready( (Vec3(target_ball.physics.location) - distance_from_surface(Vec3(target_ball.physics.location)) ).length()) >= earliest_intersection and steer_toward_target( my_car, target_location) < 1: controls.pitch = 0 controls.yaw = 0 controls.roll = 0 controls.jump = True controls.boost = False self.jumping = 0 else: jump_attack() # Catch the ball when in the air if abs( target_location.z - car_location.z ) <= 92.75 * 0.75 and get_angle( target_location - car_location, car_velocity ) >= math.atan( 200 / car_velocity.length() ) and my_car.jumped == True and my_car.double_jumped == False and False: controls.yaw = sign( (target_location - car_location - Vec3(car_velocity.y, -car_velocity.x, car_velocity.z) ).length() - (target_location - car_location - Vec3(-car_velocity.y, car_velocity.x, car_velocity.z) ).length()) controls.jump = True # Draw self.renderer.draw_line_3d(car_location, target_location, self.renderer.red()) self.renderer.draw_rect_3d(target_location, 8, 8, True, self.renderer.red(), centered=True) return target_location
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.boost_pad_tracker.update_boost_status(packet) if self.active_sequence is not None and not self.active_sequence.done: controls = self.active_sequence.tick(packet) if controls is not None: return controls # Get the distance from the nearest surface (walls, floor, roof, etc.) def distance_from_surface(pos): min_dist = math.inf nearest_surface = pos if min_dist > pos.z: min_dist = pos.z nearest_surface = Vec3(pos.x, pos.y, 0) if min_dist > self.map_size[1] - abs(pos.x): min_dist = self.map_size[1] - abs(pos.x) nearest_surface = Vec3( sign(pos.x) * self.map_size[1], pos.y, pos.z) if min_dist > self.map_size[0] - abs(pos.y): min_dist = self.map_size[0] - abs(pos.y) nearest_surface = Vec3(pos.x, sign(pos.y) * self.map_size[0], pos.z) if min_dist > 99999 + (8064 - abs(pos.x) - abs(pos.y)) / math.sqrt(2): min_dist = (8064 - abs(pos.x) - abs(pos.y)) / math.sqrt(2) nearest_surface = Vec3(pos.x, sign(pos.y) * self.map_size[0], pos.z) return nearest_surface # Get the rate in which the object is moving away from the nearest surface def velocity_from_surface(pos, vel): val = pos - distance_from_surface(pos) if val.x != 0: return vel.x * sign(val.x) elif val.y != 0: return vel.y * sign(val.y) elif val.z != 0: return vel.z * sign(val.z) # Get the order within the team def distance_order(teams): nearest = 1 overall_nearest = 1 last = 0 ref = (car_location - ball_location).length( ) / (car_velocity.length() + 2300) + math.asin( (car_velocity * safe_div(car_velocity.length()) - ball_location * safe_div(ball_location.length())).length() / 2) * 3 / math.pi * 0 for i in range(len(packet.game_cars)): if (packet.game_cars[i].team != (-teams * packet.game_cars[self.index].team + (1 + teams) / 2) or teams == False ) and packet.game_cars[i].physics.location.z > 0: time_taken = ( Vec3(packet.game_cars[i].physics.location) - ball_location).length() / (Vec3(packet.game_cars[ i].physics.velocity).length() + 2300) + math.asin( (Vec3(packet.game_cars[i].physics.velocity) * safe_div( Vec3(packet.game_cars[i].physics.velocity ).length()) - ball_location * safe_div(ball_location.length())).length() / 2) * 3 / math.pi * 0 # When the car is further front than the POV if ref > time_taken: if (Vec3(packet.game_cars[i].physics.location) + send_location).length() <= ( ball_location + send_location ).length() or (car_location + send_location).length() > ( ball_location + send_location).length(): nearest += 1 overall_nearest += 1 last += 1 # Keep the full-time goal keeper if overall_nearest == last: nearest = overall_nearest # Prevent the division by 0 error if last > 1: return (nearest - 1) / (last - 1), nearest else: return 0, nearest # Find the best boost pads to use to refuel as fast as possible def find_best_boost_pads(): best_pad = None best_score = math.inf for i in range(len(self.get_field_info().boost_pads)): pad = self.get_field_info().boost_pads[i] if packet.game_boosts[ i].is_active == True or packet.game_boosts[i].timer <= ( Vec3(pad.location) - car_location).length() / 2300: score = (Vec3(pad.location) - car_location).length( ) * safe_div(car_velocity.length()) + math.sin( ((Vec3(pad.location) - car_location) / (Vec3(pad.location) - car_location).length() - car_velocity * safe_div(car_velocity.length()) ).length() / 2) * 3 / math.pi if pad.is_full_boost: score *= safe_div((50 - my_car.boost / 2) / 6) if score < best_score: best_score = score best_pad = pad return Vec3(best_pad.location) # Get the yaw based on position def get_yaw(x, y): a = math.acos(x / Vec3(x, y, 0).length()) if abs(a) < math.pi: return a * sign(y) else: return math.pi # Get the angle between two places def get_angle(p1, p2): d = (p1 * safe_div(p1.length()) - p2 * safe_div(p2.length())).length() angle = 2 * math.asin(d / 2) return angle # Determine when the car would intersect the ball assuming a speed def intersect_time(surface): t1 = 5 t2 = 5 best_spd = math.inf slowest = math.inf if surface == False: for i in range(1, 101): time_location = Vec3(predict_ball(i / 20).physics.location) if (time_location - car_location).length( ) * 20 / i <= car_velocity.length() and t1 == 5: t1 = i / 20 if (time_location - car_location).length() * 20 / i <= slowest: slowest = (time_location - car_location).length() * 20 / i t2 = i / 20 else: for i in range(1, 101): time_location = Vec3(predict_ball(i / 20).physics.location) if (distance_from_surface(time_location) - distance_from_surface(car_location)).length( ) * 20 / i <= car_velocity.length() and t1 == 5: t1 = i / 20 if (distance_from_surface(time_location) - distance_from_surface(car_location) ).length() * 20 / i <= slowest: slowest = (distance_from_surface(time_location) - distance_from_surface(car_location) ).length() * 20 / i t2 = i / 20 return t1, t2 # Determine when the car should jump def jump_ready(val): if val <= 300 and val > 92.75: delay = (2 * (val - 92.75) / 650)**0.5 return delay else: return 0 # Get the nearest player to the ball def nearest_player(teams, speed_offset): nearest = None best_time = math.inf last = True for i in range(len(packet.game_cars)): if (packet.game_cars[i].team != (-teams * packet.game_cars[self.index].team + (1 + teams) / 2) or teams == False) and Vec3( packet.game_cars[i].physics.location) != Vec3( 0, 0, 0): time_taken = (Vec3( packet.game_cars[i].physics.location ) - ball_location).length() / (Vec3( packet.game_cars[i].physics.velocity ).length() + speed_offset) + math.sin( (Vec3(packet.game_cars[i].physics.velocity) * safe_div( Vec3(packet.game_cars[i].physics.velocity).length( )) - ball_location * safe_div(ball_location.length())).length() / 2) * 3 / math.pi if time_taken < best_time: best_time = time_taken nearest = packet.game_cars[i] return nearest, best_time # Get the list of moments when the ball should be hit def opportunities(): li = [] prev_pos = ball_location for i in range(1, 101): time_location = predict_ball(i / 20) if time_location.z <= 300: li.append([ i / 20, (time_location - car_location).length() * 20 / i ]) prev_pos = time_location return li # ??? def plane_dist(pos, dist): if Vec3(pos.x, pos.y, 0).length() <= dist: return True elif Vec3(pos.x, 0, pos.z).length() <= dist: return True elif Vec3(0, pos.y, pos.z).length() <= dist: return True else: return False # Get the ball's position and velocity at a specific time def predict_ball(t): ball_in_future = find_slice_at_time( ball_prediction, packet.game_info.seconds_elapsed + t) if ball_in_future is not None: return ball_in_future else: return packet.game_ball # Divide numbers without division by 0 def safe_div(x): if x == 0: return math.inf else: return 1 / x # Return the direction of the value from 0 def sign(x): if x < 0: return -1 elif x > 0: return 1 else: return 0 # Return a value with limitations def clamp(x, m, M): if x < m: return m elif x > M: return M else: return x # Move the target location to straighten the ascent up walls def move_target_for_walls(pos1, pos2): up1 = distance_from_surface(pos1) up2 = distance_from_surface(pos2) new_pos = pos2 if up1.z == 0 and up2.z > 0: new_pos = Vec3(new_pos, 0, new_pos) + (up2 - pos2) * safe_div( (up2 - pos2).length()) * up2.z if up1.z > 0 and up2.z == 0 and pos1.z >= 30: if abs(up1.x) == self.map_size[1]: new_pos = Vec3(up1.x, up2.y, -abs(up2.x - up1.x)) elif abs(up1.y) == self.map_size[0]: new_pos = Vec3(up2.x, up1.y, -abs(up2.y - up1.y)) return new_pos # Actions # Use flip jumps to attack the ball def jump_attack(): dir_y = 0 dir_x = 0 if ((car_location + car_velocity / 5) - (ball_location + ball_velocity / 5)).length() <= 175: if (car_location + Vec3(car_velocity.y, -car_velocity.x, 0) * safe_div(car_velocity.length()) - ball_location).length() < (car_location - ball_location).length(): dir_x = -1 if (car_location + Vec3(-car_velocity.y, car_velocity.x, 0) * safe_div(car_velocity.length()) - ball_location).length() < (car_location - ball_location).length(): dir_x = 1 if (car_location + car_velocity / 5 - ball_location - ball_velocity / 5 ).length() <= (car_location - ball_location).length() - 50: dir_y = -1 return self.flip(packet, dir_x, dir_y, not my_car.jumped) # Jump over cars to prevent collision def avoid_bump(): for i in range(len(packet.game_cars)): if i != self.index and packet.game_cars[ i].physics.location.z > 0: pos = car_location pos2 = Vec3(packet.game_cars[i].physics.location) vel = car_velocity vel2 = Vec3(packet.game_cars[i].physics.velocity) dist = (pos - pos2).length() on_course = False if dist > 40: if get_angle(pos2 - pos, vel) <= math.tan( math.sqrt(40**2 / (dist**2 - 40**2))): on_course = True else: on_course = True if on_course == True and dist <= 40 + (vel - vel2).length( ) / 2 and (vel.length() <= vel2.length() or packet.game_cars[i].team != self.team): self.jump_once(packet) def deja_vu(): if car_velocity.length() > 100: point_direction = Vec3( math.cos(car_rotation.yaw) * math.cos(car_rotation.pitch), math.sin(car_rotation.yaw) * math.cos(car_rotation.pitch), math.sin(car_rotation.pitch)) controls.handbrake = ( (target_location - car_location) * safe_div( (target_location - car_location).length()) - point_direction).length() > 0.2 # Modes def demo(): nearest = None best_time = math.inf last = True for i in range(len(packet.game_cars)): if packet.game_cars[i].team != self.team and Vec3( packet.game_cars[i].physics.location).z > 0: time_taken = (Vec3(packet.game_cars[i].physics.location) - car_location).length() if time_taken < best_time: best_time = time_taken nearest = packet.game_cars[i] # Get location target_location = Vec3( nearest.physics.location) + Vec3(nearest.physics.velocity) * ( (Vec3(nearest.physics.location) - car_location).length() - 30) * safe_div(car_velocity.length()) controls.throttle = 1 controls.boost = abs(steer_toward_target( my_car, target_location)) <= 0.1 and car_velocity.length() < 2200 self.renderer.draw_line_3d(car_location, target_location, self.renderer.black()) self.renderer.draw_rect_3d(target_location, 8, 8, True, self.renderer.black(), centered=True) return target_location def attack(): # Target target_ball = predict_ball(earliest_intersection) target_location = Vec3(target_ball.physics.location) target_location = target_location + ( target_location - send_location) / ( target_location - send_location).length() * 92.75 # Smoother wall transitions target_location = move_target_for_walls(car_location, target_location) # Manage speed if velocity_from_surface( Vec3(target_ball.physics.location), Vec3(target_ball.physics.velocity)) >= 0 or (Vec3( target_ball.physics.location) - distance_from_surface( Vec3(target_ball.physics.location))).length( ) <= 300 or target_ball.physics.location.y * sign( send_location.y) <= -self.map_size[0]: controls.throttle = 1 else: controls.throttle = 0 # Boost controls.boost = abs( steer_toward_target(my_car, target_location) ) <= 0.1 and car_velocity.length() < 2300 and ( (Vec3(target_ball.physics.location) - distance_from_surface( Vec3(target_ball.physics.location))).length() <= 300 or velocity_from_surface(Vec3(target_ball.physics.location), Vec3(target_ball.physics.velocity)) >= 0) and nearest_car == 0 # Jump h = (Vec3(target_ball.physics.location) - distance_from_surface( Vec3(target_ball.physics.location))).length() if jump_ready( (Vec3(target_ball.physics.location) - distance_from_surface(Vec3(target_ball.physics.location)) ).length()) >= earliest_intersection and steer_toward_target( my_car, target_location) < 1: controls.pitch = 0 controls.yaw = 0 controls.roll = 0 controls.jump = True controls.boost = False self.jumping = 0 else: jump_attack() # Catch the ball when in the air if abs( target_location.z - car_location.z ) <= 92.75 * 0.75 and get_angle( target_location - car_location, car_velocity ) >= math.atan( 200 / car_velocity.length() ) and my_car.jumped == True and my_car.double_jumped == False and False: controls.yaw = sign( (target_location - car_location - Vec3(car_velocity.y, -car_velocity.x, car_velocity.z) ).length() - (target_location - car_location - Vec3(-car_velocity.y, car_velocity.x, car_velocity.z) ).length()) controls.jump = True # Draw self.renderer.draw_line_3d(car_location, target_location, self.renderer.red()) self.renderer.draw_rect_3d(target_location, 8, 8, True, self.renderer.red(), centered=True) return target_location def standby(): # Friendly/enemey if enemy_time >= friendly_time + 0.2: nearest_ref = nearest_friendly md = "friendly" else: nearest_ref = nearest_enemy md = "enemy" # Target if md == "enemy" and False: target_location = ball_location - ( Vec3(nearest_ref.physics.location) - ball_location) / ( Vec3(nearest_ref.physics.location) - ball_location).length() * (2500 + Vec3( nearest_ref.physics.velocity).length() * 5 / 2.3) target_location = Vec3( target_location.x, ball_location.y + (target_location.y - ball_location.y) * sign(target_location.y - ball_location.y) * -sign(send_location.y), target_location.z) else: target_location = ball_location - ( Vec3(nearest_ref.physics.location) - ball_location) / ( Vec3(nearest_ref.physics.location) - ball_location).length() * (2500 + Vec3( nearest_ref.physics.velocity).length() * 5 / 2.3) target_location = target_location + ( target_location - send_location) / ( target_location - send_location).length() * 927.5 # Walls if (distance_from_surface(target_location) - target_location).length() <= 300: target_location = distance_from_surface(target_location) target_location = move_target_for_walls( car_location, target_location) else: target_location = Vec3(target_location.x, target_location.y, 0) # Draw self.renderer.draw_line_3d(car_location, target_location, self.renderer.green()) self.renderer.draw_rect_3d(target_location, 8, 8, True, self.renderer.green(), centered=True) # Manage speed controls.boost = False controls.throttle = (target_location - car_location).length() / 1000 return target_location def refuel(): # Target target_location = find_best_boost_pads() # Draw self.renderer.draw_line_3d(car_location, target_location, self.renderer.green()) self.renderer.draw_rect_3d(target_location, 8, 8, True, self.renderer.green(), centered=True) # Speed controls.throttle = 1 controls.boost = False return target_location def goalie(): target_location = car_location # Roll back onto the wheels if abs(car_rotation.roll) >= math.pi * 3 / 4: return self.jump_once(packet) # Prepare for a save if sign(car_location.y) == -sign(send_location.y) and abs( car_location.y) >= abs(send_location.y): target_location = car_location - Vec3(0, send_location.y, 0) if car_velocity.length() >= 850: controls.throttle = -1 else: if Vec3(car_velocity.x, car_velocity.y, 0).length() >= 250: return self.reverse_flip(packet) else: controls.pitch = 0 controls.roll = 0 if (not math.pi * 0.4 <= abs(car_rotation.yaw) <= math.pi * 0.6 or sign(car_rotation.yaw) == -sign(send_location.y)) and abs( car_velocity.z) <= 1: return self.jump_once(packet) controls.yaw = steer_toward_target( my_car, send_location) - my_car.physics.angular_velocity.z controls.throttle = 0 # Drive into the goal unless it's already done so else: target_location = defend() return target_location def defend(): if not in_goal: target_ball = predict_ball(earliest_intersection) target_location = Vec3(target_ball.physics.location) nearest_surface = (target_location - car_location) / ( target_location - car_location).length() * 140 nearest_surface_r = Vec3(-nearest_surface.y, nearest_surface.x, nearest_surface.z) nearest_surface_l = Vec3(nearest_surface.y, -nearest_surface.x, nearest_surface.z) if False: side = -sign((nearest_surface_l - send_location).length() - (nearest_surface_r - send_location).length()) else: side = -sign(((nearest_surface_l - ball_location) - ball_velocity).length() - ((nearest_surface_r - ball_location) - ball_velocity).length()) nearest_surface = Vec3(-nearest_surface.y * side, nearest_surface.x * side, nearest_surface.z) if abs(target_location.x + nearest_surface.x) >= self.map_size[1] or get_angle( car_location - send_location, car_location - target_location ) > math.pi / 3 * 2 or ball_location.z > 130: target_location = -send_location else: target_location = target_location + nearest_surface / 140 * car_velocity.length( ) controls.boost = enemy_time < 1 and abs( steer_toward_target(my_car, target_location)) <= 0.1 # Jump h = (Vec3(target_ball.physics.location) - distance_from_surface(Vec3( target_ball.physics.location))).length() if jump_ready( h) >= earliest_intersection and steer_toward_target( my_car, target_location) < 1: controls.pitch = 0 controls.yaw = 0 controls.roll = 0 controls.jump = True controls.boost = False self.jumping = 0 else: jump_attack() # Draw self.renderer.draw_line_3d(car_location, ball_location + nearest_surface, self.renderer.cyan()) self.renderer.draw_rect_3d(ball_location + nearest_surface, 8, 8, True, self.renderer.cyan(), centered=True) controls.throttle = 1 else: target_location = -send_location controls.throttle = 1 return target_location def recovery(): tx = (self.map_size[1] - car_location.x * sign(car_velocity.x)) * safe_div(car_velocity.x) ty = (self.map_size[0] - car_location.y * sign(car_velocity.y)) * safe_div(car_velocity.y) tt = 0 if car_location.z / 325 - (car_velocity.z / 650)**2 >= 0: tz = car_velocity.z / 650 + math.sqrt(car_location.z / 325 - (car_velocity.z / 650)**2) else: tz = car_velocity.z / 650 - math.sqrt(car_location.z / 325 + (car_velocity.z / 650)**2) if tx >= tz <= ty: controls.roll = -car_rotation.roll controls.pitch = -car_rotation.pitch tt = tz elif tx >= ty <= tz: point = (math.pi / 2 + sign(abs(car_rotation.yaw) - math.pi / 2) * math.pi / 2) * sign(car_rotation.yaw) controls.roll = math.pi / 2 * sign(car_velocity.y) * sign( abs(car_rotation.yaw) - math.pi / 2) - car_rotation.roll controls.pitch = point - car_rotation.yaw tt = ty draw_point = Vec3( car_location.x + car_velocity.x * tt, car_location.y + car_velocity.y * tt, car_location.z + car_velocity.z * tt - 325 * tt**2) self.renderer.draw_line_3d(car_location, draw_point, self.renderer.pink()) self.renderer.draw_rect_3d(draw_point, 8, 8, True, self.renderer.pink(), centered=True) # Variables my_car = packet.game_cars[self.index] car_location = Vec3(my_car.physics.location) car_velocity = Vec3(my_car.physics.velocity) car_rotation = my_car.physics.rotation ball_prediction = self.get_ball_prediction_struct() ball_location = Vec3(packet.game_ball.physics.location) ball_velocity = Vec3(packet.game_ball.physics.velocity) send_location = Vec3(0, sign(0.5 - self.team) * self.map_size[0], 0) earliest_intersection, easiest_intersection = intersect_time(True) nearest_car, car_index = distance_order(1) nearest_enemy, enemy_time = nearest_player(0, 460) nearest_friendly, friendly_time = nearest_player(0, 460) in_goal = (ball_location + send_location).length() > ( car_location + send_location).length() target_location = car_location mode = "" # Controls controls = SimpleControllerState() # Half-flip to quickly turn if car_velocity.length() <= 500 and get_angle( car_velocity, target_location - car_location) >= math.pi / 3 * 2: return self.reverse_flip(packet) # Assign role in the team if abs(send_location.y + Vec3(predict_ball(2).physics.location).y ) <= 1000 or abs(send_location.y + ball_location.y) <= 1000: if in_goal: mode = "Attack" else: mode = "Defense" else: if nearest_car == 0: if in_goal: mode = "Attack" else: mode = "Defense" elif nearest_car < 1: if my_car.boost == 100 and car_index == 2: mode = "Standby" else: mode = "Refuel" elif nearest_car < 1: if my_car.boost >= 50 or car_velocity.length() >= 2200: mode = "Demo" else: mode = "Refuel" else: mode = "Goalie" # Recovery if self.jumping >= 1: recovery() # Demolition if mode == "Demo": target_location = demo() # Attack the ball if mode == "Attack": avoid_bump() target_location = attack() controls.use_item = True # Stand by for hit if mode == "Standby": avoid_bump() target_location = standby() # Collect boost if mode == "Refuel": avoid_bump() target_location = refuel() # Retreat if mode == "Defense": avoid_bump() target_location = defend() # Goalie if mode == "Goalie": avoid_bump() target_location = goalie() controls.steer = steer_toward_target(my_car, target_location) # Draw if False: self.renderer.draw_string_2d( 800, 200, 1, 1, "X: " + str(packet.game_cars[1 - self.index].physics.location.x), self.renderer.white()) self.renderer.draw_string_2d( 800, 220, 1, 1, "Y: " + str(packet.game_cars[1 - self.index].physics.location.y), self.renderer.white()) self.renderer.draw_string_2d(1400, 200, 1, 1, "Map: " + str(self.map), self.renderer.white()) self.renderer.draw_string_2d( 50, 680 + car_index * 20 * (0.5 - self.team) * 2, 1, 1, "Noob Bot " + str(car_index) + ": " + str(mode), self.renderer.white()) self.renderer.draw_line_3d(target_location, distance_from_surface(target_location), self.renderer.yellow()) self.jumping += 1 / 60 return controls
def get_yaw(x, y): a = math.acos(x / Vec3(x, y, 0).length()) if abs(a) < math.pi: return a * sign(y) else: return math.pi
def is_turn_doable(speed, turn_angle, local_target): turn_rad = turn_radius(speed) angle_sign = -1 if turn_angle < 0 else 1 center_of_turn_circle = Vec3(0, angle_sign * turn_rad, 0) return (local_target - center_of_turn_circle).length() < turn_rad
def __init__(self, agent: BaseAgent): super().__init__(agent) self.balanceTime = 0 self.carToTargetIntegral = Vec3() self.steerBiasLimit = 0.5 self.lastVelocities = [vec3(0, 0, 0)] * 32
def tick(self, packet: GameTickPacket) -> bool: if super().tick(packet): return True myCar = packet.game_cars[self.agent.index] if self.agent.spikeWatcher.carrying_car == myCar: self.attachedTicks += 1 if self.attachedTicks > 14: return False else: self.attachedTicks = 0 targetSide = 1 - 2 * myCar.team carDirection = -myCar.physics.rotation.yaw carLocation = Vec3(myCar.physics.location) carVelocity = Vec3(myCar.physics.velocity) carSpeed = carVelocity.length() ballLocation = Vec3(packet.game_ball.physics.location) ballVelocity = Vec3(packet.game_ball.physics.velocity) ballFutureTime = 1 / 60 ball_prediction = self.agent.get_ball_prediction_struct() closestTeamCarDistance = 9999999 bestCar = None for j in range(0, packet.num_cars): car = packet.game_cars[j] distance = (ballLocation - Vec3(car.physics.location)).length() if car.team == myCar.team and distance < closestTeamCarDistance and not car.is_demolished: closestTeamCarDistance = distance bestCar = car # initialise it in case ball prediction isnt productive if ball_prediction is not None: for i in range(0, ball_prediction.num_slices): prediction_slice = ball_prediction.slices[i] if prediction_slice.physics.location.z - max( prediction_slice.physics.velocity.z / 60, 0) < 100: possibleBallFutureTime = (i + 1) / 60 possibleBallLocation = Vec3( prediction_slice.physics.location) for j in range(0, packet.num_cars): car = packet.game_cars[j] if car.team == myCar.team and not car.is_demolished: if ((ballLocation - Vec3(car.physics.location)).flat().length() - 200) / possibleBallFutureTime <= 2300: ballFutureTime = possibleBallFutureTime ballLocation = possibleBallLocation ballVelocity = Vec3( prediction_slice.physics.velocity) bestCar = car break else: continue break shadowing = bestCar != myCar and ( self.agent.spikeWatcher.carrying_car is None or self.agent.spikeWatcher.carrying_car.team == myCar.team) if shadowing: ballLocation.y -= 1250 * targetSide ballToCarAbsoluteLocation = (ballLocation - carLocation).flat() ballToCarLocation = ballToCarAbsoluteLocation.rotate_2D(carDirection) ballToCarVelocity = (ballVelocity - carVelocity).flat().rotate_2D(carDirection) angle = ballToCarLocation.atan2() driveDistance = (ballLocation - carLocation).flat().length() if self.agent.spikeWatcher.carrying_car is None or self.agent.spikeWatcher.carrying_car.team == myCar.team: targetSpeed = max(driveDistance, 0) / ballFutureTime targetThrottle = (targetSpeed - Vec3(myCar.physics.velocity).length()) / 300 targetThrottle = max( min((ballFutureTime - .25) * .8, (abs(ballToCarLocation.x) - 700) / 1500), targetThrottle) targetThrottle = min(1, targetThrottle) else: targetThrottle = 1 steer = min(2, max(-2, 4 * angle)) # if ballToCarLocation.length() < 1000: # steer += 0.005 * ballToCarVelocity.y self.controller.steer = min(1, max(-1, steer)) frontOrBehind = 1 - math.fabs(angle) / ( math.pi if ballToCarLocation.flat().length() > 500 else math.pi / 2 ) # allow backwards if close turnThrottle = min( 1, max(-1, math.copysign(.2, frontOrBehind) + frontOrBehind)) if driveDistance < 750 else 1 self.controller.throttle = targetThrottle * turnThrottle minimumSpeedRequired = 2300 - 991.667 / 120 * ( 1 if self.controller.boost else 10) wantToBoost = frontOrBehind > .9 and self.controller.throttle > .9 and ballToCarLocation.x > 700 self.controller.boost = (carSpeed < minimumSpeedRequired ) and myCar.boost > 0 and wantToBoost return True
def tick(self, packet: GameTickPacket) -> bool: if super().tick(packet): return True 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) realBallVelocity = 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()\ - 1000 * dot(self.agent.car.forward(), normalize(flatten(self.agent.game.ball.position - self.agent.car.position))) 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()\ - 1000 * dot(self.agent.game.cars[carIndex].forward(), normalize(flatten(self.agent.game.ball.position - self.agent.game.cars[carIndex].position))) # 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 and ballLocation.y * ( 2 * self.agent.team - 1) < 0 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 (not beAnnoying) \ 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() - Vec3(0, (5120+100) * teamDirection, 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 and not beAnnoying: if carLocation.y * ( 2 * self.agent.team - 1) > -1000 and ballToCarAbsoluteLocation.y * ( 2 * self.agent.team - 1) > 0: time = (5120 - ballLocation.y * (2 * self.agent.team - 1)) / max( 1, -ballToCarAbsoluteLocation.y * (2 * self.agent.team - 1)) projectOnWall = ballLocation.x + time * ballToCarAbsoluteLocation.x targetSteer = projectOnWall * (2 * self.agent.team - 1) 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: if ballToCarLocation.length() > 800: 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) else: self.steerBiasLimit = min( 1.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.15 - 0.65 * 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: minimumSpeedRequired = 2300 - 991.667 / 120 * ( 1 if self.controller.boost else 10) if realBallLocation.z > 500: afterTopTime = realBallVelocity.z / packet.game_info.world_gravity_z topHeight = max( 0, realBallLocation.z - 92 + 1 / 2 * -packet.game_info.world_gravity_z * afterTopTime**2) timeForBallToReachGround = max( 1 / 120, math.sqrt(2 * topHeight) - afterTopTime) nextBouncePosition = realBallLocation + timeForBallToReachGround * realBallVelocity nextBouncePosition = vec3(nextBouncePosition.x, nextBouncePosition.y, nextBouncePosition.z) minimumSpeedRequired = min( minimumSpeedRequired, norm(flatten(self.agent.car.position - nextBouncePosition)) / timeForBallToReachGround) self.controller.boost = (carSpeed < minimumSpeedRequired) and myCar.boost > 0 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 rlbot_action_server.bot_action_broker import BotActionBroker, run_action_server, find_usable_port from rlbot_action_server.bot_holder import set_bot_action_broker from rlbot_action_server.models import BotAction, AvailableActions, ActionChoice, ApiResponse from rlbot_action_server.formatting_utils import highlight_player_name from rlbot_twitch_broker_client import Configuration, RegisterApi, ApiClient, ActionServerRegistration from rlbot_twitch_broker_client.defaults import STANDARD_TWITCH_BROKER_PORT from urllib3.exceptions import MaxRetryError from rlutilities.linear_algebra import vec3, norm from rlutilities.simulation import Field, Game, ray as Ray from util.vec import Vec3 from util.orientation import Orientation, look_at_orientation BALL_RADIUS = 93.15 HITBOX_HALF_WIDTHS = Vec3(59.00368881, 42.09970474, 18.07953644) HITBOX_OFFSET = Vec3(13.97565993, 0.0, 20.75498772) PUSH_STRENGTH_BALL = BASE_PUSH_STRENGTH * 4 PUSH_STRENGTH_BALL_ANGULAR = BASE_PUSH_STRENGTH * 20 PUSH_STRENGTH_CAR = BASE_PUSH_STRENGTH * 3 PUSH_STRENGTH_CAR_ANGULAR = BASE_PUSH_STRENGTH * 0.85 SET_LASER_BOI = 'setLaserBoi' PLAYER_NAME = 'playerName' @dataclass class Push: velocity: Vec3 angular_velocity: Vec3
def draw_point(renderer, location, color): for axis in (Vec3(0, 0, 1), Vec3(0, 1, 0), Vec3(1, 0, 0)): renderer.draw_line_3d(location + 100 * axis, location - 100 * axis, color)
def __init__(self): self.velocity = Vec3(0, 0, 0) self.angular_velocity = Vec3(0, 0, 0)
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: """ This function will be called by the framework many times per second. This is where you can see the motion of the ball, etc. and return controls to drive your car. """ # Keep our boost pad info updated with which pads are currently active self.boost_pad_tracker.update_boost_status(packet) # This is good to keep at the beginning of get_output. It will allow you to continue # any sequences that you may have started during a previous call to get_output. if self.active_sequence and not self.active_sequence.done: controls = self.active_sequence.tick(packet) if controls is not None: return controls # Gather some information about our car and the ball my_car = packet.game_cars[self.index] car_location = Vec3(my_car.physics.location) car_velocity = Vec3(my_car.physics.velocity) ball_location = Vec3(packet.game_ball.physics.location) if my_car.team == 1: # Orange Team self.my_goal_right_post = Vec3(850, 5100, 320) self.my_goal_center = Vec3(0, 5100, 320) self.my_goal_left_post = Vec3(-850, 5100, 320) self.enemy_goal_right_post = Vec3(850, -5100, 320) self.enemy_goal_center = Vec3(0.0, -5100, 320) self.enemy_goal_left_post = Vec3(-850, -5100, 320) else: # Blue Team self.my_goal_right_post = Vec3(-850, -5100, 320) self.my_goal_center = Vec3(0.0, -5100, 320) self.my_goal_left_post = Vec3(850, -5100, 320) self.enemy_goal_right_post = Vec3(-850, 5100, 320) self.enemy_goal_center = Vec3(0, 5100, 320) self.enemy_goal_left_post = Vec3(850, 5100, 320) controls = SimpleControllerState() # Draw some things to help understand what the bot is thinking # self.renderer.draw_line_3d(car_location, target_location, self.renderer.white()) # self.renderer.draw_string_3d(car_location, 1, 1, f'Speed: {car_velocity.length():.1f}', self.renderer.white()) # self.renderer.draw_rect_3d(target_location, 8, 8, True, self.renderer.cyan(), centered=True) # Important flags to check / set if my_car.has_wheel_contact == True: self.airborne = False else: # try to recover here if self.airborne == False: self.send_quick_chat( team_only=False, quick_chat=QuickChatSelection.Custom_Useful_Faking) self.airborne = True if self.ball_in_kickoff_position(ball_location, packet): self.kickoff_active = True else: self.kickoff_active = False self.kickoff_position = None if self.kickoff_active and self.kickoff_position is None: self.kickoff_position = self.get_kickoff_position(car_location) # Main state checking area if self.kickoff_active and self.kickoff_position is not None: self.do_kickoff(my_car, car_location, car_velocity, ball_location, controls, packet) else: self.ball_chase(controls, my_car, car_location, car_velocity, ball_location, packet) # Other things that have been written: # self.boost_steal(controls, car_location, my_car, ball_location) # self.half_flip_sequence(packet) # self.manage_speed(packet, controls, my_car, car_velocity) return controls
def update(self, packet): self.velocity = Vec3(packet.game_ball.physics.velocity) self.rotation = packet.game_ball.physics.rotation self.location = Vec3(packet.game_ball.physics.location) self.angular_velocity = Vec3(packet.game_ball.physics.angular_velocity)
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: ball_location = Vec3(packet.game_ball.physics.location) ball_velocity = Vec3(packet.game_ball.physics.velocity) if not self.is_zombie: other_car = packet.game_cars[not self.index] team = packet.game_cars[self.index].team my_car = packet.game_cars[self.index] car_location = Vec3(my_car.physics.location) car_to_ball = ball_location - car_location car_ball_distance = distance2D(ball_location, car_location) # Find the direction of our car using the Orientation class car_orientation = Orientation(my_car.physics.rotation) car_direction = car_orientation.forward ball_future = Vec3( clamp( ball_location.x + ball_velocity.x * (car_ball_distance / 2000), -4096, 4096), clamp( ball_location.y + ball_velocity.y * (car_ball_distance / 1500), -5020, 5020), max(ball_location.z + ball_velocity.z * (car_ball_distance / 1500), -ball_location.z)) # print(f"car_ball_distance: {car_ball_distance}") # print(f"ball_future: {ball_future}") # print(f"ball_location: {ball_location}") car_to_future = ball_future - car_location car_ball_future = distance2D(ball_future, car_location) enemy_goal = Vec3(0, -5120, 0) if team == 1 else Vec3(0, 5120, 0) team_goal = Vec3(0, -5120, 0) if not team == 1 else Vec3(0, 5120, 0) car_to_own_goal = team_goal - car_location ball_to_goal = distance2D(ball_future, team_goal) #print(ball_to_goal) steer_correction_radians = find_correction(car_direction, car_to_future) pi = math.pi try: other_car_d2b = distance2D(ball_future, Vec3(other_car.physics.location)) except: pass #print(max((magnitude2D(Vec3(packet.game_ball.physics.velocity))/1000),1)) #print(team_cars) try: if other_car_d2b < car_ball_future and ball_to_goal > 2500: steer_correction_radians = find_correction( car_direction, car_to_own_goal) heading_back = True else: heading_back = False except: pass distance_to_use = -200 if self.team == 0 else 200 if ball_future.y < car_location.y - distance_to_use and ball_to_goal > 2500: steer_correction_radians = find_correction(car_direction, car_to_own_goal) heading_back = True else: heading_back = False if steer_correction_radians > 0.1: # Positive radians in the unit circle is a turn to the left. turn = -1.0 # Negative value for a turn to the left. action_display = "turn left" elif steer_correction_radians < -0.1: turn = 1.0 action_display = "turn right" else: turn = 0 action_display = "Go Forward" if abs(steer_correction_radians) > .7 and abs( steer_correction_radians) < (pi - 0.8): self.controller_state.handbrake = 1 else: self.controller_state.handbrake = 0 if not heading_back: speed = distance2D( ball_future, car_location ) / 500 if ball_location.x != 0 and ball_location.y != 0 else 1 if (speed >= 2 and abs(steer_correction_radians) < 0.1) or speed == 1: self.controller_state.boost = 1 else: self.controller_state.boost = 0 speed = min(speed, 1) if abs(steer_correction_radians) > (pi * 4 / 8): speed = max(((speed * -1) * car_ball_future / 100) / 2, -1) if not heading_back else -1 turn = turn * -1 else: speed = min(distance2D(team_goal, car_location) // 500, 1) #print(speed) #print(speed) if 500 > ball_future.z > 200 and car_ball_distance < 500: self.controller_state.jump = 1 else: self.controller_state.jump = 0 self.controller_state.throttle = speed self.controller_state.steer = turn draw_debug(self.renderer, my_car, ball_future, action_display) return self.controller_state