pacai.util.bin

  1import argparse
  2import logging
  3import typing
  4
  5import pacai.core.agentaction
  6import pacai.core.agentinfo
  7import pacai.core.board
  8import pacai.core.game
  9import pacai.core.log
 10import pacai.core.ui
 11import pacai.util.alias
 12
 13SCORE_LIST_MAX_INFO_LENGTH: int = 50
 14""" If a score list is less than this, log it to info. """
 15
 16@typing.runtime_checkable
 17class SetCLIArgs(typing.Protocol):
 18    """
 19    A function that can be used to modify a CLI parser before use.
 20    """
 21
 22    def __call__(self,
 23            parser: argparse.ArgumentParser,
 24            ) -> argparse.ArgumentParser:
 25        """
 26        Modify the CLI parser before use.
 27        Any changes may be made, including adding arguments.
 28        The modified (or new) parser should be returned.
 29        """
 30
 31@typing.runtime_checkable
 32class GetAdditionalOptions(typing.Protocol):
 33    """
 34    A function that can be used to get additional initialization options.
 35    """
 36
 37    def __call__(self,
 38            args: argparse.Namespace,
 39            ) -> dict[str, typing.Any]:
 40        """
 41        Get additional/custom initialization options.
 42        """
 43
 44@typing.runtime_checkable
 45class InitFromArgs(typing.Protocol):
 46    """
 47    A function that can be used to initialize components from CLI args.
 48    """
 49
 50    def __call__(self,
 51            args: argparse.Namespace,
 52            ) -> tuple[dict[int, pacai.core.agentinfo.AgentInfo], list[int], dict[str, typing.Any]]:
 53        """
 54        Initialize components from arguments and return
 55        the base agent infos, a list of agents to remove from the board, as well as any board options.
 56        See base_init_from_args() for the default implementation.
 57        """
 58
 59@typing.runtime_checkable
 60class LogResults(typing.Protocol):
 61    """
 62    A function that can be used to log game results.
 63    """
 64
 65    def __call__(self,
 66            results: list[pacai.core.game.GameResult],
 67            winning_agent_indexes: set[int],
 68            prefix: str = '',
 69            ) -> None:
 70        """
 71        Log the result of running several games.
 72        """
 73
 74def base_init_from_args(args: argparse.Namespace) -> tuple[dict[int, pacai.core.agentinfo.AgentInfo], list[int], dict[str, typing.Any]]:
 75    """
 76    Take in args from a parser that was passed to set_cli_args(),
 77    and initialize the proper components.
 78    """
 79
 80    base_agent_infos: dict[int, pacai.core.agentinfo.AgentInfo] = {}
 81
 82    # Create base arguments for all possible agents.
 83    for i in range(pacai.core.board.MAX_AGENTS):
 84        base_agent_infos[i] = pacai.core.agentinfo.AgentInfo(name = pacai.util.alias.AGENT_RANDOM.long)
 85
 86    return base_agent_infos, [], {}
 87
 88def base_log_results(results: list[pacai.core.game.GameResult], winning_agent_indexes: set[int], prefix: str = '') -> None:
 89    """
 90    Log the result of running several games.
 91    """
 92
 93    scores = [result.score for result in results]
 94    wins = [(not winning_agent_indexes.isdisjoint(set(result.winning_agent_indexes))) for result in results]
 95    win_rate = wins.count(True) / float(len(wins))
 96    turn_counts = [len(result.history) for result in results]
 97
 98    # Avoid logging long lists (which can be a bit slow in Python's logging module).
 99    log_lists_to_info = (len(results) < SCORE_LIST_MAX_INFO_LENGTH)
