コード例 #1
0
class VirxERLU(BaseAgent):
    # Massive thanks to ddthj/GoslingAgent (GitHub repo) for the basis of VirxERLU
    def initialize_agent(self):
        self.tournament = True
        self.print("Initalizing thread(s)...")

        if not self.tournament:
            self.gui = Gui(self)
            self.print("Starting the GUI...")
            self.gui.start()

        self.predictions = {
            "closest_enemy": 0,
            "own_goal": False,
            "goal": False,
            "ball_struct": None,
            "team_from_goal": (),
            "team_to_ball": (),
            "self_from_goal": 0,
            "self_to_ball": 0,
            "done": False
        }

        self.goalie = False
        self.air_bud = False

        self.debug = [[], []]
        self.debugging = False
        self.debug_lines = True
        self.debug_3d_bool = True
        self.debug_stack_bool = True
        self.debug_2d_bool = False
        self.show_coords = False
        self.debug_ball_path = False
        self.debug_ball_path_precision = 10

        self.disable_driving = False

        self.prediction = Prediction(self)
        self.print("Starting the predictive service...")
        self.prediction.start()

        self.match_comms = None

        self.print("Building game information")

        mutators = self.get_match_settings().MutatorSettings()

        gravity = [
            Vector(z=-650),
            Vector(z=-325),
            Vector(z=-1137.5),
            Vector(z=-3250)
        ]

        base_boost_accel = 991 + (2 / 3)

        boost_accel = [
            base_boost_accel, base_boost_accel * 1.5, base_boost_accel * 2,
            base_boost_accel * 10
        ]

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

        self.friends = ()
        self.foes = ()
        self.me = car_object(self.index)
        self.ball_to_goal = -1

        self.ball = ball_object()
        self.game = game_object(not self.team)

        self.boosts = ()

        self.friend_goal = goal_object(self.team)
        self.foe_goal = goal_object(not self.team)

        self.stack = []
        self.time = 0
        self.prev_time = 0

        self.ready = False

        self.controller = SimpleControllerState()

        self.kickoff_flag = False
        self.kickoff_done = True

        self.last_time = 0
        self.my_score = 0
        self.foe_score = 0

        self.playstyles = Playstyle
        self.playstyle = self.playstyles.Neutral

        self.can_shoot = None
        self.shooting = False
        self.shot_weight = -1
        self.shot_time = -1
        self.panic = False

        self.odd_tick = 0  # Use this for thing that can be run at 30 or 60 tps instead of 120

    def retire(self):
        # Stop the currently running threads
        if not self.tournament:
            self.gui.stop()

        if len(self.friends) > 0:
            self.match_comms.stop()

        self.prediction.stop()

    @staticmethod
    def is_hot_reload_enabled():
        # The tkinter GUI isn't compatible with hot reloading
        # Use the Continue and Spawn option in the GUI instead
        return False

    def get_ready(self, packet):
        field_info = self.get_field_info()
        self.boosts = tuple(
            boost_object(i, boost.location, boost.is_full_boost)
            for i, boost in enumerate(field_info.boost_pads))
        self.refresh_player_lists(packet)
        self.ball.update(packet)

        foe_team = -1 if self.team == 1 else 1
        team = -foe_team

        self.defensive_shots = (
            (self.foe_goal.left_post, self.foe_goal.right_post),  # Weight -> 4
            (Vector(4000, foe_team * 3250,
                    100), Vector(2750, foe_team * 3250, 100)),  # Weight -> 3
            (Vector(-4000, foe_team * 3250,
                    100), Vector(-2750, foe_team * 3250, 100)),  # Weight -> 3
            (Vector(-3600, z=100), Vector(-1000, z=500)),  # Weight -> 2
            (Vector(3600, z=100), Vector(1000, z=500))  # Weight -> 2
        )

        self.panic_shots = (
            (Vector(3100 * team, team * 3620,
                    100), Vector(3100 * team, team * 5120,
                                 100)),  # Weight -> 1
            (Vector(-3100 * team, team * 3620,
                    100), Vector(-3100 * team, team * 5120,
                                 100))  # Weight -> 1
        )

        # () => Weight -> 0
        # Short short => Weight -> -1

        self.offensive_shots = (
            (self.foe_goal.left_post, self.foe_goal.right_post),  # Weight -> 4
            (Vector(foe_team * 893, foe_team * 5120,
                    100), Vector(foe_team * 893, foe_team * 4720,
                                 320)),  # Weight -> 3
            (Vector(-foe_team * 893, foe_team * 5120,
                    100), Vector(-foe_team * 893, foe_team * 4720,
                                 320))  # Weight -> 3
        )

        self.best_shot = (Vector(foe_team * 650, foe_team * 5125, 320),
                          Vector(-foe_team * 650, foe_team * 5125, 320))

        self.max_shot_weight = 4

        self.init()

        self.ready = True
        self.print("Built")

    def refresh_player_lists(self, packet):
        # Useful to keep separate from get_ready because humans can join/leave a match
        self.friends = tuple(
            car_object(i, packet) for i in range(packet.num_cars)
            if packet.game_cars[i].team is self.team and i != self.index)
        self.foes = tuple(
            car_object(i, packet) for i in range(packet.num_cars)
            if packet.game_cars[i].team != self.team)

        if len(self.friends) > 0 and self.match_comms is None:
            self.match_comms = MatchComms(self)
            self.print("Starting the match communication handler...")
            self.match_comms.start()

    def push(self, routine):
        self.stack.append(routine)

    def pop(self):
        return self.stack.pop()

    def line(self, start, end, color=None):
        if self.debugging and self.debug_lines:
            color = color if color is not None else self.renderer.grey()

            if isinstance(start, Vector):
                start = start.copy().tuple()

            if isinstance(end, Vector):
                end = end.copy().tuple()

            self.renderer.draw_line_3d(
                start, end,
                color if type(color) != list else self.renderer.create_color(
                    255, *color))

    def print(self, item):
        if not self.tournament:
            team = "Blue" if self.team == 0 else "Red"
            print(f"{self.name} ({team}): {item}")

    def dbg_3d(self, item):
        self.debug[0].append(str(item))

    def dbg_2d(self, item):
        self.debug[1].append(str(item))

    def clear(self):
        self.shooting = False
        self.shot_weight = -1
        self.shot_time = -1
        self.stack = []
        return 'asdf'  # This is here in case I call this instead of is_clear() - It should throw an error if I do

    def is_clear(self):
        return len(self.stack) < 1

    def preprocess(self, packet):
        if packet.num_cars != len(self.friends) + len(self.foes) + 1:
            self.refresh_player_lists(packet)

        set(map(lambda car: car.update(packet), self.friends))
        set(map(lambda car: car.update(packet), self.foes))
        set(map(lambda pad: pad.update(packet), self.boosts))

        self.ball.update(packet)
        self.me.update(packet)
        self.game.update(packet)
        self.time = self.game.time

        # When a new kickoff begins we empty the stack
        if not self.kickoff_flag and self.game.round_active and self.game.kickoff:
            self.kickoff_done = False
            self.clear()

        # Tells us when to go for kickoff
        self.kickoff_flag = self.game.round_active and self.game.kickoff
        self.ball_to_goal = self.friend_goal.location.flat_dist(
            self.ball.location)

        if self.odd_tick % 2 == 0:  # This is ran @ 60 tps
            self.predictions['ball_struct'] = self.get_ball_prediction_struct()
            self.prediction.event.set()

        self.odd_tick += 1

        if self.odd_tick > 3:
            self.odd_tick = 0

    def get_output(self, packet):
        try:
            # Reset controller
            self.controller.__init__()
            # Get ready, then preprocess
            if not self.ready:
                self.get_ready(packet)
            self.preprocess(packet)

            if self.me.demolished:
                if not self.is_clear():
                    self.clear()
            elif self.game.round_active and self.predictions['done']:
                self.dbg_3d(self.playstyle.name)
                self.run(
                )  # Run strategy code; This is a very expensive function to run

                # run the routine on the end of the stack
                if not self.is_clear():
                    self.stack[-1].run(self)

                if self.is_clear() or self.stack[0].__class__.__name__ not in {
                        'Aerial', 'jump_shot', 'block_ground_shot'
                }:
                    self.shooting = False
                    self.shot_weight = -1
                    self.shot_time = -1

                if self.debugging:
                    if self.debug_3d_bool:
                        if self.debug_stack_bool:
                            self.debug[0] = itertools.chain(
                                self.debug[0], ("STACK:", ),
                                (item.__class__.__name__
                                 for item in reversed(self.stack)))

                        self.renderer.draw_string_3d(
                            self.me.location.tuple(), 2, 2,
                            "\n".join(self.debug[0]),
                            self.renderer.team_color(alt_color=True))

                        self.debug[0] = []

                    if self.debug_2d_bool:
                        if self.show_coords:
                            self.debug[1].insert(0,
                                                 str(self.me.location.int()))

                        if not self.is_clear(
                        ) and self.stack[0].__class__.__name__ in {
                                'Aerial', 'jump_shot', 'block_ground_shot'
                        }:
                            self.dbg_2d(
                                round(self.stack[0].intercept_time - self.time,
                                      4))

                        self.renderer.draw_string_2d(
                            20, 300, 2, 2, "\n".join(self.debug[1]),
                            self.renderer.team_color(alt_color=True))
                        self.debug[1] = []

                    if self.debug_ball_path:
                        if self.predictions['ball_struct'] is not None:
                            for i in range(
                                    0,
                                    self.predictions['ball_struct'].num_slices
                                    -
                                (self.predictions['ball_struct'].num_slices %
                                 self.debug_ball_path_precision) -
                                    self.debug_ball_path_precision,
                                    self.debug_ball_path_precision):
                                self.line(
                                    self.predictions['ball_struct'].slices[i].
                                    physics.location,
                                    self.predictions['ball_struct'].slices[
                                        i + self.debug_ball_path_precision].
                                    physics.location)
                else:
                    self.debug = [[], []]

            self.prev_time = self.time
            return SimpleControllerState(
            ) if self.disable_driving else self.controller
        except Exception:
            print(self.name)
            print_exc()
            return SimpleControllerState()

    def handle_match_comm(self, bot, team, msg):
        pass

    def test(self):
        pass

    def run(self):
        pass

    def handle_quick_chat(self, index, team, quick_chat):
        pass

    def init(self):
        pass
