Skip to content

Input and Output Data

redd edited this page Nov 25, 2022 · 36 revisions

BaseAgent

The base agent has some useful values that we can read from in order to get some basic information about the car that we are controlling, the team our car is on, and even the name of our car in the game.

These values can be accessed at any time from the base agent using the self parameter:

def initialize_agent(self):
    # Print the bot's team
    print(self.team)

def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
    # Get our car
    my_car = packet.game_cars[self.index]

Some useful values to know are:

self.team = 0                # The team that our bot is on
self.name = "ExampleBot"     # Our bot's in-game name
self.index = 2               # The index of the car our agent is controlling

GameTickPacket

Every tick, your bot will receive a GameTickPacket from the framework. The packet will contain all the raw values from the game (such as car and ball locations). Your bot receives the packet in the get_output(self, packet) function and you should return a ControllerState which describes what your bot's response is.

Getting values from the packet is simple:

def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
    
    # You can get values out of the packet like this:
    x_location_of_first_car = packet.game_cars[0].physics.location.x;

NOTE: Some structs/lists in the GameTickPacket has a fixed length. So make sure you use the num_cars and the num_boosts values as lengths if you iterate through the associated lists.

Sample game tick packet

packet: {
    'game_cars': [
        {
            'physics': {
                'location': {'x': 0.0, 'y': 0.0, 'z': 0.0},
                'rotation': {'pitch': 0.0, 'yaw': 0.0, 'roll': 0.0},
                'velocity': {'x': 0.0, 'y': 0.0, 'z': 0.0},
                'angular_velocity': {'x': 0.0, 'y': 0.0, 'z': 0.0}
            },
            'is_demolished': False,
            'has_wheel_contact': True,
            'is_super_sonic': False,
            'is_bot': True,
            'jumped': False,
            'double_jumped': True,
            'name': 'Jupiter',
            'team': 0,
            'boost': 48.0,
            'hitbox': {'length': 118, 'width': 84, 'height': 36},
            'hitbox_offset': {'x': 13.88, 'y': 0.0, 'z': 20.75},
            'score_info': {
                'score': 340,
                'goals': 2,
                'own_goals': 0,
                'assists': 1,
                'saves': 1,
                'shots': 3,
                'demolitions': 1
            }
        },
        { ... }
    ],
    'num_cars': 2,
    'game_boosts': [
        {
            'is_active': True,
            'timer': 0.0
        },
        { ... }
    ],
    'num_boost': 36,
    'game_ball': {
        'physics': {
            'location': {'x': 0.0, 'y': 0.0, 'z': 0.0},
            'rotation': {'pitch': 0.0, 'yaw': 0.0, 'roll': 0.0},
            'velocity': {'x': 0.0, 'y': 0.0, 'z': 0.0},
            'angular_velocity': {'x': 0.0, 'y': 0.0, 'z': 0.0}
        },
        'latest_touch': {
            'player_name': 'Beavis',
            'time_seconds': 120.63,
            'hit_location': {'x': 0.0, 'y': 0.0, 'z': 0.0},
            'hit_normal': {'x': 0.0, 'y': 0.0, 'z': 0.0},
            'team': 0,
            'player_index': 0
        },
        'drop_shot_info': {
            'damage_index': 0,
            'absorbed_force': 0,
            'force_accum_recent': 0
        },
        'collision_shape': {
            'type': 1,
            'box': {'length': 153.0, 'width': 153.0, 'height': 153.0},
            'sphere': {'diameter': 184.0},
            'cylinder': {'diameter': 184.0, 'height': 30.0}
        }
    },
    'game_info': {
        'seconds_elapsed': 405.12,
        'game_time_remaining': 34.0,
        'is_overtime': False,
        'is_unlimited_time': False,
        'is_round_active': True,
        'is_kickoff_pause': False,
        'is_match_ended': False,
        'world_gravity_z': -650.0,
        'game_speed': 1.0,
        'frame_num': 3923
    },
    'teams': [
        {
            'team_index': 0,
            'score': 7
        },
        { ... }
    ],
    'num_teams': 2,
}

ControllerState

A ControllerState is the controller input the bot should perform. Import ControllerState like this: from rlbot.agents.base_agent import SimpleControllerState, and create a new instance with SimpleControllerState()

