Example #1
0
def test_never_best_response_conditional():
    """Test never best response conditional"""
    profiles = [
        [2, 0],
        [0, 2],
    ]
    payoffs = [
        [1, 0],
        [0, 1],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.never_best_response(game, conditional=True)
    assert np.all(dom == [False, False])

    profiles = [
        [1, 1],
        [0, 2],
    ]
    payoffs = [
        [2, 2],
        [0, 3],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.never_best_response(game, conditional=True)
    assert np.all(dom == [True, False])
Example #2
0
def test_remove_dpr_profiles_with_no_data():
    """Test that dpr removes profiles with no data"""
    profiles = [[1, 3],
                [3, 1]]
    payoffs = [[3, 4],
               [6, np.nan]]
    game = dpr.reduce_game(paygame.game(4, 2, profiles, payoffs), 2)
    assert game.num_profiles == 1

    profiles = [[1, 3],
                [3, 1]]
    payoffs = [[np.nan, 4],
               [6, np.nan]]
    game = dpr.reduce_game(paygame.game(4, 2, profiles, payoffs), 2)
    assert game.is_empty()

    profiles = [[1, 3]]
    payoffs = [[3, 4]]
    game = dpr.reduce_game(paygame.game(4, 2, profiles, payoffs), 2)
    assert game.num_profiles == 1

    profiles = [[1, 3]]
    payoffs = [[np.nan, 4]]
    game = dpr.reduce_game(paygame.game(4, 2, profiles, payoffs), 2)
    assert game.is_empty()
Example #3
0
async def test_merge_trace():
    """Test that traces are merged"""
    game0 = asyncgame.wrap(
        paygame.game(2, 2, [[2, 0], [1, 1], [0, 2]], [[0, 0], [1, 1], [0, 0]]))
    game1 = asyncgame.wrap(
        paygame.game(2, 2, [[2, 0], [1, 1], [0, 2]], [[0, 0], [1, 1], [0, 3]]))
    traces = await trace.trace_all_equilibria(game0, game1)
    assert len(traces) == 1
Example #4
0
def test_weakly_dominated():
    """Test weak domination"""
    profiles = [
        [2, 0],
        [1, 1],
        [0, 2],
    ]
    payoffs = [
        [2, 0],
        [2, 1],
        [0, 1],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.weakly_dominated(game)
    assert np.all(dom == [False, True])

    profiles = [[2, 0], [0, 2]]
    payoffs = [
        [2, 0],
        [0, 2],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.weakly_dominated(game)
    assert np.all(dom == [False, False])

    profiles = [
        [2, 0],
        [1, 1],
        [0, 2],
    ]
    payoffs = [
        [2, 0],
        [2, 1],
        [0, 2],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.weakly_dominated(game)
    assert np.all(dom == [False, True])

    profiles = [
        [2, 0],
        [1, 1],
        [0, 2],
    ]
    payoffs = [
        [2, 0],
        [2, 2],
        [0, 2],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.weakly_dominated(game)
    assert np.all(dom == [True, True])
Example #5
0
def random_pairs():
    """Generate random pairs of games"""
    mix = gamegen.sym_2p2s_known_eq(.5)
    dom1 = paygame.game(
        2, 2, [[2, 0], [1, 1], [0, 2]], [[.1, 0], [.1, 0], [0, 0]])
    dom2 = paygame.game(
        2, 2, [[2, 0], [1, 1], [0, 2]], [[0, 0], [0, .1], [0, .1]])
    yield mix, dom1
    yield dom1, mix
    yield mix, dom2
    yield dom2, mix
    for base in tu.edge_games():
        play, strats = base.num_role_players, base.num_role_strats
        yield (gamegen.poly_aggfn(play, strats, 6),
               gamegen.poly_aggfn(play, strats, 6))
Example #6
0
def test_strictly_dominated_conditional():
    """Test strict domination conditional"""
    profiles = [
        [0, 2],
        [1, 1],
    ]
    payoffs = [
        [0, 1],
        [2, 1],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.strictly_dominated(game)
    assert np.all(dom == [False, False])
    dom = dominance.strictly_dominated(game, conditional=False)
    assert np.all(dom == [False, True])

    profiles = [
        [2, 0],
        [1, 1],
    ]
    payoffs = [
        [2, 0],
        [2, 1],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.strictly_dominated(game)
    assert np.all(dom == [False, True])

    profiles = [
        [2, 0],
        [1, 1],
    ]
    payoffs = [
        [2, 0],
        [2, 2],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.strictly_dominated(game)
    assert np.all(dom == [False, False])

    profiles = [[2, 0], [0, 2]]
    payoffs = [
        [2, 0],
        [0, 1],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.strictly_dominated(game, conditional=False)
    assert np.all(dom == [False, False])
Example #7
0
def test_pure_incomplete_data():
    """Test pure regret with incomplete data"""
    profiles = [[2, 0]]
    payoffs = [[1.0, 0.0]]
    game = paygame.game(2, 2, profiles, payoffs)
    reg = regret.pure_strategy_regret(game, [2, 0])
    assert np.isnan(reg), 'regret of missing profile not nan'
Example #8
0
def test_analysis_dup_equilibria():
    """Test analysis dpr equilibria"""
    # Two restrictions, but dominated, so identical equilibria
    profiles = [
        [2, 0, 0, 0],
        [1, 1, 0, 0],
        [1, 0, 1, 0],
        [0, 2, 0, 0],
        [0, 1, 1, 0],
        [0, 0, 2, 0],
        [0, 1, 0, 1],
        [0, 0, 1, 1],
        [0, 0, 0, 2],
    ]
    payoffs = [
        [0, 0, 0, 0],
        [0, 1, 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 0],
        [0, 1, 1, 0],
        [0, 0, 0, 0],
        [0, 1, 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 0],
    ]
    game = paygame.game(2, 4, profiles, payoffs)
    game_str = json.dumps(game.to_json())

    with stdin(game_str), stdout() as out, stderr() as err:
        assert run('analyze', '-s'), err.getvalue()
    assert 'Found 2 maximal complete restricted games' in out.getvalue()
Example #9
0
def test_analysis_dev_explored():
    """Test analysis deviations explored"""
    # Beneficial deviation to an already explored restriction
    profiles = [
        [2, 0, 0, 0],
        [1, 1, 0, 0],
        [1, 0, 1, 0],
        [0, 2, 0, 0],
        [0, 1, 1, 0],
        [0, 0, 2, 0],
        [0, 1, 0, 1],
        [0, 0, 1, 1],
        [0, 0, 0, 2],
    ]
    payoffs = [
        [0, 0, 0, 0],
        [0, 1, 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 0],
        [0, 1, 1, 0],
        [0, 0, 0, 0],
        [0, 0, 0, 1],
        [0, 0, 0, 1],
        [0, 0, 0, 0],
    ]
    game = paygame.game(2, 4, profiles, payoffs)
    game_str = json.dumps(game.to_json())

    with stdin(game_str), stdout() as out, stderr() as err:
        assert run('analyze', '-s'), err.getvalue()
    assert ('Found no unexplored best-response restricted games'
            in out.getvalue())
Example #10
0
def test_pure_strategy_deviation_gains():
    """Test pure strategy deviation gains"""
    profiles = [[2, 0, 2, 0],
                [2, 0, 1, 1],
                [2, 0, 0, 2],
                [1, 1, 2, 0],
                [1, 1, 1, 1],
                [1, 1, 0, 2],
                [0, 2, 2, 0],
                [0, 2, 1, 1],
                [0, 2, 0, 2]]
    payoffs = [[1, 0, 2, 0],
               [3, 0, 4, 5],
               [6, 0, 0, 7],
               [8, 9, 10, 0],
               [11, 12, 13, 14],
               [15, 16, 0, 17],
               [0, 18, 19, 0],
               [0, 20, 21, 22],
               [0, 23, 0, 24]]
    game = paygame.game(2, [2, 2], profiles, payoffs)

    gains = regret.pure_strategy_deviation_gains(game, [2, 0, 2, 0])
    assert np.allclose(gains, [0, 8, 0, 0, 0, 3, 0, 0])
    gains = regret.pure_strategy_deviation_gains(game, [1, 1, 1, 1])
    assert np.allclose(gains, [0, 9, -9, 0, 0, 4, -4, 0])
Example #11
0
def test_mixed_incomplete_data_2():
    """Test mixed with incomplete data"""
    profiles = [[2, 0]]
    payoffs = [[1.0, 0.0]]
    game = paygame.game(2, 2, profiles, payoffs)
    devgains = regret.mixture_deviation_gains(game, [1, 0])
    assert np.allclose(devgains, [0, np.nan], equal_nan=True), \
        "nonzero regret or deviation without payoff didn't return nan"
Example #12
0
def test_profiles_payoffs():
    """Test payoffs"""
    matg = matgame.matgame([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
    copy = paygame.game_copy(matg)
    profs = [[1, 0, 1, 0], [1, 0, 0, 1], [0, 1, 1, 0], [0, 1, 0, 1]]
    pays = [[1, 0, 2, 0], [3, 0, 0, 4], [0, 5, 6, 0], [0, 7, 0, 8]]
    game = paygame.game([1, 1], 2, profs, pays)
    assert copy == game
Example #13
0
def sym_2p2s_known_eq(eq_prob):
    """Generate a symmetric 2-player 2-strategy game

    This game has a single mixed equilibrium where strategy one is played with
    probability eq_prob.
    """
    profiles = [[2, 0], [1, 1], [0, 2]]
    payoffs = [[0, 0], [eq_prob, 1 - eq_prob], [0, 0]]
    return paygame.game(2, 2, profiles, payoffs)
Example #14
0
def test_analysis_no_data():
    """Test analysis on empty game"""
    game = paygame.game([2], [2], [[1, 1]], [[5, float('nan')]])
    game_str = json.dumps(game.to_json())

    with stdin(game_str), stdout() as out, stderr() as err:
        assert run('analyze', '-s'), err.getvalue()
    out = out.getvalue()
    assert 'There was no profile with complete payoff data' in out
    assert 'Found no complete restricted games' in out
Example #15
0
def test_canon():
    """Test basic canon game"""
    profs = [[2, 0, 0, 3],
             [1, 1, 0, 3]]
    pays = [[1, 0, 0, 1],
            [2, 3, 0, np.nan]]
    game = paygame.game([2, 3], [3, 1], profs, pays)
    cgame = canongame.canon(game)
    assert cgame.num_profiles == 2
    assert cgame.num_complete_profiles == 1
    pay = cgame.get_payoffs([2, 0, 0])
    assert np.allclose(pay, [1, 0, 0])

    expected = [[2, 0, 0],
                [1, 1, 0]]
    assert np.all(cgame.profiles() == expected)
    expected = [[1, 0, 0],
                [2, 3, 0]]
    assert np.allclose(cgame.payoffs(), expected)

    assert np.allclose(cgame.deviation_payoffs([1, 0, 0]), [1, 3, np.nan],
                       equal_nan=True)
    dev, jac = cgame.deviation_payoffs([0.5, 0.5, 0], jacobian=True)
    assert dev.shape == (3,)
    assert jac.shape == (3, 3)

    assert np.allclose(cgame.min_strat_payoffs(), [1, 3, np.nan],
                       equal_nan=True)
    assert np.allclose(cgame.max_strat_payoffs(), [2, 3, np.nan],
                       equal_nan=True)

    ngame = cgame.normalize()
    expected = [[0, 0, 0],
                [0.5, 1, 0]]
    assert utils.allclose_perm(ngame.payoffs(), expected)

    rgame = cgame.restrict([True, True, False])
    expected = [[1, 0],
                [2, 3]]
    assert utils.allclose_perm(rgame.payoffs(), expected)

    copy_str = json.dumps(cgame.to_json())
    copy = canongame.canon_json(json.loads(copy_str))
    assert hash(cgame) == hash(copy)
    assert cgame == copy

    assert [2, 0, 0] in cgame
    assert [0, 2, 0] not in cgame

    assert repr(cgame) == 'CanonGame([2], [3], 2 / 6)'

    other = canongame.canon(gamegen.normal_aggfn([2, 2, 3], [3, 1, 1], 2))
    assert other + cgame == cgame + other
Example #16
0
def test_empty_dpr_1():
    """Reduction is empty because profile is invalid"""
    profiles = [
        [2, 4],
    ]
    payoffs = [
        [1, 2],
    ]
    game = paygame.game(6, 2, profiles, payoffs)
    red_game = dpr.reduce_game(game, 2)
    assert np.all(red_game.num_role_players == [2])
    assert red_game.is_empty()
Example #17
0
def test_trace_equilibria():
    """Test trace known game equilibrium"""
    profs = [[2, 0],
             [1, 1],
             [0, 2]]
    pays1 = [[1, 0],
             [1, 0],
             [0, 0]]
    game0 = paygame.game(2, 2, profs, pays1)
    pays2 = [[0, 0],
             [0, 1],
             [0, 1]]
    game1 = paygame.game(2, 2, profs, pays2)

    probs, mixes = trace.trace_equilibrium(game0, game1, 0, [1, 0], 1)
    assert np.isclose(probs[0], 0)
    assert np.isclose(probs[-1], 0.5, atol=1.1e-3)
    assert np.allclose(mixes, [1, 0])
    probs, mixes = trace.trace_equilibrium(game0, game1, 1, [0, 1], 0)
    assert np.isclose(probs[0], 1)
    assert np.isclose(probs[-1], 0.5, atol=1.1e-3)
    assert np.allclose(mixes, [0, 1])
Example #18
0
def test_mixed_incomplete_data():
    """Test mixed incomplete data"""
    profiles = [[2, 0],
                [1, 1]]
    payoffs = [[4.3, 0],
               [6.2, 6.7]]
    game = paygame.game(2, 2, profiles, payoffs)
    dev_gain = regret.mixture_deviation_gains(game, [1, 0])
    expected_gains = [0.0, 2.4]
    assert np.allclose(dev_gain, expected_gains), \
        'mixture gains wrong {} instead of {}'.format(dev_gain, expected_gains)
    dev_gain = regret.mixture_deviation_gains(game, game.uniform_mixture())
    assert np.isnan(dev_gain).all(), 'had data for mixture without data'
Example #19
0
def test_empty_dpr_2():
    """Reduction is empty because profile doesn\'t have all payoffs"""
    profiles = [
        [1, 3],
    ]
    payoffs = [
        [1, 2],
    ]
    game = paygame.game(4, 2, profiles, payoffs)
    red_game = dpr.reduce_game(game, 2)
    assert np.all(red_game.num_role_players == [2])
    assert np.all(red_game.profiles() == [[1, 1]])
    assert [1, 1] not in red_game  # incomplete profiles don't register
Example #20
0
def test_dpr_incomplete_profile():
    """Test that when allow_incomplete, we get appropriate payoffs"""
    profiles = [[4, 0, 0, 9],
                [1, 3, 9, 0],
                [2, 2, 9, 0]]
    payoffs = [[1, 0, 0, 2],
               [3, 4, 5, 0],
               [6, 7, 8, 0]]
    game = paygame.game([4, 9], 2, profiles, payoffs)
    red_game = dpr.reduce_game(game, [2, 3])
    actual = red_game.get_payoffs([2, 0, 0, 3])
    assert np.allclose(actual, [1, 0, 0, 2])
    actual = red_game.get_payoffs([1, 1, 3, 0])
    assert np.allclose(actual, [3, np.nan, 8, 0], equal_nan=True)
Example #21
0
def test_profiles_payoffs():
    """Test payoffs"""
    matg = matgame.matgame([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
    copy = paygame.game_copy(matg)
    profs = [[1, 0, 1, 0],
             [1, 0, 0, 1],
             [0, 1, 1, 0],
             [0, 1, 0, 1]]
    pays = [[1, 0, 2, 0],
            [3, 0, 0, 4],
            [0, 5, 6, 0],
            [0, 7, 0, 8]]
    game = paygame.game([1, 1], 2, profs, pays)
    assert copy == game
Example #22
0
def test_weakly_dominated_conditional():
    """Test weak domination conditional"""
    profiles = [
        [0, 2],
        [1, 1],
    ]
    payoffs = [
        [0, 1],
        [1, 1],
    ]
    game = paygame.game(2, 2, profiles, payoffs)
    dom = dominance.weakly_dominated(game)
    assert np.all(dom == [True, False])
    dom = dominance.weakly_dominated(game, conditional=False)
    assert np.all(dom == [True, True])
Example #23
0
def test_maximal_restrictions_partial_profiles():
    """Test that maximal restrictions properly handles partial profiles"""
    profiles = [[2, 0],
                [1, 1],
                [0, 2]]
    payoffs = [[1, 0],
               [np.nan, 2],
               [0, 3]]
    game = paygame.game([2], [2], profiles, payoffs)
    rests = restrict.maximal_restrictions(game)
    expected = utils.axis_to_elem(np.array([
        [True, False],
        [False, True]]))
    assert np.setxor1d(utils.axis_to_elem(rests), expected).size == 0, \
        "Didn't produce both pure restrictions"
Example #24
0
def test_canon():
    """Test basic canon game"""
    profs = [[2, 0, 0, 3], [1, 1, 0, 3]]
    pays = [[1, 0, 0, 1], [2, 3, 0, np.nan]]
    game = paygame.game([2, 3], [3, 1], profs, pays)
    cgame = canongame.canon(game)
    assert cgame.num_profiles == 2
    assert cgame.num_complete_profiles == 1
    pay = cgame.get_payoffs([2, 0, 0])
    assert np.allclose(pay, [1, 0, 0])

    expected = [[2, 0, 0], [1, 1, 0]]
    assert np.all(cgame.profiles() == expected)
    expected = [[1, 0, 0], [2, 3, 0]]
    assert np.allclose(cgame.payoffs(), expected)

    assert np.allclose(cgame.deviation_payoffs([1, 0, 0]), [1, 3, np.nan],
                       equal_nan=True)
    dev, jac = cgame.deviation_payoffs([0.5, 0.5, 0], jacobian=True)
    assert dev.shape == (3, )
    assert jac.shape == (3, 3)

    assert np.allclose(cgame.min_strat_payoffs(), [1, 3, np.nan],
                       equal_nan=True)
    assert np.allclose(cgame.max_strat_payoffs(), [2, 3, np.nan],
                       equal_nan=True)

    ngame = cgame.normalize()
    expected = [[0, 0, 0], [0.5, 1, 0]]
    assert utils.allclose_perm(ngame.payoffs(), expected)

    rgame = cgame.restrict([True, True, False])
    expected = [[1, 0], [2, 3]]
    assert utils.allclose_perm(rgame.payoffs(), expected)

    copy_str = json.dumps(cgame.to_json())
    copy = canongame.canon_json(json.loads(copy_str))
    assert hash(cgame) == hash(copy)
    assert cgame == copy

    assert [2, 0, 0] in cgame
    assert [0, 2, 0] not in cgame

    assert repr(cgame) == 'CanonGame([2], [3], 2 / 6)'

    other = canongame.canon(gamegen.normal_aggfn([2, 2, 3], [3, 1, 1], 2))
    assert other + cgame == cgame + other
Example #25
0
def test_max_pure_profile_profile_game():
    """Test that game are correct when profiles have incomplete data"""
    profiles = [[2, 0, 2, 0],
                [1, 1, 2, 0],
                [1, 1, 1, 1]]
    payoffs = [[np.nan, 0, 5, 0],  # Max role 2
               [2, 3, np.nan, 0],  # Max role 1
               [1, 1, 1, 1]]       # Max total
    game = paygame.game([2, 2], [2, 2], profiles, payoffs)
    welfare, profile = regret.max_pure_social_welfare(game)
    assert welfare == 4
    assert np.all(profile == [1, 1, 1, 1])
    welfares, profiles = regret.max_pure_social_welfare(game, by_role=True)
    assert np.allclose(welfares, [5, 10])
    expected = [[1, 1, 2, 0],
                [2, 0, 2, 0]]
    assert np.all(profiles == expected)
Example #26
0
 def find_eq(self, n=1):
     players = self.num_players
     strats = [self.ms] * len(players)
     eg = rsgame.empty(players, strats)
     profs = eg.all_profiles()
     pays = []
     for p in profs:
         pays.append(
             utils.estimate_payoff(self.sim, p, self.num_players, n=n))
     pays = np.array(pays)
     pays[profs == 0] = 0
     pg = paygame.game(players, strats, profs, pays)
     self.game = pg
     # Compute the Nash for a couple of times
     nashes = []
     for _ in range(5):
         n = nash.replicator_dynamics(pg, pg.random_mixture())
         nashes.append(n)
     return nashes
Example #27
0
def test_max_pure_profile():
    """Test max_pure_prof"""
    profiles = [[2, 0],
                [1, 1],
                [0, 2]]
    payoffs = [[3, 0],
               [4, 4],
               [0, 1]]
    game = paygame.game(2, 2, profiles, payoffs)
    prof = regret.max_pure_social_welfare(game)[1]
    assert np.all(prof == [1, 1])

    game = rsgame.empty(2, 2)
    welfare, prof = regret.max_pure_social_welfare(game)
    assert np.isnan(welfare)
    assert prof is None

    (welfare,), (prof,) = regret.max_pure_social_welfare(game, by_role=True)
    assert np.isnan(welfare)
    assert prof is None
Example #28
0
def sym_2p2s_game(a, b, c, d, distribution=default_distribution): # pylint: disable=invalid-name
    """Create a symmetric 2-player 2-strategy game of the specified form.

    Four payoff values get drawn from U(min_val, max_val), and then are
    assigned to profiles in order from smallest to largest according to the
    order parameters as follows:

    +---+-----+-----+
    |   | s0  | s1  |
    +---+-----+-----+
    |s0 | a,a | b,c |
    +---+-----+-----+
    |s1 | c,b | d,d |
    +---+-----+-----+

    distribution must accept a size parameter a la numpy distributions.
    """
    utils.check({a, b, c, d} == set(range(4)), 'numbers must be each of 1-4')
    # Generate payoffs
    payoffs = distribution(4)
    payoffs.sort()
    profs = [[2, 0], [1, 1], [0, 2]]
    pays = [[payoffs[a], 0], [payoffs[b], payoffs[c]], [0, payoffs[d]]]
    return paygame.game(2, 2, profs, pays)
Example #29
0
def test_analysis_equilibria():
    """Test analysis with equilibria"""
    profiles = [
        # Complete deviations but unexplored
        [4, 0, 0, 0, 0],
        [3, 1, 0, 0, 0],
        [3, 0, 1, 0, 0],
        [3, 0, 0, 1, 0],
        [3, 0, 0, 0, 1],
        # Deviating restriction also explored
        [0, 4, 0, 0, 0],
        [0, 3, 1, 0, 0],
        [0, 2, 2, 0, 0],
        [0, 1, 3, 0, 0],
        [0, 0, 4, 0, 0],
        # Deviations
        [1, 3, 0, 0, 0],
        [1, 2, 1, 0, 0],
        [1, 1, 2, 0, 0],
        [1, 0, 3, 0, 0],
        [0, 3, 0, 1, 0],
        [0, 2, 1, 1, 0],
        [0, 1, 2, 1, 0],
        [0, 0, 3, 1, 0],
        [0, 3, 0, 0, 1],
        [0, 2, 1, 0, 1],
        [0, 1, 2, 0, 1],
        [0, 0, 3, 0, 1],
        # Deviating restriction
        [0, 2, 0, 2, 0],
        [0, 1, 0, 3, 0],
        [0, 0, 0, 4, 0],
    ]
    payoffs = [
        # Complete deviations but unexplored
        [4, 0, 0, 0, 0],
        [4, 1, 0, 0, 0],
        [4, 0, 1, 0, 0],
        [4, 0, 0, 1, 0],
        [4, 0, 0, 0, 0],
        # Deviating restriction also explored
        [0, 1, 0, 0, 0],
        [0, 1, 4, 0, 0],
        [0, 1, 4, 0, 0],
        [0, 1, 4, 0, 0],
        [0, 0, 4, 0, 0],
        # Deviations
        [1, 3, 0, 0, 0],
        [1, 2, 1, 0, 0],
        [1, 1, 2, 0, 0],
        [1, 0, 3, 0, 0],
        [0, 3, 0, 5, 0],
        [0, 2, 1, 5, 0],
        [0, 1, 2, 5, 0],
        [0, 0, 3, 5, 0],
        [0, 3, 0, 0, 0],
        [0, 2, 1, 0, 0],
        [0, 1, 2, 0, 0],
        [0, 0, 3, 0, 0],
        # Deviating restriction
        [0, 2, 0, 2, 0],
        [0, 1, 0, 3, 0],
        [0, 0, 0, 4, 0],
    ]
    game = paygame.game([4], [5], profiles, payoffs)
    game_str = json.dumps(game.to_json())

    with stdin(game_str), stdout() as out, stderr() as err:
        assert run('analyze', '-sd'), err.getvalue()
    out = out.getvalue()
    assert 'Found 1 dominated strategy' in out
    assert 'Found 1 unconfirmed candidate' in out
    assert 'Found 1 unexplored best-response restricted game' in out