コード例 #2
0
ファイル: agent.py プロジェクト: rivques/VirxERLUShotFixing
class VirxERLU(StandaloneBot):
    # Massive thanks to ddthj/GoslingAgent (GitHub repo) for the basis of VirxERLU
    # VirxERLU on VirxEC Showcase -> https://virxerlu.virxcase.dev/
    # Wiki -> https://github.com/VirxEC/VirxERLU/wiki
    def initialize_agent(self):
        self.tournament = TOURNAMENT_MODE
        self.extra_debugging = EXTRA_DEBUGGING
        self.startup_time = time_ns()
        self.true_name = re.split(r' \(\d+\)$', self.name)[0]

        self.debug = [[], []]
        self.debugging = not self.tournament
        self.debug_lines = True
        self.debug_3d_bool = True
        self.debug_stack_bool = True
        self.debug_2d_bool = self.true_name == self.name
        self.show_coords = False
        self.debug_ball_path = False
        self.debug_ball_path_precision = 10
        self.disable_driving = False

        T = datetime.now()
        T = T.strftime("%Y-%m-%d %H;%M")

        self.traceback_file = (
            os.getcwd(),
            f"-traceback ({T}).txt"
        )

        if not self.tournament and self.extra_debugging:
            self.gui = Gui(self)
            self.print("Starting the GUI...")
            self.gui.start()

            if self.matchcomms_root is not None:
                self.match_comms = MatchComms(self)
                self.print("Starting the match communication handler...")
                self.match_comms.start()

        self.print("Building game information")

        match_settings = self.get_match_settings()
        mutators = match_settings.MutatorSettings()

        gravity = (
            Vector(z=-650),
            Vector(z=-325),
            Vector(z=-1137.5),
            Vector(z=-3250)
        )

        base_boost_accel = 991 + (2/3)

        boost_accel = (
            base_boost_accel,
            base_boost_accel * 1.5,
            base_boost_accel * 2,
            base_boost_accel * 10
        )

        boost_amount = (
            "default",
            "unlimited",
            "slow recharge",
            "fast recharge",
            "no boost"
        )

        game_mode = (
            "soccer",
            "hoops",
            "dropshot",
            "hockey",
            "rumble",
            "heatseeker"
        )

        ball_size = (
            92.75,
            69.25,
            139.5,
            239.75
        )

        self.gravity = gravity[mutators.GravityOption()]
        self.boost_accel = boost_accel[mutators.BoostStrengthOption()]
        self.boost_amount = boost_amount[mutators.BoostOption()]
        self.game_mode = game_mode[match_settings.GameMode()]
        self.ball_radius = ball_size[mutators.BallSizeOption()]

        self.friends = ()
        self.foes = ()
        self.me = car_object(self.index)
        self.ball_to_goal = -1

        self.ball = ball_object()
        self.game = game_object()

        self.boosts = ()

        self.friend_goal = goal_object(self.team)
        self.foe_goal = goal_object(not self.team)

        self.stack = []
        self.time = 0

        self.ready = False

        self.controller = SimpleControllerState()

        self.kickoff_flag = False
        self.kickoff_done = True
        self.shooting = False
        self.odd_tick = -1
        self.delta_time = 1 / 120

        self.future_ball_location_slice = 180
        self.balL_prediction_struct = None

    def retire(self):
        # Stop the currently running threads
        if not self.tournament and self.extra_debugging:
            self.gui.stop()

            if self.matchcomms_root is not None:
                self.match_comms.stop()

    def is_hot_reload_enabled(self):
        # The tkinter GUI isn't compatible with hot reloading
        # Use the Continue and Spawn option in the RLBotGUI instead
        return not self.extra_debugging

    def get_ready(self, packet):
        field_info = self.get_field_info()
        self.boosts = tuple(boost_object(i, boost.location, boost.is_full_boost) for i, boost in enumerate(field_info.boost_pads))
        self.refresh_player_lists(packet)
        self.ball.update(packet)

        self.init()

        load_time = (time_ns() - self.startup_time) / 1e+6
        print(f"{self.name}: Built game info in {load_time} milliseconds")

        self.ready = True

    def refresh_player_lists(self, packet):
        # Useful to keep separate from get_ready because humans can join/leave a match
        self.friends = tuple(car_object(i, packet) for i in range(packet.num_cars) if packet.game_cars[i].team is self.team and i != self.index)
        self.foes = tuple(car_object(i, packet) for i in range(packet.num_cars) if packet.game_cars[i].team != self.team)
        self.me = car_object(self.index, packet)

    def push(self, routine):
        self.stack.append(routine)

    def pop(self):
        return self.stack.pop()

    def line(self, start, end, color=None):
        if self.debugging and self.debug_lines:
            color = color if color is not None else self.renderer.grey()
            self.renderer.draw_line_3d(start.copy(), end.copy(), self.renderer.create_color(255, *color) if type(color) in {list, tuple} else color)

    def polyline(self, vectors, color=None):
        if self.debugging and self.debug_lines:
            color = color if color is not None else self.renderer.grey()
            vectors = tuple(vector.copy() for vector in vectors)
            self.renderer.draw_polyline_3d(vectors, self.renderer.create_color(255, *color) if type(color) in {list, tuple} else color)

    def sphere(self, location, radius, color=None):
        if self.debugging and self.debug_lines:
            x = Vector(x=radius)
            y = Vector(y=radius)
            z = Vector(z=radius)

            diag = Vector(1.0, 1.0, 1.0).scale(radius).x
            d1 = Vector(diag, diag, diag)
            d2 = Vector(-diag, diag, diag)
            d3 = Vector(-diag, -diag, diag)
            d4 = Vector(-diag, diag, -diag)

            self.line(location - x, location + x, color)
            self.line(location - y, location + y, color)
            self.line(location - z, location + z, color)
            self.line(location - d1, location + d1, color)
            self.line(location - d2, location + d2, color)
            self.line(location - d3, location + d3, color)
            self.line(location - d4, location + d4, color)

    def print(self, item):
        if not self.tournament:
            print(f"{self.name}: {item}")

    def dbg_3d(self, item):
        self.debug[0].append(str(item))

    def dbg_2d(self, item):
        self.debug[1].append(str(item))

    def clear(self):
        self.shooting = False
        self.stack = []

    def is_clear(self):
        return len(self.stack) < 1

    def preprocess(self, packet):
        if packet.num_cars != len(self.friends)+len(self.foes)+1:
            self.refresh_player_lists(packet)

        set(map(lambda car: car.update(packet), self.friends))
        set(map(lambda car: car.update(packet), self.foes))
        set(map(lambda pad: pad.update(packet), self.boosts))

        self.ball.update(packet)
        self.me.update(packet)
        self.game.update(self.team, packet)

        self.delta_time = self.game.time - self.time
        self.time = self.game.time
        self.gravity = self.game.gravity

        # When a new kickoff begins we empty the stack
        if not self.kickoff_flag and self.game.round_active and self.game.kickoff:
            self.kickoff_done = False
            self.clear()

        # Tells us when to go for kickoff
        self.kickoff_flag = self.game.round_active and self.game.kickoff

        self.ball_to_goal = self.friend_goal.location.flat_dist(self.ball.location)

        self.odd_tick += 1

        if self.odd_tick > 3:
            self.odd_tick = 0

        self.ball_prediction_struct = self.get_ball_prediction_struct()

        if self.tournament and self.matchcomms_root is not None:
            while 1:
                try:
                    msg = self.matchcomms.incoming_broadcast.get_nowait()
                except Exception:
                    break
                
                try:
                    self.handle_match_comm(msg)
                except Exception:
                    print_exc()

    def get_output(self, packet):
        try:
            # Reset controller
            self.controller.__init__(use_item=True)

            # Get ready, then preprocess
            if not self.ready:
                self.get_ready(packet)

            self.preprocess(packet)

            if self.me.demolished:
                if not self.is_clear():
                    self.clear()
            elif self.game.round_active:
                stack_routine_name = '' if self.is_clear() else self.stack[0].__class__.__name__
                if stack_routine_name in {'Aerial', 'jump_shot', 'double_jump', 'ground_shot', 'short_shot'}:
                    self.shooting = True
                else:
                    self.shooting = False

                try:
                    self.run()  # Run strategy code; This is a very expensive function to run
                except Exception:
                    t_file = os.path.join(self.traceback_file[0], self.name+self.traceback_file[1])
                    print(f"ERROR in {self.name}; see '{t_file}'")
                    print_exc(file=open(t_file, "a"))

                # run the routine on the end of the stack
                if not self.is_clear():
                    try:
                        r_name = self.stack[-1].__class__.__name__
                        self.stack[-1].run(self)
                    except Exception:
                        t_file = os.path.join(self.traceback_file[0], r_name+self.traceback_file[1])
                        print(f"ERROR in {self.name}'s {r_name} routine; see '{t_file}'")
                        print_exc(file=open(t_file, "a"))

                if self.debugging:
                    if self.debug_3d_bool:
                        if self.debug_stack_bool:
                            self.debug[0] = itertools.chain(self.debug[0], ("STACK:",), (item.__class__.__name__ for item in reversed(self.stack)))

                        self.renderer.draw_string_3d(tuple(self.me.location), 2, 2, "\n".join(self.debug[0]), self.renderer.team_color(alt_color=True))

                        self.debug[0] = []

                    if self.show_coords:
                        self.debug[1].insert(0, f"Hitbox: [{self.me.hitbox.length} {self.me.hitbox.width} {self.me.hitbox.height}]")
                        self.debug[1].insert(0, f"Location: {round(self.me.location)}")

                        car = self.me
                        center = car.local(car.hitbox.offset) + car.location
                        top = car.up * (car.hitbox.height / 2)
                        front = car.forward * (car.hitbox.length / 2)
                        right = car.right * (car.hitbox.width / 2)

                        bottom_front_right = center - top + front + right
                        bottom_front_left = center - top + front - right
                        bottom_back_right = center - top - front + right
                        bottom_back_left = center - top - front - right
                        top_front_right = center + top + front + right
                        top_front_left = center + top + front - right
                        top_back_right = center + top - front + right
                        top_back_left = center + top - front - right

                        hitbox_color = self.renderer.team_color(alt_color=True)

                        self.polyline((top_back_left, top_front_left, top_front_right, bottom_front_right, bottom_front_left, top_front_left), hitbox_color)
                        self.polyline((bottom_front_left, bottom_back_left, bottom_back_right, top_back_right, top_back_left, bottom_back_left), hitbox_color)
                        self.line(bottom_back_right, bottom_front_right, hitbox_color)
                        self.line(top_back_right, top_front_right, hitbox_color)

                    if self.debug_2d_bool:
                        if self.delta_time != 0:
                            self.debug[1].insert(0, f"TPS: {round(1 / self.delta_time)}")

                        if not self.is_clear() and self.stack[0].__class__.__name__ in {'Aerial', 'jump_shot', 'ground_shot', 'double_jump'}:
                            self.dbg_2d(round(self.stack[0].intercept_time - self.time, 4))

                        self.renderer.draw_string_2d(20, 300, 2, 2, "\n".join(self.debug[1]), self.renderer.team_color(alt_color=True))
                        self.debug[1] = []

                    if self.debug_ball_path and self.ball_prediction_struct is not None:
                        self.polyline(tuple(Vector(ball_slice.physics.location.x, ball_slice.physics.location.y, ball_slice.physics.location.z) for ball_slice in self.ball_prediction_struct.slices[::self.debug_ball_path_precision]))
                else:
                    self.debug = [[], []]

            return SimpleControllerState() if self.disable_driving else self.controller
        except Exception:
            t_file = os.path.join(self.traceback_file[0], "VirxERLU"+self.traceback_file[1])
            print(f"ERROR with VirxERLU in {self.name}; see '{t_file}' and please report the bug at 'https://github.com/VirxEC/VirxERLU/issues'")
            print_exc(file=open(t_file, "a"))
            return SimpleControllerState()

    def handle_match_comm(self, msg):
        pass

    def run(self):
        pass

    def handle_quick_chat(self, index, team, quick_chat):
        pass

    def init(self):
        pass
