Source code for egta.asyncgame

"""module for games that require asynchronous profile access"""
import abc
import asyncio

import numpy as np
from gameanalysis import rsgame
from gameanalysis import utils


class _AsyncGame(rsgame._GameLike):  # pylint: disable=protected-access
    """An asynchronous game

    Supports asynchronous methods for ensuring particular payoff data"""

    @abc.abstractmethod
    def get_game(self):
        """Return all data available"""
        pass  # pragma: no cover

    @abc.abstractmethod
    async def get_restricted_game(self, rest):
        """Return a complete restricted game"""
        pass  # pragma: no cover

    @abc.abstractmethod
    async def get_deviation_game(self, rest, role_index=None):
        """Return a game with payoff data for all deviations

        If role index is specified, only deviations from that role are
        necessary."""
        pass  # pragma: no cover


class _CompleteAsyncGame(_AsyncGame):
    """A wrapper for a complete RsGame"""

    def __init__(self, game):
        super().__init__(game.role_names, game.strat_names, game.num_role_players)
        self._game = game

    def get_game(self):
        return self._game

    async def get_restricted_game(self, rest):
        return self._game.restrict(rest)

    async def get_deviation_game(self, rest, role_index=None):
        return self._game

    def __eq__(self, other):
        return (
            super().__eq__(other) and self._game == other._game
        )  # pylint: disable=protected-access

    def __hash__(self):
        return hash(self._game)

    def __str__(self):
        return repr(self._game)


[docs]def wrap(game): """Wrap a CompleteGame as an AsyncGame""" utils.check(game.is_complete(), "must use a complete game") return _CompleteAsyncGame(game)
class _MixedAsyncGame(_AsyncGame): """A lazy merging of two async games""" def __init__(self, agame0, agame1, prob): super().__init__(agame0.role_names, agame0.strat_names, agame0.num_role_players) self._agame0 = agame0 self._agame1 = agame1 self._prob = prob def get_game(self): return rsgame.mix(self._agame0.get_game(), self._agame1.get_game(), self._prob) async def get_restricted_game(self, rest): game0, game1 = await asyncio.gather( self._agame0.get_restricted_game(rest), self._agame1.get_restricted_game(rest), ) return rsgame.mix(game0, game1, self._prob) async def get_deviation_game(self, rest, role_index=None): game0, game1 = await asyncio.gather( self._agame0.get_deviation_game(rest, role_index), self._agame1.get_deviation_game(rest, role_index), ) return rsgame.mix(game0, game1, self._prob) def __eq__(self, othr): # pylint: disable-msg=protected-access return super().__eq__(othr) and ( ( self._agame0 == othr._agame0 and self._agame1 == othr._agame1 and np.isclose(self._prob, othr._prob) ) or ( self._agame0 == othr._agame1 and self._agame1 == othr._agame0 and np.isclose(self._prob, 1 - othr._prob) ) ) @utils.memoize def __hash__(self): return hash(frozenset([self._agame0, self._agame1])) def __str__(self): return "{} - {:g} - {}".format(self._agame0, self._prob, self._agame1)
[docs]def mix(agame0, agame1, prob): """Mix two async games""" utils.check( rsgame.empty_copy(agame0) == rsgame.empty_copy(agame1), "games must have identically structure", ) return _MixedAsyncGame(agame0, agame1, prob)