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
If a score list is less than this, log it to info.
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.
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)
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.
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)
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.
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)
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.
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)
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.
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.
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.
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.
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().
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.