100    log_lists_to_debug = (logging.getLogger().getEffectiveLevel() <= logging.DEBUG)
101
102    joined_scores = ''
103    joined_record = ''
104    joined_turn_counts = ''
105
106    if (log_lists_to_info or log_lists_to_debug):
107        joined_scores = ', '.join([str(score) for score in scores])
108        joined_record = ', '.join([['Loss', 'Win'][int(win)] for win in wins])
109        joined_turn_counts = ', '.join([str(turn_count) for turn_count in turn_counts])
110
111    logging.info('%sAverage Score: %s', prefix, sum(scores) / float(len(results)))
112
113    if (log_lists_to_info):
114        logging.info('%sScores:        %s', prefix, joined_scores)
115    elif (log_lists_to_debug):
116        logging.debug('%sScores:        %s', prefix, joined_scores)
117
118    logging.info('%sWin Rate:      %d / %d (%0.2f)', prefix, wins.count(True), len(wins), win_rate)
119
120    if (log_lists_to_info):
121        logging.info('%sRecord:        %s', prefix, joined_record)
122    elif (log_lists_to_debug):
123        logging.debug('%sRecord:        %s', prefix, joined_record)
124
125    logging.info('%sAverage Turns: %s', prefix, sum(turn_counts) / float(len(results)))
126
127    if (log_lists_to_info):
128        logging.info('%sTurn Counts:   %s', prefix, joined_turn_counts)
129    elif (log_lists_to_debug):
130        logging.debug('%sTurn Counts:   %s', prefix, joined_turn_counts)
131
132def run_main(
133        description: str,
134        default_board: str,
135        game_class: typing.Type[pacai.core.game.Game],
136        custom_set_cli_args: SetCLIArgs | None = None,
137        get_additional_ui_options: GetAdditionalOptions | None = None,
138        custom_init_from_args: InitFromArgs = base_init_from_args,
139        winning_agent_indexes: set[int] | None = None,
140        log_results: LogResults | None = base_log_results,
141        argv: list[str] | None = None,
142        ) -> tuple[list[pacai.core.game.GameResult], list[pacai.core.game.GameResult]]:
143    """
144    A full main function to prep and run games.
145
146    Will return the results of any training games followed by the results of any non-training games.
147    """
148
149    # Create a CLI parser.
150    parser = get_parser(description, default_board, custom_set_cli_args = custom_set_cli_args)
151
152    # Parse the CLI args.
153    args = parse_args(parser, game_class,
154            get_additional_ui_options = get_additional_ui_options, custom_init_from_args = custom_init_from_args,
155            argv = argv)
156
157    return run_games(args, winning_agent_indexes = winning_agent_indexes, log_results = log_results)
158
159def get_parser(
160        description: str,
161        default_board: str,
162        custom_set_cli_args: SetCLIArgs | None = None,
163        ) -> argparse.ArgumentParser:
164    """ Get a parser with all the options. """
165
166    parser = argparse.ArgumentParser(description = description)
167
168    # Add logging arguments.
169    parser = pacai.core.log.set_cli_args(parser)
170
171    # Add UI arguments.
172    parser = pacai.core.ui.set_cli_args(parser)
173
174    # Add game arguments.
175    parser = pacai.core.game.set_cli_args(parser, default_board = default_board)
176
177    # Add custom options.
178    if (custom_set_cli_args is not None):
179        parser = custom_set_cli_args(parser)
180
181    return parser
182
183def parse_args(
184        parser: argparse.ArgumentParser,
185        game_class: typing.Type[pacai.core.game.Game],
186        get_additional_ui_options: GetAdditionalOptions | None = None,
187        custom_init_from_args: InitFromArgs = base_init_from_args,
188        argv: list[str] | None = None,
189        ) -> argparse.Namespace:
190    """ Parse the args from the parser returned by get_parser(). """
191
192    args = parser.parse_args(args = argv)
193
194    # Parse logging arguments.
195    args = pacai.core.log.init_from_args(parser, args)
196
197    # Parse custom options.
198    base_agent_infos, remove_agent_indexes, board_options = custom_init_from_args(args)
199
200    # Parse UI arguments.
201
202    additional_ui_args = {}
203    if (get_additional_ui_options is not None):
204        additional_ui_args = get_additional_ui_options(args)
205
206    null_out_uis = args.num_training
207    if (args.show_training_ui):
208        null_out_uis = 0
209
210    args = pacai.core.ui.init_from_args(args, null_out_uis = null_out_uis, additional_args = additional_ui_args)
211
212    # Parse game arguments.
213
214    args = pacai.core.game.init_from_args(args, game_class,
215            base_agent_infos = base_agent_infos,
216            remove_agent_indexes = remove_agent_indexes,
217            board_options = board_options)
218
219    return args
220
221def run_games(
222        args: argparse.Namespace,
223        winning_agent_indexes: set[int] | None = None,
224        log_results: LogResults | None = base_log_results,
225        ) -> tuple[list[pacai.core.game.GameResult], list[pacai.core.game.GameResult]]:
226    """
227    Run one or more standard games using pre-parsed arguments.
228    The arguments are expected to have `_games` and `_uis`,
229    as if `pacai.core.ui.init_from_args()` and `pacai.core.game.init_from_args()` have been called.
230
231    Will return the results of any training games followed by the results of any non-training games.
232    """
233
234    if (winning_agent_indexes is None):
235        winning_agent_indexes = set()
236
237    training_infos: dict[int, dict[str, typing.Any]] = {}
238    training_results = []
239
240    # Run training games/epochs.
241    for i in range(args.num_training):
242        game = args._games[i]
243        ui = args._uis[i]
244
245        for (agent_index, agent_info) in game.game_info.agent_infos.items():
246            # Set information gained from the previous training epochs.
247            data = training_infos.get(agent_index, {}).copy()
248
249            # Tell agents we are training.
250            data['training'] = True
251            data['training_epoch'] = i
252
253            agent_info.extra_arguments.update(data)
254
255        result = game.run(ui)
256        training_results.append(result)
257
258        for (agent_index, agent_record) in result.agent_complete_records.items():
259            if (agent_record.agent_action is not None):
260                training_infos[agent_index] = agent_record.agent_action.training_info
261
262    results = []
263
264    for i in range(args.num_games):
265        game = args._games[i + args.num_training]
266        ui = args._uis[i + args.num_training]
267
268        # Set any information gained from training.
269        for (agent_index, training_info) in training_infos.items():
270            game.game_info.agent_infos[agent_index].extra_arguments.update(training_info)
271
272        result = game.run(ui)
273        results.append(result)
274
275    if (len(training_results) > 0):
276        if (log_results is not None):
277            log_results(training_results, winning_agent_indexes, prefix = 'Training ')
278
279    if (len(results) > 0):
280        if (log_results is not None):
281            log_results(results, winning_agent_indexes)
282
283    return training_results, results
SCORE_LIST_MAX_INFO_LENGTH: int = 50

