Пример #1
0
    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
Пример #2
0
    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
Пример #3
0
    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")
Пример #4
0
 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()
Пример #7
0
 async def get_game_info(self) -> GameInfo:
     result = await self._execute(game_info=sc_pb.RequestGameInfo())
     return GameInfo(result.game_info)
Пример #8
0
 def game_info(self):
     """Get the basic information about the game."""
     return self._client.send(game_info=sc_pb.RequestGameInfo())
Пример #9
0
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
Пример #10
0
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
Пример #11
0
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
Пример #12
0
    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