async def play(self, ws, observation): success = True request_data = api.Request( data=api.RequestData(ability_id=True, unit_type_id=True, upgrade_id=True) ) await asyncio.wait_for(ws.send(request_data.SerializeToString()), 5) try: result = await asyncio.wait_for(ws.recv(), 5) data_response = api.Response.FromString(result) game_data = data_response.data request_game_info = api.Request(game_info=api.RequestGameInfo()) await asyncio.wait_for(ws.send(request_game_info.SerializeToString()), 5) result = await asyncio.wait_for(ws.recv(), 5) game_info_response = api.Response.FromString(result) # If game is still on if game_data.units: obj = decode_observation(observation.observation.observation, game_data, game_info_response) obs = MessageToDict(observation) obs = str(obs) obs = obs.replace("\'", "\"") obs = obs.replace("False", "false") obs = obs.replace("True", "true") obs = json.loads(obs,encoding="UTF-8") game_meta = api.Request( game_info=api.RequestGameInfo() ) await ws.send(game_meta.SerializeToString()) result = await ws.recv() game_meta = api.Response.FromString(result) game_meta = MessageToDict(game_meta) game_meta = str(game_meta) game_meta = game_meta.replace("\'", "\"") game_meta = game_meta.replace("False", "false") game_meta = game_meta.replace("True", "true") game_meta = json.loads(game_meta,encoding="UTF-8") game_meta = game_meta.get("gameInfo", None) game_meta.pop("modNames") game_meta.pop("options") game_meta.pop("mapName") if("localMapPath" in game_meta.keys()): game_meta.pop("localMapPath") game_meta.pop("playerInfo") game_meta.update(game_meta["startRaw"]) game_meta.pop("startRaw") game_meta.pop("mapSize") await self.process_step(ws, obj, raw=(obs, game_meta, game_data)) # function = self.decision_function # alvailable_actions = self.query_alvailable_actions() # to_do_action = function(observation, alvailable_actions) # while(to_do_action and alvailable_actions): # self.send_order(self, to_do_action) # to_do_action = self.query_alvailable_actions() except asyncio.TimeoutError: return False return True
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
async def on_start(self) -> None: """ Set up data that require information from the game. Note: This function is called automatically at iteration = 0. Args: None Returns: None """ 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 self.pathing = PathManager(raw_game_data, raw_game_info, raw_observation) self.creeper = Creeper(raw_game_data, raw_game_info, raw_observation) # build_selector = BuildOrderManager(self.enemy_race) # self.build_order = build_selector.select_build_order() with open("builds/1312.pickle", "rb") as f: self.build_order = pickle.load(f) # nosec # all possible arguments are handled by BuildOrderManager class self.tag_dicts.append(self.pathing.pathing_dict) self.target = self.enemy_start_locations[0].position await self.chat_send("gl hf")
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()
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)
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()
async def get_game_info(self) -> GameInfo: result = await self._execute(game_info=sc_pb.RequestGameInfo()) return GameInfo(result.game_info)
def game_info(self): """Get the basic information about the game.""" return self._client.send(game_info=sc_pb.RequestGameInfo())
async def _play_game_ai(client, player_id, ai, realtime, step_time_limit, game_time_limit): 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)) ai._initialize_variables() game_data = await client.get_game_data() game_info = await client.get_game_info() # This game_data will become self._game_data in botAI ai._prepare_start(client, player_id, game_info, game_data, realtime=realtime) 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() except Exception as e: logger.exception(f"AI on_start threw an error") logger.error(f"resigning due to previous error") await ai.on_end(Result.Defeat) return Result.Defeat iteration = 0 realtime_game_loop = -1 while True: if iteration != 0: 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 as error: # print(f"caught type error {error}") # print(f"return {client._game_result[player_id]}") return client._game_result[player_id] return client._game_result[player_id] gs = GameState(state.observation) logger.debug(f"Score: {gs.score.score}") if game_time_limit and (gs.game_loop * 0.725 * (1 / 16)) > 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) logger.debug( f"Running AI step, it={iteration} {gs.game_loop * 0.725 * (1 / 16):.2f}s" ) try: if realtime: # Prevent bot from running multiple times in the same game_loop in realtime=True if ai.state.game_loop != realtime_game_loop: # Issue event liks unit created or unit destroyed await ai.issue_events() await ai.on_step(iteration) realtime_game_loop = await ai._after_step() 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.issue_events() await ai.on_step(iteration) await ai._after_step() 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.issue_events() 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 is not None: if time_penalty == "resign": raise RuntimeError("Out of time") else: time_penalty_cooldown = int(time_penalty) time_window.clear() await ai._after_step() 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: logger.error("Game over, but no results gathered") raise await ai.on_end(result) return result # NOTE: this message is caught by pytest suite logger.exception(f"AI step threw an error") # DO NOT EDIT! logger.error(f"Error: {e}") logger.error(f"Resigning due to previous error") try: await ai.on_end(Result.Defeat) except TypeError as error: # print(f"caught type error {error}") # print(f"return {Result.Defeat}") return 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 await 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() except Exception as e: logger.exception(f"AI on_start threw an error") logger.error(f"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 as error: # print(f"caught type error {error}") # print(f"return {client._game_result[player_id]}") 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() 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: # logger.error("Game over, but no results gathered") # raise await ai.on_end(Result.Victory) return None # NOTE: this message is caught by pytest suite logger.exception(f"AI step threw an error") # DO NOT EDIT! logger.error(f"Error: {e}") logger.error(f"Resigning due to previous error") try: await ai.on_end(Result.Defeat) except TypeError as error: # print(f"caught type error {error}") # print(f"return {Result.Defeat}") return 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 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
async def load_replay(self, replay_file, id=0): print(replay_file) async with websockets.connect("ws://{0}:{1}/sc2api".format( self.host.address, self.host.port)) as ws: replay_meta = api.Request(replay_info=api.RequestReplayInfo( replay_path=replay_file)) await ws.send(replay_meta.SerializeToString()) result = await ws.recv() metadata = api.Response.FromString(result) self.replay_info = { "map": metadata.replay_info.map_name, "races": [ metadata.replay_info.player_info[0].player_info. race_requested, metadata.replay_info.player_info[1].player_info. race_requested, ], "results": [ metadata.replay_info.player_info[0].player_result.result, metadata.replay_info.player_info[1].player_result.result, ], } print(self.replay_info) msg = api.Request(start_replay=api.RequestStartReplay( replay_path=replay_file, observed_player_id=id, options=api.InterfaceOptions(raw=True, score=False), )) await ws.send(msg.SerializeToString()) time.sleep(1) result = await ws.recv() response = api.Response.FromString(result) print(response) game_meta = api.Request(game_info=api.RequestGameInfo()) await ws.send(game_meta.SerializeToString()) result = await ws.recv() game_meta = api.Response.FromString(result) game_meta = MessageToDict(game_meta) game_meta = str(game_meta) game_meta = game_meta.replace("\'", "\"") game_meta = game_meta.replace("False", "false") game_meta = game_meta.replace("True", "true") game_meta = json.loads(game_meta, encoding="UTF-8") if "gameInfo" in game_meta.keys(): game_meta = game_meta.get("gameInfo", None) game_meta.pop("modNames") game_meta.pop("options") game_meta.pop("mapName") if ("localMapPath" in game_meta.keys()): game_meta.pop("localMapPath") game_meta.pop("playerInfo") game_meta.update(game_meta["startRaw"]) game_meta.pop("startRaw") game_meta.pop("mapSize") self.game_info = game_meta self.status = "started" return True else: return False