If a score list is less than this, log it to info.

@typing.runtime_checkable
class SetCLIArgs(typing.Protocol):
17@typing.runtime_checkable
18class SetCLIArgs(typing.Protocol):
19    """
20    A function that can be used to modify a CLI parser before use.
21    """
22
23    def __call__(self,
24            parser: argparse.ArgumentParser,
25            ) -> argparse.ArgumentParser:
26        """
27        Modify the CLI parser before use.
28        Any changes may be made, including adding arguments.
29        The modified (or new) parser should be returned.
30        """

A function that can be used to modify a CLI parser before use.

SetCLIArgs(*args, **kwargs)
1953def _no_init_or_replace_init(self, *args, **kwargs):
1954    cls = type(self)
1955
1956    if cls._is_protocol:
1957        raise TypeError('Protocols cannot be instantiated')
1958
1959    # Already using a custom `__init__`. No need to calculate correct
1960    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1961    if cls.__init__ is not _no_init_or_replace_init:
1962        return
1963
1964    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1965    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1966    # searches for a proper new `__init__` in the MRO. The new `__init__`
1967    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1968    # instantiation of the protocol subclass will thus use the new
1969    # `__init__` and no longer call `_no_init_or_replace_init`.
1970    for base in cls.__mro__:
1971        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1972        if init is not _no_init_or_replace_init:
1973            cls.__init__ = init
1974            break
1975    else:
1976        # should not happen
1977        cls.__init__ = object.__init__
1978
1979    cls.__init__(self, *args, **kwargs)
@typing.runtime_checkable
class GetAdditionalOptions(typing.Protocol):
32@typing.runtime_checkable
33class GetAdditionalOptions(typing.Protocol):
34    """
35    A function that can be used to get additional initialization options.
36    """
37
38    def __call__(self,
39            args: argparse.Namespace,
40            ) -> dict[str, typing.Any]:
41        """
42        Get additional/custom initialization options.
43        """

A function that can be used to get additional initialization options.

