async def initialize_first_step() -> Optional[Result]: nonlocal gs ai._initialize_variables() game_data = await client.get_game_data() game_info = await client.get_game_info() ping_response = await client.ping() # This game_data will become self.game_data in botAI ai._prepare_start( client, player_id, game_info, game_data, realtime=realtime, base_build=ping_response.ping.base_build ) state = await client.observation() # check game result every time we get the observation if client._game_result: await ai.on_end(client._game_result[player_id]) return client._game_result[player_id] gs = GameState(state.observation) proto_game_info = await client._execute(game_info=sc_pb.RequestGameInfo()) try: ai._prepare_step(gs, proto_game_info) await ai.on_before_start() ai._prepare_first_step() await ai.on_start() # TODO Catching too general exception Exception (broad-except) # pylint: disable=W0703 except Exception: logger.exception("AI on_start threw an error") logger.error("Resigning due to previous error") await ai.on_end(Result.Defeat) return Result.Defeat
def __init__( self, raw_game_data: Any, raw_game_info: Any, raw_observation: Any ) -> None: """ Set up variables for use within Creeper. Args: raw_game_data (Any): self.game_data from main instance raw_game_info (Any): self.game_info from main instance raw_observation (Any): self.game_state from main instance Returns: None """ self.bot = BotAI() game_data = GameData(raw_game_data.data) game_info = GameInfo(raw_game_info.game_info) game_state = GameState(raw_observation) self.bot._initialize_variables() self.bot._prepare_start( client=None, player_id=1, game_info=game_info, game_data=game_data ) self.bot._prepare_step(state=game_state, proto_game_info=raw_game_info) self.pathing = PathManager(raw_game_data, raw_game_info, raw_observation) self.pf = PathFind(self.pathing.map_grid)
def import_bot_instance( raw_game_data: Response, raw_game_info: Response, raw_observation: ResponseObservation, ) -> BotAI: """ import_bot_instance DocString """ bot = BotAI() game_data = GameData(raw_game_data.data) game_info = GameInfo(raw_game_info.game_info) game_state = GameState(raw_observation) # noinspection PyProtectedMember bot._initialize_variables() # noinspection PyProtectedMember bot._prepare_start(client=None, player_id=1, game_info=game_info, game_data=game_data) # noinspection PyProtectedMember bot._prepare_first_step() # noinspection PyProtectedMember bot._prepare_step(state=game_state, proto_game_info=raw_game_info) # noinspection PyProtectedMember bot._find_expansion_locations() return bot
def __init__( self, raw_game_data: Any, raw_game_info: Any, raw_observation: Any ) -> None: """ Set up variables for use within PathMangager. Args: raw_game_data (Any): self.game_data from main instance raw_game_info (Any): self.game_info from main instance raw_observation (Any): self.game_state from main instance Returns: None """ self.bot = BotAI() game_data = GameData(raw_game_data.data) game_info = GameInfo(raw_game_info.game_info) game_state = GameState(raw_observation) self.bot._initialize_variables() self.bot._prepare_start( client=None, player_id=1, game_info=game_info, game_data=game_data ) self.bot._prepare_step(state=game_state, proto_game_info=raw_game_info) map_name = game_info.map_name self.pathing_dict: Dict[int, PathDict] = {} map_width = game_info.map_size.width map_height = game_info.map_size.height raw_grid = np.zeros((map_width, map_height)) for x in range(map_width): for y in range(map_height): pos = Point2((x, y)) raw_grid[x][y] = game_info.pathing_grid[pos] self.map_grid = np.rot90(raw_grid.astype(int)) np.save(f"map_grids/{map_name}_grid", self.map_grid) self.pf = PathFind(self.map_grid)
def _ai_preparation(self, bot_ai, player_id, client): game_data = self._loop_run(client.get_game_data()) game_info = self._loop_run(client.get_game_info()) state = self._loop_run(client.observation()) game_state = GameState(state.observation, game_data) bot_ai._prepare_start(client, player_id, game_info, game_data) bot_ai.on_start() bot_ai._prepare_step(game_state) bot_ai._prepare_first_step() return game_data, game_info, game_state
async def _advance_steps(self, steps: int): """Advances the game loop by amount of 'steps'. This function is meant to be used as a debugging and testing tool only. If you are using this, please be aware of the consequences, e.g. 'self.units' will be filled with completely new data.""" await self._after_step() # Advance simulation by exactly "steps" frames await self.client.step(steps) state = await self.client.observation() gs = GameState(state.observation) proto_game_info = await self.client._execute( game_info=sc_pb.RequestGameInfo()) self._prepare_step(gs, proto_game_info) await self.issue_events()
def get_map_specific_bot(map_path: Path) -> BotAI: assert map_path in MAPS with lzma.open(str(map_path.absolute()), "rb") as f: raw_game_data, raw_game_info, raw_observation = pickle.load(f) # Build fresh bot object, and load the pickle'd data into the bot object bot = BotAI() game_data = GameData(raw_game_data.data) game_info = GameInfo(raw_game_info.game_info) game_state = GameState(raw_observation) bot._initialize_variables() bot._prepare_start(client=None, player_id=1, game_info=game_info, game_data=game_data) bot._prepare_step(state=game_state, proto_game_info=raw_game_info) return bot
async def store_data_to_file(self, file_path: str): # Grab all raw data from observation raw_game_data = await self._client._execute( data=sc_pb.RequestData(ability_id=True, unit_type_id=True, upgrade_id=True, buff_id=True, effect_id=True) ) raw_game_info = await self._client._execute(game_info=sc_pb.RequestGameInfo()) raw_observation = self.state.response_observation # To test if this data is convertable in the first place game_data = GameData(raw_game_data.data) game_info = GameInfo(raw_game_info.game_info) game_state = GameState(raw_observation) os.makedirs(os.path.dirname(file_path), exist_ok=True) with lzma.open(file_path, "wb") as f: pickle.dump([raw_game_data, raw_game_info, raw_observation], f)
def get_map_specific_bots() -> Iterable[BotAI]: folder = os.path.dirname(__file__) subfolder_name = "pickle_data" pickle_folder_path = os.path.join(folder, subfolder_name) files = os.listdir(pickle_folder_path) for file in (f for f in files if f.endswith(".xz")): with lzma.open(os.path.join(folder, subfolder_name, file), "rb") as f: raw_game_data, raw_game_info, raw_observation = pickle.load(f) # Build fresh bot object, and load the pickle'd data into the bot object bot = BotAI() game_data = GameData(raw_game_data.data) game_info = GameInfo(raw_game_info.game_info) game_state = GameState(raw_observation) bot._initialize_variables() bot._prepare_start(client=None, player_id=1, game_info=game_info, game_data=game_data) bot._prepare_step(state=game_state, proto_game_info=raw_game_info) yield bot
async def on_start_async(self): raw_game_data = await self._client._execute( data=sc_pb.RequestData(ability_id=True, unit_type_id=True, upgrade_id=True, buff_id=True, effect_id=True) ) raw_game_info = await self._client._execute(game_info=sc_pb.RequestGameInfo()) raw_observation = self.state.response_observation # To test if this data is convertable in the first place game_data = GameData(raw_game_data.data) game_info = GameInfo(raw_game_info.game_info) game_state = GameState(raw_observation) print(f"Saving file to {self.map_name}.xz") file_path = self.get_pickle_file_path() os.makedirs(os.path.dirname(file_path), exist_ok=True) with lzma.open(file_path, "wb") as f: pickle.dump([raw_game_data, raw_game_info, raw_observation], f) await self._client.leave()
def _step(self, action_args): # if self.env.allies_client._status self.t += 1 allies_game_state = self.allies_game_state # current game_state self._loop_run(self.allies.ai.issue_events()) self._loop_run(self.allies.ai.on_step(action_args)) for _ in range(self.frame_skip_rate): self._loop_run(self.allies_client.step()) allies_state = self._loop_run(self.allies_client.observation()) next_game_state = GameState(allies_state.observation, self.allies_game_data) self.allies_game_state = next_game_state self.allies.ai._prepare_step(allies_game_state) game_result = self.allies_client._game_result done = True if game_result else False return next_game_state, done
async def _play_game_ai(client, player_id, ai, realtime, step_time_limit, game_time_limit, log_queue=None): if realtime: assert step_time_limit is None # step_time_limit works like this: # * If None, then step time is not limited # * If given integer or float, the bot will simpy resign if any step takes longer than that # * Otherwise step_time_limit must be an object, with following settings: # # Key | Value | Description # ------------|------------|------------- # penalty | None | No penalty, the bot can continue on next step # penalty | N: int | Cooldown penalty, BotAI.on_step will not be called for N steps # penalty | "resign" | Bot resigns when going over time limit # time_limit | int/float | Time limit for a single step # window_size | N: int | The time limit will be used for last N steps, instad of 1 # # Cooldown is a harsh penalty. The both loses the ability to act, but even worse, # the observation data from skipped steps is also lost. It's like falling asleep in # a middle of the game. time_penalty_cooldown = 0 if step_time_limit is None: time_limit = None time_window = None time_penalty = None elif isinstance(step_time_limit, (int, float)): time_limit = float(step_time_limit) time_window = SlidingTimeWindow(1) time_penalty = "resign" else: assert isinstance(step_time_limit, dict) time_penalty = step_time_limit.get("penalty", None) time_window = SlidingTimeWindow( int(step_time_limit.get("window_size", 1))) time_limit = float(step_time_limit.get("time_limit", None)) game_data = await client.get_game_data() game_info = await client.get_game_info() ai._prepare_start(client, player_id, game_info, game_data) try: ai.on_start() except Exception as e: logger.exception(f"AI on_start threw an error") logger.error(f"resigning due to previous error") ai.on_end(Result.Defeat) return Result.Defeat iteration = 0 while True: state = await client.observation() logger.debug(f"Score: {state.observation.observation.score.score}") if client._game_result: ai.on_end(client._game_result[player_id]) return client._game_result[player_id] gs = GameState(state.observation, game_data) if game_time_limit and (gs.game_loop * 0.725 * (1 / 16)) > game_time_limit: ai.on_end(Result.Tie) return Result.Tie ai._prepare_step(gs) if iteration == 0: ai._prepare_first_step() logger.debug( f"Running AI step, it={iteration} {gs.game_loop * 0.725 * (1 / 16):.2f}s" ) try: await ai.issue_events() if realtime: await ai.on_step(iteration) else: if time_penalty_cooldown > 0: time_penalty_cooldown -= 1 logger.warning( f"Running AI step: penalty cooldown: {time_penalty_cooldown}" ) iteration -= 1 # Do not increment the iteration on this round elif time_limit is None: await ai.on_step(iteration) else: out_of_budget = False budget = time_limit - time_window.available # Tell the bot how much time it has left attribute ai.time_budget_available = budget if budget < 0: logger.warning( f"Running AI step: out of budget before step") step_time = 0.0 out_of_budget = True else: step_start = time.monotonic() try: async with async_timeout.timeout(budget): await ai.on_step(iteration) except asyncio.TimeoutError: step_time = time.monotonic() - step_start logger.warning( f"Running AI step: out of budget; " + f"budget={budget:.2f}, steptime={step_time:.2f}, " + f"window={time_window.available_fmt}") out_of_budget = True step_time = time.monotonic() - step_start time_window.push(step_time) if out_of_budget and time_penalty != None: if time_penalty == "resign": raise RuntimeError("Out of time") else: time_penalty_cooldown = int(time_penalty) time_window.clear() except Exception as e: if isinstance(e, ProtocolError) and e.is_game_over_error: if realtime: return None result = client._game_result[player_id] if result is None: log.error("Game over, but no results gathered") raise return result # NOTE: this message is caught by pytest suite logger.exception(f"AI step threw an error") # DO NOT EDIT! logger.error(f"resigning due to previous error") # hspark8312: begin if log_queue is not None: from time import gmtime, strftime import traceback tb = traceback.format_exc() log_queue.put(f"AI step threw an error") log_queue.put(f'{e} -> {tb}') # hspark8312: end ai.on_end(Result.Defeat) return Result.Defeat logger.debug(f"Running AI step: done") if not realtime: if not client.in_game: # Client left (resigned) the game ai.on_end(client._game_result[player_id]) return client._game_result[player_id] await client.step() iteration += 1
async def _play_replay(client, ai, realtime=False, player_id=0): ai._initialize_variables() game_data = await client.get_game_data() game_info = await client.get_game_info() ping_response = await client.ping() client.game_step = 1 # This game_data will become self._game_data in botAI ai._prepare_start( client, player_id, game_info, game_data, realtime=realtime, base_build=ping_response.ping.base_build ) state = await client.observation() # Check game result every time we get the observation if client._game_result: await ai.on_end(client._game_result[player_id]) return client._game_result[player_id] gs = GameState(state.observation) proto_game_info = await client._execute(game_info=sc_pb.RequestGameInfo()) ai._prepare_step(gs, proto_game_info) ai._prepare_first_step() try: await ai.on_start() # TODO Catching too general exception Exception (broad-except) # pylint: disable=W0703 except Exception: logger.exception("AI on_start threw an error") logger.error("resigning due to previous error") await ai.on_end(Result.Defeat) return Result.Defeat iteration = 0 while True: if iteration != 0: if realtime: # TODO: check what happens if a bot takes too long to respond, so that the requested # game_loop might already be in the past state = await client.observation(gs.game_loop + client.game_step) else: state = await client.observation() # check game result every time we get the observation if client._game_result: try: await ai.on_end(client._game_result[player_id]) except TypeError: return client._game_result[player_id] return client._game_result[player_id] gs = GameState(state.observation) logger.debug(f"Score: {gs.score.score}") proto_game_info = await client._execute(game_info=sc_pb.RequestGameInfo()) ai._prepare_step(gs, proto_game_info) logger.debug(f"Running AI step, it={iteration} {gs.game_loop * 0.725 * (1 / 16):.2f}s") try: # Issue event like unit created or unit destroyed await ai.issue_events() await ai.on_step(iteration) await ai._after_step() # pylint: disable=W0703 # TODO Catching too general exception Exception (broad-except) except Exception as e: if isinstance(e, ProtocolError) and e.is_game_over_error: if realtime: return None await ai.on_end(Result.Victory) return None # NOTE: this message is caught by pytest suite logger.exception("AI step threw an error") # DO NOT EDIT! logger.error(f"Error: {e}") logger.error("Resigning due to previous error") try: await ai.on_end(Result.Defeat) except TypeError: return Result.Defeat return Result.Defeat logger.debug("Running AI step: done") if not realtime: if not client.in_game: # Client left (resigned) the game await ai.on_end(Result.Victory) return Result.Victory await client.step() # unindent one line to work in realtime iteration += 1
async def _play_game_ai( client: Client, player_id: int, ai: BotAI, realtime: bool, game_time_limit: Optional[int] ) -> Result: gs: GameState = None async def initialize_first_step() -> Optional[Result]: nonlocal gs ai._initialize_variables() game_data = await client.get_game_data() game_info = await client.get_game_info() ping_response = await client.ping() # This game_data will become self.game_data in botAI ai._prepare_start( client, player_id, game_info, game_data, realtime=realtime, base_build=ping_response.ping.base_build ) state = await client.observation() # check game result every time we get the observation if client._game_result: await ai.on_end(client._game_result[player_id]) return client._game_result[player_id] gs = GameState(state.observation) proto_game_info = await client._execute(game_info=sc_pb.RequestGameInfo()) try: ai._prepare_step(gs, proto_game_info) await ai.on_before_start() ai._prepare_first_step() await ai.on_start() # TODO Catching too general exception Exception (broad-except) # pylint: disable=W0703 except Exception: logger.exception("AI on_start threw an error") logger.error("Resigning due to previous error") await ai.on_end(Result.Defeat) return Result.Defeat result = await initialize_first_step() if result is not None: return result async def run_bot_iteration(iteration: int): nonlocal gs logger.debug(f"Running AI step, it={iteration} {gs.game_loop / 22.4:.2f}s") # Issue event like unit created or unit destroyed await ai.issue_events() await ai.on_step(iteration) await ai._after_step() logger.debug("Running AI step: done") # Only used in realtime=True previous_state_observation = None for iteration in range(10**10): if realtime and gs: # On realtime=True, might get an error here: sc2.protocol.ProtocolError: ['Not in a game'] with suppress(ProtocolError): requested_step = gs.game_loop + client.game_step state = await client.observation(requested_step) # If the bot took too long in the previous observation, request another observation one frame after if state.observation.observation.game_loop > requested_step: logger.debug("Skipped a step in realtime=True") previous_state_observation = state.observation state = await client.observation(state.observation.observation.game_loop + 1) else: state = await client.observation() # check game result every time we get the observation if client._game_result: await ai.on_end(client._game_result[player_id]) return client._game_result[player_id] gs = GameState(state.observation, previous_state_observation) previous_state_observation = None logger.debug(f"Score: {gs.score.score}") if game_time_limit and gs.game_loop / 22.4 > game_time_limit: await ai.on_end(Result.Tie) return Result.Tie proto_game_info = await client._execute(game_info=sc_pb.RequestGameInfo()) ai._prepare_step(gs, proto_game_info) await run_bot_iteration(iteration) # Main bot loop if not realtime: if not client.in_game: # Client left (resigned) the game await ai.on_end(client._game_result[player_id]) return client._game_result[player_id] # TODO: In bot vs bot, if the other bot ends the game, this bot gets stuck in requesting an observation when using main.py:run_multiple_games await client.step() return Result.Undecided