Top

sc2.main module

import asyncio
import async_timeout

import logging
logger = logging.getLogger(__name__)

from .sc2process import SC2Process
from .portconfig import Portconfig
from .client import Client
from .player import Human, Bot
from .data import Race, Difficulty, Result, ActionResult, CreateGameError
from .game_state import GameState
from .protocol import ConnectionAlreadyClosed

async def _play_game_human(client, player_id, realtime, game_time_limit):
    while True:
        state = await client.observation()
        if client._game_result:
            return client._game_result[player_id]

        if game_time_limit and (state.observation.observation.game_loop * 0.725 * (1 / 16)) > game_time_limit:
            print(state.observation.game_loop, state.observation.game_loop * 0.14)
            return Result.Tie

        if not realtime:
            await client.step()

async def _play_game_ai(client, player_id, ai, realtime, step_time_limit, game_time_limit):
    game_data = await client.get_game_data()
    game_info = await client.get_game_info()

    ai._prepare_start(client, player_id, game_info, game_data)
    ai.on_start()

    iteration = 0
    while True:
        state = await client.observation()
        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, realtime={realtime}")

        try:
            await ai.issue_events()
            if realtime:
                await ai.on_step(iteration)
            else:
                logger.debug(f"Running AI step, timeout={step_time_limit}")
                try:
                    async with async_timeout.timeout(step_time_limit):
                        await ai.on_step(iteration)
                except asyncio.TimeoutError:
                    logger.warning(f"Running AI step: out of time")
        except Exception as e:
            # 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")
            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_game(player, client, realtime, portconfig, step_time_limit=None, game_time_limit=None, rgb_render_config=None):
    assert isinstance(realtime, bool), repr(realtime)

    player_id = await client.join_game(player.race, portconfig=portconfig, rgb_render_config=rgb_render_config)
    logging.info(f"Player id: {player_id}")

    if isinstance(player, Human):
        result = await _play_game_human(client, player_id, realtime, game_time_limit)
    else:
        result = await _play_game_ai(client, player_id, player.ai, realtime, step_time_limit, game_time_limit)

    logging.info(f"Result for player id: {player_id}: {result}")
    return result

async def _setup_host_game(server, map_settings, players, realtime, random_seed=None):
    r = await server.create_game(map_settings, players, realtime, random_seed)
    if r.create_game.HasField("error"):
        err = f"Could not create game: {CreateGameError(r.create_game.error)}"
        if r.create_game.HasField("error_details"):
            err += f": {r.create_game.error_details}"
        logger.critical(err)
        raise RuntimeError(err)

    return Client(server._ws)


async def _host_game(map_settings, players, realtime, portconfig=None, save_replay_as=None, step_time_limit=None,
                     game_time_limit=None, rgb_render_config=None, random_seed=None):
    assert len(players) > 0, "Can't create a game without players"

    assert any(isinstance(p, (Human, Bot)) for p in players)

    async with SC2Process(render=rgb_render_config is not None) as server:
        await server.ping()

        client = await _setup_host_game(server, map_settings, players, realtime, random_seed)

        try:
            result = await _play_game(players[0], client, realtime, portconfig, step_time_limit, game_time_limit, rgb_render_config)
            if save_replay_as is not None:
                await client.save_replay(save_replay_as)
            await client.leave()
            await client.quit()
        except ConnectionAlreadyClosed:
            logging.error(f"Connection was closed before the game ended")
            return None

        return result

async def _host_game_aiter(map_settings, players, realtime, portconfig=None, save_replay_as=None, step_time_limit=None, game_time_limit=None):
    assert players, "Can't create a game without players"

    assert any(isinstance(p, (Human, Bot)) for p in players)

    async with SC2Process() as server:
        while True:
            await server.ping()

            client = await _setup_host_game(server, map_settings, players, realtime)

            try:
                result = await _play_game(players[0], client, realtime, portconfig, step_time_limit, game_time_limit)

                if save_replay_as is not None:
                    await client.save_replay(save_replay_as)
                await client.leave()
            except ConnectionAlreadyClosed:
                logging.error(f"Connection was closed before the game ended")
                return

            new_players = yield result
            if new_players is not None:
                players = new_players

def _host_game_iter(*args, **kwargs):
    game = _host_game_aiter(*args, **kwargs)
    new_playerconfig = None
    while True:
        new_playerconfig = yield asyncio.get_event_loop().run_until_complete(game.asend(new_playerconfig))


async def _join_game(players, realtime, portconfig, save_replay_as=None, step_time_limit=None, game_time_limit=None):
    async with SC2Process() as server:
        await server.ping()

        client = Client(server._ws)

        try:
            result = await _play_game(players[1], client, realtime, portconfig, step_time_limit, game_time_limit)
            if save_replay_as is not None:
                await client.save_replay(save_replay_as)
            await client.leave()
            await client.quit()
        except ConnectionAlreadyClosed:
            logging.error(f"Connection was closed before the game ended")
            return None

        return result

def run_game(map_settings, players, **kwargs):
    if sum(isinstance(p, (Human, Bot)) for p in players) > 1:
        host_only_args = ["save_replay_as", "rgb_render_config", "random_seed"]
        join_kwargs = {k: v for k, v in kwargs.items() if k not in host_only_args}

        portconfig = Portconfig()
        result = asyncio.get_event_loop().run_until_complete(asyncio.gather(
            _host_game(map_settings, players, **kwargs, portconfig=portconfig),
            _join_game(players, **join_kwargs, portconfig=portconfig)
        ))
    else:
        result = asyncio.get_event_loop().run_until_complete(
            _host_game(map_settings, players, **kwargs)
        )
    return result

Module variables

var logger

Functions

def run_game(

map_settings, players, **kwargs)

def run_game(map_settings, players, **kwargs):
    if sum(isinstance(p, (Human, Bot)) for p in players) > 1:
        host_only_args = ["save_replay_as", "rgb_render_config", "random_seed"]
        join_kwargs = {k: v for k, v in kwargs.items() if k not in host_only_args}

        portconfig = Portconfig()
        result = asyncio.get_event_loop().run_until_complete(asyncio.gather(
            _host_game(map_settings, players, **kwargs, portconfig=portconfig),
            _join_game(players, **join_kwargs, portconfig=portconfig)
        ))
    else:
        result = asyncio.get_event_loop().run_until_complete(
            _host_game(map_settings, players, **kwargs)
        )
    return result