GetAdditionalOptions(*args, **kwargs)
1953def _no_init_or_replace_init(self, *args, **kwargs):
1954    cls = type(self)
1955
1956    if cls._is_protocol:
1957        raise TypeError('Protocols cannot be instantiated')
1958
1959    # Already using a custom `__init__`. No need to calculate correct
1960    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1961    if cls.__init__ is not _no_init_or_replace_init:
1962        return
1963
1964    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1965    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1966    # searches for a proper new `__init__` in the MRO. The new `__init__`
1967    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1968    # instantiation of the protocol subclass will thus use the new
1969    # `__init__` and no longer call `_no_init_or_replace_init`.
1970    for base in cls.__mro__:
1971        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1972        if init is not _no_init_or_replace_init:
1973            cls.__init__ = init
1974            break
1975    else:
1976        # should not happen
1977        cls.__init__ = object.__init__
1978
1979    cls.__init__(self, *args, **kwargs)
@typing.runtime_checkable
class InitFromArgs(typing.Protocol):
45@typing.runtime_checkable
46class InitFromArgs(typing.Protocol):
47    """
48    A function that can be used to initialize components from CLI args.
49    """
50
51    def __call__(self,
52            args: argparse.Namespace,
53            ) -> tuple[dict[int, pacai.core.agentinfo.AgentInfo], list[int], dict[str, typing.Any]]:
54        """
55        Initialize components from arguments and return
56        the base agent infos, a list of agents to remove from the board, as well as any board options.
57        See base_init_from_args() for the default implementation.
58        """

A function that can be used to initialize components from CLI args.

InitFromArgs(*args, **kwargs)
1953def _no_init_or_replace_init(self, *args, **kwargs):
1954    cls = type(self)
1955
1956    if cls._is_protocol:
1957        raise TypeError('Protocols cannot be instantiated')
1958
1959    # Already using a custom `__init__`. No need to calculate correct
1960    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1961    if cls.__init__ is not _no_init_or_replace_init:
1962        return
1963
1964    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1965    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1966    # searches for a proper new `__init__` in the MRO. The new `__init__`
1967    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1968    # instantiation of the protocol subclass will thus use the new
1969    # `__init__` and no longer call `_no_init_or_replace_init`.
1970    for base in cls.__mro__:
1971        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1972        if init is not _no_init_or_replace_init:
1973            cls.__init__ = init
1974            break
1975    else:
1976        # should not happen
1977        cls.__init__ = object.__init__
1978
1979    cls.__init__(self, *args, **kwargs)
@typing.runtime_checkable
class LogResults(typing.Protocol):
60@typing.runtime_checkable
61class LogResults(typing.Protocol):
62    """
63    A function that can be used to log game results.
64    """
65
66    def __call__(self,
67            results: list[pacai.core.game.GameResult],
68            winning_agent_indexes: set[int],
69            prefix: str = '',
70            ) -> None:
71        """
72        Log the result of running several games.
73        """

A function that can be used to log game results.

LogResults(*args, **kwargs)
1953def _no_init_or_replace_init(self, *args, **kwargs):
1954    cls = type(self)
1955
1956    if cls._is_protocol:
1957        raise TypeError('Protocols cannot be instantiated')
1958
1959    # Already using a custom `__init__`. No need to calculate correct
1960    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1961    if cls.__init__ is not _no_init_or_replace_init:
1962        return
1963
1964    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1965    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1966    # searches for a proper new `__init__` in the MRO. The new `__init__`
1967    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1968    # instantiation of the protocol subclass will thus use the new
1969    # `__init__` and no longer call `_no_init_or_replace_init`.
1970    for base in cls.__mro__:
1971        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1972        if init is not _no_init_or_replace_init:
1973            cls.__init__ = init
1974            break
1975    else:
1976        # should not happen
1977        cls.__init__ = object.__init__
1978
1979    cls.__init__(self, *args, **kwargs)
def base_init_from_args( args: argparse.Namespace) -> tuple[dict[int, pacai.core.agentinfo.AgentInfo], list[int], dict[str, typing.Any]]:
75def base_init_from_args(args: argparse.Namespace) -> tuple[dict[int, pacai.core.agentinfo.AgentInfo], list[int], dict[str, typing.Any]]:
76    """
77    Take in args from a parser that was passed to set_cli_args(),
78    and initialize the proper components.
79    """
80
81    base_agent_infos: dict[int, pacai.core.agentinfo.AgentInfo] = {}
82
83    # Create base arguments for all possible agents.
84    for i in range(pacai.core.board.MAX_AGENTS):
85        base_agent_infos[i] = pacai.core.agentinfo.AgentInfo(name = pacai.util.alias.AGENT_RANDOM.long)
86
87    return base_agent_infos, [], {}

Take in args from a parser that was passed to set_cli_args(), and initialize the proper components.

def base_log_results( results: list[pacai.core.game.GameResult], winning_agent_indexes: set[int], prefix: str = '') -> None:
 89def base_log_results(results: list[pacai.core.game.GameResult], winning_agent_indexes: set[int], prefix: str = '') -> None:
 90    """
 91    Log the result of running several games.
 92    """
 93
 94    scores = [result.score for result in results]
 95    wins = [(not winning_agent_indexes.isdisjoint(set(result.winning_agent_indexes))) for result in results]
 96    win_rate = wins.count(True) / float(len(wins))
 97    turn_counts = [len(result.history) for result in results]
 98
 99    # Avoid logging long lists (which can be a bit slow in Python's logging module).