コード例 #3
0
class VirxERLU(BaseAgent):
    # Massive thanks to ddthj/GoslingAgent (GitHub repo) for the basis of VirxERLU
    # VirxERLU on VirxEC Showcase -> https://virxerlu.virxcase.dev/
    # Wiki -> https://github.com/VirxEC/VirxERLU/wiki
    def initialize_agent(self):
        self.tournament = False
        self.startup_time = time_ns()

        self.debug = [[], []]
        self.debugging = not self.tournament
        self.debug_lines = True
        self.debug_3d_bool = True
        self.debug_stack_bool = True
        self.debug_2d_bool = False
        self.show_coords = False
        self.debug_ball_path = False
        self.debug_ball_path_precision = 10
        self.disable_driving = False

        if not self.tournament:
            self.gui = Gui(self)
            self.print("Starting the GUI...")
            self.gui.start()

        self.match_comms = None

        self.print("Building game information")

        mutators = self.get_match_settings().MutatorSettings()

        gravity = (Vector(z=-650), Vector(z=-325), Vector(z=-1137.5),
                   Vector(z=-3250))

        base_boost_accel = 991 + (2 / 3)

        boost_accel = (base_boost_accel, base_boost_accel * 1.5,
                       base_boost_accel * 2, base_boost_accel * 10)

        boost_amount = ("default", "unlimited", "slow recharge",
                        "fast recharge", "no boost")

        self.gravity = gravity[mutators.GravityOption()]
        self.boost_accel = boost_accel[mutators.BoostStrengthOption()]
        self.boost_amount = boost_amount[mutators.BoostOption()]

        self.friends = ()
        self.foes = ()
        self.me = car_object(self.index)
        self.ball_to_goal = -1

        self.ball = ball_object()
        self.game = game_object()

        self.boosts = ()

        self.friend_goal = goal_object(self.team)
        self.foe_goal = goal_object(not self.team)

        self.stack = []
        self.time = 0

        self.ready = False

        self.controller = SimpleControllerState()

        self.kickoff_flag = False
        self.kickoff_done = True
        self.shooting = False
        self.best_shot_value = 92
        self.odd_tick = -1

        self.future_ball_location_slice = 180
        self.balL_prediction_struct = None

    def retire(self):
        # Stop the currently running threads
        if not self.tournament:
            self.gui.stop()

        if self.match_comms is not None:
            self.match_comms.stop()

    @staticmethod
    def is_hot_reload_enabled():
        # The tkinter GUI isn't compatible with hot reloading
        # Use the Continue and Spawn option in the GUI instead
        return False

    def get_ready(self, packet):
        field_info = self.get_field_info()
        self.boosts = tuple(
            boost_object(i, boost.location, boost.is_full_boost)
            for i, boost in enumerate(field_info.boost_pads))
        self.refresh_player_lists(packet)
        self.ball.update(packet)

        self.best_shot_value = math.floor((92.75 + self.me.hitbox.width / 2))
        self.print(f"Best shot value: {self.best_shot_value}")

        self.init()

        self.ready = True

        load_time = (time_ns() - self.startup_time) / 1e+6
        print(f"{self.name}: Built game info in {load_time} milliseconds")

    def refresh_player_lists(self, packet):
        # Useful to keep separate from get_ready because humans can join/leave a match
        self.friends = tuple(
            car_object(i, packet) for i in range(packet.num_cars)
            if packet.game_cars[i].team is self.team and i != self.index)
        self.foes = tuple(
            car_object(i, packet) for i in range(packet.num_cars)
            if packet.game_cars[i].team != self.team)
        self.me = car_object(self.index, packet)

        if len(self.friends) > 0 and self.match_comms is None:
            self.match_comms = MatchComms(self)
            self.print("Starting the match communication handler...")
            self.match_comms.start()

    def push(self, routine):
        self.stack.append(routine)

    def pop(self):
        return self.stack.pop()

    def line(self, start, end, color=None):
        if self.debugging and self.debug_lines:
            color = color if color is not None else self.renderer.grey()
            self.renderer.draw_line_3d(
                start.copy(), end.copy(),
                self.renderer.create_color(255, *color)
                if type(color) in {list, tuple} else color)

    def polyline(self, vectors, color=None):
        if self.debugging and self.debug_lines:
            color = color if color is not None else self.renderer.grey()
            vectors = tuple(vector.copy() for vector in vectors)
            self.renderer.draw_polyline_3d(
                vectors,
                self.renderer.create_color(255, *color)
                if type(color) in {list, tuple} else color)

    def print(self, item):
        if not self.tournament:
            print(f"{self.name}: {item}")

    def dbg_3d(self, item):
        self.debug[0].append(str(item))

    def dbg_2d(self, item):
        self.debug[1].append(str(item))

    def clear(self):
        self.shooting = False
        self.stack = []

    def is_clear(self):
        return len(self.stack) < 1

    def preprocess(self, packet):
        if packet.num_cars != len(self.friends) + len(self.foes) + 1:
            self.refresh_player_lists(packet)

        set(map(lambda car: car.update(packet), self.friends))
        set(map(lambda car: car.update(packet), self.foes))
        set(map(lambda pad: pad.update(packet), self.boosts))

        self.ball.update(packet)
        self.me.update(packet)
        self.game.update(self.team, packet)
        self.time = self.game.time
        self.gravity = self.game.gravity

        # When a new kickoff begins we empty the stack
        if not self.kickoff_flag and self.game.round_active and self.game.kickoff:
            self.kickoff_done = False
            self.clear()

        # Tells us when to go for kickoff
        self.kickoff_flag = self.game.round_active and self.game.kickoff
        self.ball_to_goal = self.friend_goal.location.flat_dist(
            self.ball.location)

        self.ball_prediction_struct = self.get_ball_prediction_struct()

        self.odd_tick += 1

        if self.odd_tick > 3:
            self.odd_tick = 0

    def get_output(self, packet):
        try:
            # Reset controller
            self.controller.__init__(use_item=True)

            # Get ready, then preprocess
            if not self.ready:
                self.get_ready(packet)

            self.preprocess(packet)

            if self.me.demolished:
                if not self.is_clear():
                    self.clear()
            elif self.game.round_active:
                try:
                    self.run(
                    )  # Run strategy code; This is a very expensive function to run
                except Exception:
                    print(self.name)
                    print_exc()

                if self.debugging:
                    if self.debug_3d_bool:
                        if self.debug_stack_bool:
                            self.debug[0] = itertools.chain(
                                self.debug[0], ("STACK:", ),
                                (item.__class__.__name__
                                 for item in reversed(self.stack)))

                        self.renderer.draw_string_3d(
                            self.me.location.tuple(), 2, 2,
                            "\n".join(self.debug[0]),
                            self.renderer.team_color(alt_color=True))

                        self.debug[0] = []

                    if self.debug_2d_bool:
                        if self.show_coords:
                            self.debug[1].insert(0,
                                                 str(self.me.location.int()))

                        if not self.is_clear(
                        ) and self.stack[0].__class__.__name__ in {
                                'Aerial', 'jump_shot', 'block_ground_shot',
                                'double_jump'
                        }:
                            self.dbg_2d(
                                round(self.stack[0].intercept_time - self.time,
                                      4))

                        self.renderer.draw_string_2d(
                            20, 300, 2, 2, "\n".join(self.debug[1]),
                            self.renderer.team_color(alt_color=True))
                        self.debug[1] = []

                    if self.debug_ball_path and self.ball_prediction_struct is not None:
                        self.polyline(
                            tuple(
                                Vector(ball_slice.physics.location.x,
                                       ball_slice.physics.location.y,
                                       ball_slice.physics.location.z)
                                for ball_slice in self.ball_prediction_struct.
                                slices[::self.debug_ball_path_precision]))
                else:
                    self.debug = [[], []]

                # run the routine on the end of the stack
                if not self.is_clear():
                    self.stack[-1].run(self)

                if self.is_clear() or self.stack[0].__class__.__name__ not in {
                        'Aerial', 'jump_shot', 'block_ground_shot',
                        'double_jump'
                }:
                    self.shooting = False

            return SimpleControllerState(
            ) if self.disable_driving else self.controller
        except Exception:
            print(self.name)
            print_exc()
            return SimpleControllerState()

    def handle_match_comm(self, msg):
        pass

    def run(self):
        pass

    def handle_quick_chat(self, index, team, quick_chat):
        pass

    def init(self):
        pass