Example:

from rlbot.agents.base_agent import BaseAgent, SimpleControllerState
from rlbot.utils.structures.game_data_struct import GameTickPacket


class ExampleBot(BaseAgent):
    def initialize_agent(self):
        pass

    def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
        # Make the bot go forwards by setting throttle to 1
        controller = SimpleControllerState()
        controller.throttle = 1
        return controller

The ControllerState has the following attributes:

{
  throttle:float; /// -1 for full reverse, 1 for full forward
  steer:float; /// -1 for full left, 1 for full right
  pitch:float; /// -1 for nose down, 1 for nose up
  yaw:float; /// -1 for full left, 1 for full right
  roll:float; /// -1 for roll left, 1 for roll right
  jump:bool; /// true if you want to press the jump button
  boost:bool; /// true if you want to press the boost button
  handbrake:bool; /// true if you want to press the handbrake button
  use_item:bool; /// true if you want to use a rumble item
}

Field Info

A few values are constant, like the locations of boosts and goals. Some of these can be found in the FieldInfo data. FieldInfo contains the following:

field_info: {
    'boost_pads': [
        {
            'location': Vector3,
            'is_full_boost': boolean
        },
        { ... }
    ],
    'num_boosts': int,
    'goals': [
        {
            'team_num': int,
            'location': Vector3,
            'direction': Vector3,
            'width': float,
            'height': float
        },
        { ... }
    ],
    'num_goals': int
}

Note: Dropshot tiles can be found in FieldInfo as GoalInfo objects. Note2: Boost pads and Dropshot tiles will be sorted according to y * 100 + x, just like their counterparts (game_boosts and dropshot_tiles that contain their current state) in the game tick packet.

In Python, you can retrieve this information by calling get_field_info() on the BaseAgent:

from rlbot.agents.base_agent import BaseAgent, SimpleControllerState
from rlbot.utils.structures.game_data_struct import GameTickPacket

class ExampleBot(BaseAgent):
    def initialize_agent(self):
        pass

    def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
        # Constant values can be found the the FieldInfo:
        info = self.get_field_info()
        
        # Manually construct a list of all big boost pads
        # info.boost_pads has a fixed size but info.num_boosts is how many pads there actually are
        big_pads = []
        for i in range(info.num_boosts):
            pad = info.boost_pads[i]
            if pad.is_full_boost:
                big_pads.append(pad)

Match settings (mutators & gamemode)

Mutators can only be set before the game starts by a human.

To get the current mutators, do something like this:

from rlbot.messages.flat.BoostOption import BoostOption

def initialize_agent(self):
    # See all of the match settings at https://github.com/RLBot/RLBot/blob/master/src/main/flatbuffers/rlbot.fbs#L784
    match_settings = self.get_match_settings()
    mutators = match_settings.MutatorSettings()

    # Examples

    # Game mode
    game_mode = (
        "soccer",
        "hoops",
        "dropshot",
        "hockey",
        "rumble",
        "heatseeker"
    )
    self.gamemode = game_mode[match_settings.GameMode()]

    # View all mutator options at https://github.com/RLBot/RLBot/blob/master/src/main/flatbuffers/rlbot.fbs#L753

    # Boost amount
    if mutators.BoostOption() == BoostOption.Unlimited_Boost:
        # Do whatever

    # Boost acceleration
    # Leverage some known constants to get an acceleration value based on the setting.
    # These values are from the Physics section of the Useful Game Values wiki -> https://github.com/RLBot/RLBot/wiki/Useful-Game-Values#physics
    base_boost_accel = 991 + (2/3)
    boost_accel = (
        base_boost_accel, # Default
        base_boost_accel * 1.5, # Strength x 1.5
        base_boost_accel * 2,  # Strength x 2
        base_boost_accel * 10  # Strength x 10
    )

    self.boost_accel = boost_accel[mutators.BoostStrengthOption()]
    

Documentation on what it means

You can find descriptions of the tricky values in this file:

https://github.com/RLBot/RLBot/blob/master/src/main/flatbuffers/rlbot.fbs

You will recognize some of the previous mentioned classes as tables. I.e. packet.score_info is a ScoreInfo table.