100    log_lists_to_info = (len(results) < SCORE_LIST_MAX_INFO_LENGTH)
101    log_lists_to_debug = (logging.getLogger().getEffectiveLevel() <= logging.DEBUG)
102
103    joined_scores = ''
104    joined_record = ''
105    joined_turn_counts = ''
106
107    if (log_lists_to_info or log_lists_to_debug):
108        joined_scores = ', '.join([str(score) for score in scores])
109        joined_record = ', '.join([['Loss', 'Win'][int(win)] for win in wins])
110        joined_turn_counts = ', '.join([str(turn_count) for turn_count in turn_counts])
111
112    logging.info('%sAverage Score: %s', prefix, sum(scores) / float(len(results)))
113
114    if (log_lists_to_info):
115        logging.info('%sScores:        %s', prefix, joined_scores)
116    elif (log_lists_to_debug):
117        logging.debug('%sScores:        %s', prefix, joined_scores)
118
119    logging.info('%sWin Rate:      %d / %d (%0.2f)', prefix, wins.count(True), len(wins), win_rate)
120
121    if (log_lists_to_info):
122        logging.info('%sRecord:        %s', prefix, joined_record)
123    elif (log_lists_to_debug):
124        logging.debug('%sRecord:        %s', prefix, joined_record)
125
126    logging.info('%sAverage Turns: %s', prefix, sum(turn_counts) / float(len(results)))
127
128    if (log_lists_to_info):
129        logging.info('%sTurn Counts:   %s', prefix, joined_turn_counts)
130    elif (log_lists_to_debug):
131        logging.debug('%sTurn Counts:   %s', prefix, joined_turn_counts)

Log the result of running several games.

def run_main( description: str, default_board: str, game_class: Type[pacai.core.game.Game], custom_set_cli_args: SetCLIArgs | None = None, get_additional_ui_options: GetAdditionalOptions | None = None, custom_init_from_args: InitFromArgs = <function base_init_from_args>, winning_agent_indexes: set[int] | None = None, log_results: LogResults | None = <function base_log_results>, argv: list[str] | None = None) -> tuple[list[pacai.core.game.GameResult], list[pacai.core.game.GameResult]]:
133def run_main(
134        description: str,
135        default_board: str,
136        game_class: typing.Type[pacai.core.game.Game],
137        custom_set_cli_args: SetCLIArgs | None = None,
138        get_additional_ui_options: GetAdditionalOptions | None = None,
139        custom_init_from_args: InitFromArgs = base_init_from_args,
140        winning_agent_indexes: set[int] | None = None,
141        log_results: LogResults | None = base_log_results,
142        argv: list[str] | None = None,
143        ) -> tuple[list[pacai.core.game.GameResult], list[pacai.core.game.GameResult]]:
144    """
145    A full main function to prep and run games.
146
147    Will return the results of any training games followed by the results of any non-training games.
148    """
149
150    # Create a CLI parser.
151    parser = get_parser(description, default_board, custom_set_cli_args = custom_set_cli_args)
152
153    # Parse the CLI args.
154    args = parse_args(parser, game_class,
155            get_additional_ui_options = get_additional_ui_options, custom_init_from_args = custom_init_from_args,
156            argv = argv)
157
158    return run_games(args, winning_agent_indexes = winning_agent_indexes, log_results = log_results)

A full main function to prep and run games.

Will return the results of any training games followed by the results of any non-training games.

def get_parser( description: str, default_board: str, custom_set_cli_args: SetCLIArgs | None = None) -> argparse.ArgumentParser:
160def get_parser(
161        description: str,
162        default_board: str,
163        custom_set_cli_args: SetCLIArgs | None = None,
164        ) -> argparse.ArgumentParser:
165    """ Get a parser with all the options. """
166
167    parser = argparse.ArgumentParser(description = description)
168
169    # Add logging arguments.
170    parser = pacai.core.log.set_cli_args(parser)
171
172    # Add UI arguments.
173    parser = pacai.core.ui.set_cli_args(parser)
174
175    # Add game arguments.
176    parser = pacai.core.game.set_cli_args(parser, default_board = default_board)
177
178    # Add custom options.
179    if (custom_set_cli_args is not None):
180        parser = custom_set_cli_args(parser)
181
182    return parser

Get a parser with all the options.

def parse_args( parser: argparse.ArgumentParser, game_class: Type[pacai.core.game.Game], get_additional_ui_options: GetAdditionalOptions | None = None, custom_init_from_args: InitFromArgs = <function base_init_from_args>, argv: list[str] | None = None) -> argparse.Namespace:
184def parse_args(
185        parser: argparse.ArgumentParser,
186        game_class: typing.Type[pacai.core.game.Game],
187        get_additional_ui_options: GetAdditionalOptions | None = None,
188        custom_init_from_args: InitFromArgs = base_init_from_args,
189        argv: list[str] | None = None,
190        ) -> argparse.Namespace:
191    """ Parse the args from the parser returned by get_parser(). """
192
193    args = parser.parse_args(args = argv)
194
195    # Parse logging arguments.
196    args = pacai.core.log.init_from_args(parser, args)
197
198    # Parse custom options.
199    base_agent_infos, remove_agent_indexes, board_options = custom_init_from_args(args)
200
201    # Parse UI arguments.
202
203    additional_ui_args = {}
204    if (get_additional_ui_options is not None):
205        additional_ui_args = get_additional_ui_options(args)
206
207    null_out_uis = args.num_training
208    if (args.show_training_ui):
209        null_out_uis = 0
210
211    args = pacai.core.ui.init_from_args(args, null_out_uis = null_out_uis, additional_args = additional_ui_args)
212
213    # Parse game arguments.
214
215    args = pacai.core.game.init_from_args(args, game_class,
216            base_agent_infos = base_agent_infos,
217            remove_agent_indexes = remove_agent_indexes,
218            board_options = board_options)
219
220    return args

Parse the args from the parser returned by get_parser().

def run_games( args: argparse.Namespace, winning_agent_indexes: set[int] | None = None, log_results: LogResults | None = <function base_log_results>) -> tuple[list[pacai.core.game.GameResult], list[pacai.core.game.GameResult]]:
222def run_games(
223        args: argparse.Namespace,
224        winning_agent_indexes: set[int] | None = None,
225        log_results: LogResults | None = base_log_results,
226        ) -> tuple[list[pacai.core.game.GameResult], list[pacai.core.game.GameResult]]:
227    """
228    Run one or more standard games using pre-parsed arguments.
229    The arguments are expected to have `_games` and `_uis`,
230    as if `pacai.core.ui.init_from_args()` and `pacai.core.game.init_from_args()` have been called.
231
232    Will return the results of any training games followed by the results of any non-training games.
233    """
234
235    if (winning_agent_indexes is None):
236        winning_agent_indexes = set()
237
238    training_infos: dict[int, dict[str, typing.Any]] = {}
239    training_results = []
240
241    # Run training games/epochs.
242    for i in range(args.num_training):
243        game = args._games[i]
244        ui = args._uis[i]
245
246        for (agent_index, agent_info) in game.game_info.agent_infos.items():
247            # Set information gained from the previous training epochs.
248            data = training_infos.get(agent_index, {}).copy()
249
250            # Tell agents we are training.
251            data['training'] = True
252            data['training_epoch'] = i
253
254            agent_info.extra_arguments.update(data)
255
256        result = game.run(ui)
257        training_results.append(result)
258
259        for (agent_index, agent_record) in result.agent_complete_records.items():
260            if (agent_record.agent_action is not None):
261                training_infos[agent_index] = agent_record.agent_action.training_info
262
263    results = []
264
265    for i in range(args.num_games):
266        game = args._games[i + args.num_training]
267        ui = args._uis[i + args.num_training]
268
269        # Set any information gained from training.
270        for (agent_index, training_info) in training_infos.items():
271            game.game_info.agent_infos[agent_index].extra_arguments.update(training_info)
272
273        result = game.run(ui)
274        results.append(result)
275
276    if (len(training_results) > 0):
277        if (log_results is not None):
278            log_results(training_results, winning_agent_indexes, prefix = 'Training ')
279
280    if (len(results) > 0):
281        if (log_results is not None):
282            log_results(results, winning_agent_indexes)
283
284    return training_results, results

Run one or more standard games using pre-parsed arguments. The arguments are expected to have _games and _uis, as if pacai.core.ui.init_from_args() and pacai.core.game.init_from_args() have been called.

Will return the results of any training games followed by the results of any non-training games.