diff --git a/tests/games.py b/tests/games.py index deb1eb580..450282a52 100644 --- a/tests/games.py +++ b/tests/games.py @@ -5,7 +5,6 @@ from abc import ABC, abstractmethod import numpy as np -import pytest import pygambit as gbt @@ -39,65 +38,6 @@ def create_efg_corresponding_to_bimatrix_game( return g -################################################################################################ -# Normal-form (aka strategic-form) games (nfg) - - -def create_2x2_zero_nfg() -> gbt.Game: - """ - Returns - ------- - Game - 2x2 all-zero-payoffs bimatrix, with player names and a duplicate label set intentionally - for testing purposes - """ - game = gbt.Game.new_table([2, 2]) - - game.players[0].label = "Joe" - game.players["Joe"].strategies[0].label = "cooperate" - game.players["Joe"].strategies[1].label = "defect" - - game.players[1].label = "Dan" - game.players["Dan"].strategies[0].label = "defect" - # intentional duplicate label for player - with pytest.warns(FutureWarning): - game.players["Dan"].strategies[1].label = "defect" - - return game - - -def create_2x2x2_nfg() -> gbt.Game: - """ - - This comes from a local max cut instance: - players {1,2,3} are nodes; edge weight{1,2} = 2; weight{1,3} = -1; weight{2,3} = 2 - - Pure strategies {a,b} encode if respective player is on left or right of the cut - - The payoff to a player is the sum of their incident edges across the implied cut - - Pure equilibrium iff local max cuts; in addition, uniform mixture is an equilibrium - - Equilibrium analysis for pure profiles: - a a a: 0 0 0 -- Not Nash (regrets: 1, 4, 1) - b a a: 1 2 -1 -- Not Nash (regrets: 0, 0, 3) - a b a: 2 4 2 -- Nash (global max cut) - b b a: -1 2 1 -- Not Nash (regrets: 3, 0, 0) - a a b: -1 2 1 -- Not Nash (regrets: 3, 0, 0) - b a b: 2 4 2 -- Nash (global max cut) - a b b: 1 2 -1 -- Not Nash (regrets: 0, 0, 3) - b b b: 0 0 0 -- Not Nash (regrets: 1, 4, 1) - """ - return read_from_file("2x2x2_nfg_with_two_pure_one_mixed_eq.nfg") - - -def create_coord_4x4_nfg(outcome_version: bool = False) -> gbt.Game: - """ - Returns - ------- - Game - 4x4 coordination game, either via reading in a payoff version nfg, or an - outcome version nfg, which has strategy labels useful for testing - """ - version = "outcome" if outcome_version else "payoff" - return read_from_file(f"coordination_4x4_{version}.nfg") - - ################################################################################################ # Extensive-form games (efg) @@ -105,255 +45,16 @@ def create_coord_4x4_nfg(outcome_version: bool = False) -> gbt.Game: def create_2x2_zero_sum_efg(missing_term_outcome: bool = False) -> gbt.Game: """ EFG corresponding to 2x2 zero-sum game (I,-I). - If missing_term_outcome, the terminal node after "T" then "r" does not have an outcome. - """ - g = gbt.Game.new_tree( - players=["Alice", "Bob"], title="2x2 matrix games (I,-I)") - g.append_move(g.root, "Alice", ["T", "B"]) - g.append_move(g.root.children, "Bob", ["l", "r"]) - - alice_win = g.add_outcome([1, -1], label="Alice win") - draw = g.add_outcome([0, 0], label="Draw") - - g.set_outcome(g.root.children["T"].children["l"], alice_win) - g.set_outcome(g.root.children["B"].children["r"], alice_win) - g.set_outcome(g.root.children["B"].children["l"], draw) - - if not missing_term_outcome: - g.set_outcome(g.root.children["T"].children["r"], draw) - - return g - - -def create_perfect_info_with_chance_efg() -> gbt.Game: - # Tests case in which sequence profile probabilities don't sum to 1 - g = gbt.Game.new_tree(players=["1", "2"], title="2 player perfect info with chance") - g.append_move(g.root, "1", ["a", "b"]) - g.append_move(g.root.children[0], g.players.chance, ["L", "R"]) - g.append_move(g.root.children[0].children[0], "2", ["A", "B"]) - g.append_move(g.root.children[0].children[1], "2", ["C", "D"]) - g.set_outcome( - g.root.children[0].children[0].children[0], g.add_outcome([-2, 2], label="aLA") - ) - g.set_outcome( - g.root.children[0].children[0].children[1], g.add_outcome([-2, 2], label="aLB") - ) - g.set_outcome( - g.root.children[0].children[1].children[0], g.add_outcome([-2, 2], label="aRC") - ) - g.set_outcome( - g.root.children[0].children[1].children[1], g.add_outcome([-2, 2], label="aRD") - ) - g.set_outcome(g.root.children[1], g.add_outcome([-1, 1], label="b")) - return g - - -def create_three_action_internal_outcomes_efg(nonterm_outcomes: bool = False) -> gbt.Game: - """ - with nonterm_outcomes there are nonterminal outcomes, and missing outcomes at some leaves - """ - g = gbt.Game.new_tree(players=["1", "2"], title="") - g.append_move(g.root, g.players.chance, ["H", "L"]) - for i in range(2): - g.append_move(g.root.children[i], "1", ["A", "B", "C"]) - for i in range(3): - g.append_move(g.root.children[0].children[i], "2", ["X", "Y"]) - g.append_infoset(g.root.children[1].children[i], g.root.children[0].children[i].infoset) - o_1 = g.add_outcome([1, -1], label="1") - o_m1 = g.add_outcome([-1, 1], label="-1") - o_2 = g.add_outcome([2, -2], label="2") - o_m2 = g.add_outcome([-2, 2], label="-2") - o_z = g.add_outcome([0, 0], label="0") - if nonterm_outcomes: - g.set_outcome(g.root.children[0].children[0], o_1) - g.set_outcome(g.root.children[1].children[2], o_m1) - g.set_outcome(g.root.children[0].children[0].children[1], o_m2) - g.set_outcome(g.root.children[0].children[1].children[0], o_m1) - g.set_outcome(g.root.children[0].children[1].children[1], o_1) - g.set_outcome(g.root.children[0].children[2].children[0], o_1) - g.set_outcome(g.root.children[1].children[0].children[1], o_1) - g.set_outcome(g.root.children[1].children[1].children[0], o_1) - g.set_outcome(g.root.children[1].children[1].children[1], o_m1) - g.set_outcome(g.root.children[1].children[2].children[1], o_2) - else: - g.set_outcome(g.root.children[0].children[0].children[0], o_1) - g.set_outcome(g.root.children[0].children[0].children[1], o_m1) - g.set_outcome(g.root.children[0].children[1].children[0], o_m1) - g.set_outcome(g.root.children[0].children[1].children[1], o_1) - g.set_outcome(g.root.children[0].children[2].children[0], o_1) - g.set_outcome(g.root.children[0].children[2].children[1], o_z) - - g.set_outcome(g.root.children[1].children[0].children[0], o_z) - g.set_outcome(g.root.children[1].children[0].children[1], o_1) - g.set_outcome(g.root.children[1].children[1].children[0], o_1) - g.set_outcome(g.root.children[1].children[1].children[1], o_m1) - g.set_outcome(g.root.children[1].children[2].children[0], o_m1) - g.set_outcome(g.root.children[1].children[2].children[1], o_1) - return g - - -def create_entry_accomodation_efg(nonterm_outcomes: bool = False) -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2"], - title="Entry-accomodation game") - g.append_move(g.root, "1", ["S", "T"]) - g.append_move(g.root.children[0], "2", ["E", "O"]) - g.append_infoset(g.root.children[1], g.root.children[0].infoset) - g.append_move(g.root.children[0].children[0], "1", ["A", "F"]) - g.append_move(g.root.children[1].children[0], "1", ["A", "F"]) - if nonterm_outcomes: - g.set_outcome(g.root.children[0], g.add_outcome([3, 2])) - g.set_outcome(g.root.children[0].children[0].children[1], g.add_outcome([-3, -1])) - g.set_outcome(g.root.children[0].children[1], g.add_outcome([-2, 1])) - else: - g.set_outcome(g.root.children[0].children[0].children[0], g.add_outcome([3, 2])) - g.set_outcome(g.root.children[0].children[0].children[1], g.add_outcome([0, 1])) - g.set_outcome(g.root.children[0].children[1], g.add_outcome([1, 3])) - g.set_outcome(g.root.children[1].children[0].children[0], g.add_outcome([2, 3])) - g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([1, 0])) - g.set_outcome(g.root.children[1].children[1], g.add_outcome([3, 1])) - return g - - -def create_non_zero_sum_lacking_outcome_efg(missing_term_outcome: bool = False) -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2"], title="Non constant-sum game lacking outcome") - g.append_move(g.root, g.players.chance, ["H", "T"]) - g.set_chance_probs(g.root.infoset, ["1/2", "1/2"]) - g.append_move(g.root.children[0], "1", ["A", "B"]) - g.append_infoset(g.root.children[1], g.root.children[0].infoset) - g.append_move(g.root.children[0].children[0], "2", ["X", "Y"]) - g.append_infoset(g.root.children[0].children[1], g.root.children[0].children[0].infoset) - g.append_infoset(g.root.children[1].children[0], g.root.children[0].children[0].infoset) - g.append_infoset(g.root.children[1].children[1], g.root.children[0].children[0].infoset) - g.set_outcome(g.root.children[0].children[0].children[0], g.add_outcome([2, 1])) - g.set_outcome(g.root.children[0].children[0].children[1], g.add_outcome([-1, 2])) - g.set_outcome(g.root.children[0].children[1].children[0], g.add_outcome([1, -1])) - if not missing_term_outcome: - g.set_outcome(g.root.children[0].children[1].children[1], g.add_outcome([0, 0])) - g.set_outcome(g.root.children[1].children[0].children[0], g.add_outcome([1, 0])) - g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([0, 1])) - g.set_outcome(g.root.children[1].children[1].children[0], g.add_outcome([-1, 1])) - g.set_outcome(g.root.children[1].children[1].children[1], g.add_outcome([2, -1])) - return g - - -def create_chance_in_middle_efg(nonterm_outcomes: bool = False) -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2"], - title="Chance in middle game") - g.append_move(g.root, "1", ["A", "B"]) - g.append_move(g.root.children[0], g.players.chance, ["H", "L"]) - g.set_chance_probs(g.root.children[0].infoset, ["1/5", "4/5"]) - g.append_move(g.root.children[1], g.players.chance, ["H", "L"]) - g.set_chance_probs(g.root.children[1].infoset, ["7/10", "3/10"]) - for i in range(2): - g.append_move(g.root.children[0].children[i], "2", ["X", "Y"]) - ist = g.root.children[0].children[i].infoset - g.append_infoset(g.root.children[1].children[i], ist) - for i in range(2): - for j in range(2): - g.append_move(g.root.children[i].children[0].children[j], "1", ["C", "D"]) - ist = g.root.children[i].children[0].children[j].infoset - g.append_infoset(g.root.children[i].children[1].children[j], ist) - o_1 = g.add_outcome([1, -1], label="1") - o_m1 = g.add_outcome([-1, 1], label="-1") - o_m2 = g.add_outcome([-2, 2], label="-2") - o_h = g.add_outcome(["1/2", "-1/2"], label="0.5") - o_mh = g.add_outcome(["-1/2", "1/2"], label="-0.5") - o_z = g.add_outcome([0, 0], label="0") - o_m3o2 = g.add_outcome(["-3/2", "3/2"], label="-1.5") - if nonterm_outcomes: - g.set_outcome(g.root.children[0].children[0], g.add_outcome([-1, 1], label="a")) - g.set_outcome(g.root.children[0].children[0].children[0].children[0], o_1) - g.set_outcome(g.root.children[0].children[0].children[0].children[1], o_m1) - g.set_outcome(g.root.children[0].children[0].children[1].children[0], o_h) - g.set_outcome(g.root.children[0].children[0].children[1].children[1], o_mh) - else: - g.set_outcome(g.root.children[0].children[0].children[0].children[0], o_z) - g.set_outcome(g.root.children[0].children[0].children[0].children[1], o_m2) - g.set_outcome(g.root.children[0].children[0].children[1].children[0], o_mh) - g.set_outcome(g.root.children[0].children[0].children[1].children[1], o_m3o2) - g.set_outcome(g.root.children[0].children[1].children[0].children[0], o_h) - g.set_outcome(g.root.children[0].children[1].children[0].children[1], o_mh) - g.set_outcome(g.root.children[0].children[1].children[1].children[0], o_1) - g.set_outcome(g.root.children[0].children[1].children[1].children[1], o_m1) - g.set_outcome(g.root.children[1].children[0].children[0].children[0], o_h) - g.set_outcome(g.root.children[1].children[0].children[0].children[1], o_mh) - g.set_outcome(g.root.children[1].children[0].children[1].children[0], o_1) - g.set_outcome(g.root.children[1].children[0].children[1].children[1], o_m1) - g.set_outcome(g.root.children[1].children[1].children[0].children[0], o_1) - g.set_outcome(g.root.children[1].children[1].children[0].children[1], o_m1) - g.set_outcome(g.root.children[1].children[1].children[1].children[0], o_h) - g.set_outcome(g.root.children[1].children[1].children[1].children[1], o_mh) - return g - - -def create_large_payoff_game_efg() -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2"], title="Large payoff game") - g.append_move(g.root, g.players.chance, ["L", "R"]) - for i in range(2): - g.append_move(g.root.children[i], "1", ["A", "B"]) - for i in range(2): - g.append_move(g.root.children[0].children[i], "2", ["X", "Y"]) - g.append_infoset(g.root.children[1].children[i], g.root.children[0].children[i].infoset) - o_large = g.add_outcome([10000000000000000000, -10000000000000000000], label="large payoff") - o_1 = g.add_outcome([1, -1], label="1") - o_m1 = g.add_outcome([-1, 1], label="-1") - o_zero = g.add_outcome([0, 0], label="0") - g.set_outcome(g.root.children[0].children[0].children[0], o_large) - g.set_outcome(g.root.children[0].children[0].children[1], o_1) - g.set_outcome(g.root.children[0].children[1].children[0], o_m1) - g.set_outcome(g.root.children[0].children[1].children[1], o_zero) - g.set_outcome(g.root.children[1].children[0].children[0], o_m1) - g.set_outcome(g.root.children[1].children[0].children[1], o_1) - g.set_outcome(g.root.children[1].children[1].children[0], o_zero) - g.set_outcome(g.root.children[1].children[1].children[1], o_large) - return g - - -def create_3_player_with_internal_outcomes_efg(nonterm_outcomes: bool = False) -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2", "3"], title="3 player game") - g.append_move(g.root, g.players.chance, ["H", "T"]) - g.set_chance_probs(g.root.infoset, ["1/2", "1/2"]) - g.append_move(g.root.children[0], "1", ["a", "b"]) - g.append_move(g.root.children[1], "1", ["c", "d"]) - g.append_move(g.root.children[0].children[0], "2", ["A", "B"]) - g.append_infoset(g.root.children[1].children[0], g.root.children[0].children[0].infoset) - g.append_move(g.root.children[0].children[1], "3", ["W", "X"]) - g.append_infoset(g.root.children[1].children[1], g.root.children[0].children[1].infoset) - g.append_move(g.root.children[0].children[0].children[0], "3", ["Y", "Z"]) - iset = g.root.children[0].children[0].children[0].infoset - g.append_infoset(g.root.children[0].children[0].children[1], iset) - g.append_move(g.root.children[0].children[1].children[1], "2", ["C", "D"]) - o = g.add_outcome([3, 1, 4]) - g.set_outcome(g.root.children[0].children[0].children[0].children[0], o) - o = g.add_outcome([4, 0, 1]) - g.set_outcome(g.root.children[0].children[0].children[0].children[1], o) - o = g.add_outcome([1, 3, 2]) - g.set_outcome(g.root.children[0].children[1].children[0], o) - o = g.add_outcome([2, 4, 1]) - g.set_outcome(g.root.children[0].children[1].children[1].children[0], o) - o = g.add_outcome([4, 1, 3]) - g.set_outcome(g.root.children[0].children[1].children[1].children[1], o) - if nonterm_outcomes: - o = g.add_outcome([1, 2, 3]) - g.set_outcome(g.root.children[1], o) - o = g.add_outcome([1, 0, 1]) - g.set_outcome(g.root.children[1].children[0].children[0], o) - o = g.add_outcome([2, -1, -2]) - g.set_outcome(g.root.children[1].children[0].children[1], o) - o = g.add_outcome([-1, 2, -1]) - g.set_outcome(g.root.children[1].children[1].children[0], o) - else: - o = g.add_outcome([2, 2, 4]) - g.set_outcome(g.root.children[1].children[0].children[0], o) - o = g.add_outcome([3, 1, 1]) - g.set_outcome(g.root.children[1].children[0].children[1], o) - o = g.add_outcome([0, 4, 2]) - g.set_outcome(g.root.children[1].children[1].children[0], o) - o = g.add_outcome([1, 2, 3]) - g.set_outcome(g.root.children[1].children[1].children[1], o) - o = g.add_outcome([0, 0, 0]) - g.set_outcome(g.root.children[0].children[0].children[1].children[0], o) - g.set_outcome(g.root.children[0].children[0].children[1].children[1], o) + If missing_term_outcome, the terminal node after action 0 then 1 does not have an outcome. + """ + title = "EFG for 2x2 zero-sum game (I,-I)" + if missing_term_outcome: + title += " with missing terminal outcome" + A = np.eye(2) + B = -A + g = create_efg_corresponding_to_bimatrix_game(A, B, title) + if missing_term_outcome: + g.delete_outcome(g.root.children[0].children[1].outcome) return g @@ -361,35 +62,18 @@ def create_matching_pennies_efg(with_neutral_outcome: bool = False) -> gbt.Game: """ The version with_neutral_outcome adds a (0,0) payoff outcomes at a non-terminal node. """ - g = gbt.Game.new_tree( - players=["Alice", "Bob"], title="Matching pennies") - g.append_move(g.root, "Alice", ["H", "T"]) - g.append_move(g.root.children, "Bob", ["h", "t"]) - alice_lose = g.add_outcome([-1, 1], label="Alice lose") - alice_win = g.add_outcome([1, -1], label="Alice win") + title = "Matching Pennies" + if with_neutral_outcome: + title += " with nonterminal neutral outcome" + A = np.array([[1, -1], [-1, 1]]) + B = -A + g = create_efg_corresponding_to_bimatrix_game(A, B, title) if with_neutral_outcome: neutral = g.add_outcome([0, 0], label="neutral") - g.set_outcome(g.root.children["H"], neutral) - g.set_outcome(g.root.children["H"].children["h"], alice_win) - g.set_outcome(g.root.children["T"].children["t"], alice_win) - g.set_outcome(g.root.children["H"].children["t"], alice_lose) - g.set_outcome(g.root.children["T"].children["h"], alice_lose) + g.set_outcome(g.root.children[0], neutral) return g -def create_mixed_behav_game_efg() -> gbt.Game: - """ - Returns - ------- - Game - Three-player extensive form game: binary tree with 3 infomation sets, one per player, - with 1, 2, and 4 nodes respectively - - Since no information is revealed this is directly equivalent to a simultaneous move game - """ - return read_from_file("mixed_behavior_game.efg") - - def create_stripped_down_poker_efg(nonterm_outcomes: bool = False) -> gbt.Game: """ Returns @@ -689,6 +373,8 @@ def kuhn_poker_lcp_first_mixed_strategy_prof(): def create_one_shot_trust_efg(unique_NE_variant: bool = False) -> gbt.Game: """ + TODO: this could be replaced with two .efg files + One-shot trust game, after Kreps (1990) The unique_NE_variant makes Trust a dominant strategy, replacing the @@ -717,347 +403,6 @@ def create_one_shot_trust_efg(unique_NE_variant: bool = False) -> gbt.Game: return g -def create_centipede_game_with_chance_efg() -> gbt.Game: - """ - Returns - ------- - Game - 2-player Centipede Game with 3 innings and a probability of altruism - """ - return read_from_file("cent3.efg") - - -def create_el_farol_bar_game_efg() -> gbt.Game: - """ - Returns - ------- - Game - 5-player El Farol Bar Game - """ - return read_from_file("el_farol_bar.efg") - - -def create_selten_horse_game_efg() -> gbt.Game: - """ - Returns - ------- - Game - 5-player Selten's Horse Game - """ - return read_from_file("e01.efg") - - -def create_reduction_generic_payoffs_efg() -> gbt.Game: - # tree with only root - g = gbt.Game.new_tree( - players=["1", "2"], title="2 player reduction generic payoffs" - ) - - # add four children - g.append_move(g.root, "2", ["a", "b", "c", "d"]) - - # add L and R after a - g.append_move(g.root.children[0], "1", ["L", "R"]) - - # add C and D to single infoset after b and c - nodes = [g.root.children[1], g.root.children[2]] - g.append_move(nodes, "1", ["C", "D"]) - - # add s and t from single infoset after rightmost C and D - g.append_move(g.root.children[2].children, "2", ["s", "t"]) - - # add p and q - g.append_move(g.root.children[0].children[1], "2", ["p", "q"]) - - # add U and V in a single infoset after p and q - g.append_move(g.root.children[0].children[1].children, "1", ["U", "V"]) - - # Set outcomes - - g.set_outcome(g.root.children[0].children[0], g.add_outcome([1, -1], label="aL")) - g.set_outcome( - g.root.children[0].children[1].children[0].children[0], - g.add_outcome([2, -2], label="aRpU"), - ) - g.set_outcome( - g.root.children[0].children[1].children[0].children[1], - g.add_outcome([3, -3], label="aRpV"), - ) - g.set_outcome( - g.root.children[0].children[1].children[1].children[0], - g.add_outcome([4, -4], label="aRqU"), - ) - g.set_outcome( - g.root.children[0].children[1].children[1].children[1], - g.add_outcome([5, -5], label="aRqV"), - ) - - g.set_outcome(g.root.children[1].children[0], g.add_outcome([6, -6], label="bC")) - g.set_outcome(g.root.children[1].children[1], g.add_outcome([7, -7], label="bD")) - - g.set_outcome( - g.root.children[2].children[0].children[0], g.add_outcome([8, -8], label="cCs") - ) - g.set_outcome( - g.root.children[2].children[0].children[1], g.add_outcome([9, -9], label="cCt") - ) - g.set_outcome( - g.root.children[2].children[1].children[0], - g.add_outcome([10, -10], label="cDs"), - ) - g.set_outcome( - g.root.children[2].children[1].children[1], - g.add_outcome([11, -11], label="cDt"), - ) - - g.set_outcome(g.root.children[3], g.add_outcome([12, -12], label="d")) - return g - - -def create_reduction_one_player_generic_payoffs_efg() -> gbt.Game: - g = gbt.Game.new_tree(players=["1"], title="One player reduction generic payoffs") - g.append_move(g.root, "1", ["a", "b", "c", "d"]) - g.append_move(g.root.children[0], "1", ["e", "f"]) - g.set_outcome(g.root.children[0].children[0], g.add_outcome([1])) - g.set_outcome(g.root.children[0].children[1], g.add_outcome([2])) - g.set_outcome(g.root.children[1], g.add_outcome([3])) - g.set_outcome(g.root.children[2], g.add_outcome([4])) - g.set_outcome(g.root.children[3], g.add_outcome([5])) - return g - - -def create_reduction_both_players_payoff_ties_efg() -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2"], title="From GTE survey") - g.append_move(g.root, "1", ["A", "B", "C", "D"]) - g.append_move(g.root.children[0], "2", ["a", "b"]) - g.append_move(g.root.children[1], "2", ["c", "d"]) - g.append_move(g.root.children[2], "2", ["e", "f"]) - g.append_move(g.root.children[0].children[1], "2", ["g", "h"]) - g.append_move(g.root.children[2].children, "1", ["E", "F"]) - - g.set_outcome(g.root.children[0].children[0], g.add_outcome([2, 8])) - g.set_outcome(g.root.children[0].children[1].children[0], g.add_outcome([0, 1])) - g.set_outcome(g.root.children[0].children[1].children[1], g.add_outcome([5, 2])) - g.set_outcome(g.root.children[1].children[0], g.add_outcome([7, 6])) - g.set_outcome(g.root.children[1].children[1], g.add_outcome([4, 2])) - g.set_outcome(g.root.children[2].children[0].children[0], g.add_outcome([3, 7])) - g.set_outcome(g.root.children[2].children[0].children[1], g.add_outcome([8, 3])) - g.set_outcome(g.root.children[2].children[1].children[0], g.add_outcome([7, 8])) - g.set_outcome(g.root.children[2].children[1].children[1], g.add_outcome([2, 2])) - g.set_outcome(g.root.children[3], g.add_outcome([6, 4])) - return g - - -def create_problem_example_efg() -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2"], title="") - g.append_move(g.root, player="1", actions=["L", "R"]) - # do the second child first on purpose to diverge from sort infosets order - g.append_move(g.root.children[1], "2", actions=["l2", "r2"]) - g.append_move(g.root.children[0], "2", actions=["l1", "r1"]) - g.set_outcome(g.root.children[0].children[0], outcome=g.add_outcome(payoffs=[5, -5])) - g.set_outcome(g.root.children[0].children[1], outcome=g.add_outcome(payoffs=[2, -2])) - g.set_outcome(g.root.children[1].children[0], outcome=g.add_outcome(payoffs=[-5, 5])) - g.set_outcome(g.root.children[1].children[1], outcome=g.add_outcome(payoffs=[-2, 2])) - return g - - -def create_STOC_simplified() -> gbt.Game: - """ - """ - g = gbt.Game.new_tree(players=["1", "2"], title="") - g.append_move(g.root, g.players.chance, actions=["1", "2"]) - g.set_chance_probs(g.root.infoset, [0.2, 0.8]) - g.append_move(g.root.children[0], player="1", actions=["l", "r"]) - g.append_move(g.root.children[1], player="1", actions=["c", "d"]) - g.append_move(g.root.children[0].children[1], player="2", actions=["p", "q"]) - g.append_move( - g.root.children[0].children[1].children[0], player="1", actions=["L", "R"] - ) - g.append_infoset( - g.root.children[0].children[1].children[1], - g.root.children[0].children[1].children[0].infoset, - ) - g.set_outcome( - g.root.children[0].children[0], - outcome=g.add_outcome(payoffs=[5, -5], label="l"), - ) - g.set_outcome( - g.root.children[0].children[1].children[0].children[0], - outcome=g.add_outcome(payoffs=[10, -10], label="rpL"), - ) - g.set_outcome( - g.root.children[0].children[1].children[0].children[1], - outcome=g.add_outcome(payoffs=[15, -15], label="rpR"), - ) - g.set_outcome( - g.root.children[0].children[1].children[1].children[0], - outcome=g.add_outcome(payoffs=[20, -20], label="rqL"), - ) - g.set_outcome( - g.root.children[0].children[1].children[1].children[1], - outcome=g.add_outcome(payoffs=[-5, 5], label="rqR"), - ) - g.set_outcome( - g.root.children[1].children[0], - outcome=g.add_outcome(payoffs=[10, -10], label="c"), - ) - g.set_outcome( - g.root.children[1].children[1], - outcome=g.add_outcome(payoffs=[20, -20], label="d"), - ) - return g - - -def create_STOC_simplified2() -> gbt.Game: - """ - """ - g = gbt.Game.new_tree(players=["1", "2"], title="") - g.append_move(g.root, g.players.chance, actions=["1", "2"]) - g.set_chance_probs(g.root.infoset, [0.2, 0.8]) - g.append_move(g.root.children[0], player="1", actions=["r"]) - g.append_move(g.root.children[1], player="1", actions=["c"]) - g.append_move(g.root.children[0].children[0], player="2", actions=["p", "q"]) - g.append_move( - g.root.children[0].children[0].children[0], player="1", actions=["L", "R"] - ) - g.append_infoset( - g.root.children[0].children[0].children[1], - g.root.children[0].children[0].children[0].infoset, - ) - g.set_outcome( - g.root.children[0].children[0].children[0].children[0], - outcome=g.add_outcome(payoffs=[10, -10], label="rpL"), - ) - g.set_outcome( - g.root.children[0].children[0].children[0].children[1], - outcome=g.add_outcome(payoffs=[15, -15], label="rpR"), - ) - g.set_outcome( - g.root.children[0].children[0].children[1].children[0], - outcome=g.add_outcome(payoffs=[20, -20], label="rqL"), - ) - g.set_outcome( - g.root.children[0].children[0].children[1].children[1], - outcome=g.add_outcome(payoffs=[-5, 5], label="rqR"), - ) - g.set_outcome( - g.root.children[1].children[0], - outcome=g.add_outcome(payoffs=[10, -10], label="c"), - ) - return g - - -def create_seq_form_STOC_paper_zero_sum_2_player_efg() -> gbt.Game: - """ - Example from - - Fast Algorithms for Finding Randomized Strategies in Game Trees (1994) - Koller, Megiddo, von Stengel - """ - g = gbt.Game.new_tree(players=["1", "2"], title="From STOC'94 paper") - g.append_move(g.root, g.players.chance, actions=["1", "2", "3", "4"]) - g.set_chance_probs(g.root.infoset, [0.2, 0.2, 0.2, 0.4]) - g.append_move(g.root.children[0], player="1", actions=["l", "r"]) - g.append_move(g.root.children[1], player="1", actions=["c", "d"]) - g.append_infoset(g.root.children[2], g.root.children[1].infoset) - g.append_move(g.root.children[0].children[1], player="2", actions=["p", "q"]) - g.append_move( - g.root.children[0].children[1].children[0], player="1", actions=["L", "R"] - ) - g.append_infoset( - g.root.children[0].children[1].children[1], - g.root.children[0].children[1].children[0].infoset, - ) - g.append_move(g.root.children[2].children[0], player="2", actions=["s", "t"]) - g.append_infoset( - g.root.children[2].children[1], g.root.children[2].children[0].infoset - ) - - g.set_outcome( - g.root.children[0].children[0], - outcome=g.add_outcome(payoffs=[5, -5], label="l"), - ) - g.set_outcome( - g.root.children[0].children[1].children[0].children[0], - outcome=g.add_outcome(payoffs=[10, -10], label="rpL"), - ) - g.set_outcome( - g.root.children[0].children[1].children[0].children[1], - outcome=g.add_outcome(payoffs=[15, -15], label="rpR"), - ) - g.set_outcome( - g.root.children[0].children[1].children[1].children[0], - outcome=g.add_outcome(payoffs=[20, -20], label="rqL"), - ) - g.set_outcome( - g.root.children[0].children[1].children[1].children[1], - outcome=g.add_outcome(payoffs=[-5, 5], label="rqR"), - ) - g.set_outcome( - g.root.children[1].children[0], - outcome=g.add_outcome(payoffs=[10, -10], label="c"), - ) - g.set_outcome( - g.root.children[1].children[1], - outcome=g.add_outcome(payoffs=[20, -20], label="d"), - ) - g.set_outcome( - g.root.children[2].children[0].children[0], - outcome=g.add_outcome(payoffs=[20, -20], label="cs"), - ) - g.set_outcome( - g.root.children[2].children[0].children[1], - outcome=g.add_outcome(payoffs=[50, -50], label="ct"), - ) - g.set_outcome( - g.root.children[2].children[1].children[0], - outcome=g.add_outcome(payoffs=[30, -30], label="ds"), - ) - g.set_outcome( - g.root.children[2].children[1].children[1], - outcome=g.add_outcome(payoffs=[15, -15], label="dt"), - ) - g.set_outcome( - g.root.children[3], outcome=g.add_outcome(payoffs=[5, -5], label="nothing") - ) - g.root.children[0].infoset.label = "0" - g.root.children[1].infoset.label = "1" - g.root.children[0].children[1].infoset.label = "01" - g.root.children[2].children[0].infoset.label = "20" - g.root.children[0].children[1].children[0].infoset.label = "010" - return g - - -def create_two_player_perfect_info_win_lose_efg(nonterm_outcomes: bool = False) -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2"], title="2 player perfect info win lose") - g.append_move(g.root, "2", ["a", "b"]) - g.append_move(g.root.children[0], "1", ["L", "R"]) - g.append_move(g.root.children[1], "1", ["L", "R"]) - g.append_move(g.root.children[0].children[0], "2", ["l", "r"]) - if not nonterm_outcomes: - g.set_outcome( - g.root.children[0].children[0].children[0], g.add_outcome([1, -1], label="aLl") - ) - g.set_outcome( - g.root.children[0].children[0].children[1], g.add_outcome([-1, 1], label="aLr") - ) - g.set_outcome(g.root.children[0].children[1], g.add_outcome([1, -1], label="aR")) - g.set_outcome(g.root.children[1].children[0], g.add_outcome([1, -1], label="bL")) - g.set_outcome(g.root.children[1].children[1], g.add_outcome([-1, 1], label="bR")) - else: - g.set_outcome(g.root.children[0], g.add_outcome([-100, 50], label="a")) - g.set_outcome( - g.root.children[0].children[0].children[0], g.add_outcome([101, -51], label="aLl") - ) - g.set_outcome( - g.root.children[0].children[0].children[1], g.add_outcome([99, -49], label="aLr") - ) - g.set_outcome(g.root.children[0].children[1], g.add_outcome([101, -51], label="aR")) - g.set_outcome(g.root.children[1].children[0], g.add_outcome([1, -1], label="bL")) - g.set_outcome(g.root.children[1].children[1], g.add_outcome([-1, 1], label="bR")) - return g - - def create_EFG_for_nxn_bimatrix_coordination_game(n: int) -> gbt.Game: A = np.eye(n, dtype=int) B = A diff --git a/tests/test_behav.py b/tests/test_behav.py index c3468ef0c..62ec7bbf3 100644 --- a/tests/test_behav.py +++ b/tests/test_behav.py @@ -22,20 +22,22 @@ def _set_action_probs(profile: gbt.MixedBehaviorProfile, probs: list, rational_f @pytest.mark.parametrize( "game,player_idx,payoff,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, 3.0, False), - (games.create_mixed_behav_game_efg(), 1, 3.0, False), - (games.create_mixed_behav_game_efg(), 2, 3.25, False), - (games.create_mixed_behav_game_efg(), 0, "3", True), - (games.create_mixed_behav_game_efg(), 1, "3", True), - (games.create_mixed_behav_game_efg(), 2, "13/4", True), - (games.create_stripped_down_poker_efg(), 0, -0.25, False), - (games.create_stripped_down_poker_efg(), 1, 0.25, True), - (games.create_stripped_down_poker_efg(), 0, "-1/4", True), - (games.create_stripped_down_poker_efg(), 1, "1/4", True) - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 3.25, False), + (games.read_from_file("mixed_behavior_game.efg"), 0, "3", True), + (games.read_from_file("mixed_behavior_game.efg"), 1, "3", True), + (games.read_from_file("mixed_behavior_game.efg"), 2, "13/4", True), + (games.create_stripped_down_poker_efg(), 0, -0.25, False), + (games.create_stripped_down_poker_efg(), 1, 0.25, True), + (games.create_stripped_down_poker_efg(), 0, "-1/4", True), + (games.create_stripped_down_poker_efg(), 1, "1/4", True), + ], ) -def test_payoff_reference(game: gbt.Game, player_idx: int, payoff: str | float, - rational_flag: bool): +def test_payoff_reference( + game: gbt.Game, player_idx: int, payoff: str | float, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) payoff = gbt.Rational(payoff) if rational_flag else payoff assert profile.payoff(game.players[player_idx]) == payoff @@ -43,20 +45,22 @@ def test_payoff_reference(game: gbt.Game, player_idx: int, payoff: str | float, @pytest.mark.parametrize( "game,label,payoff,rational_flag", - [(games.create_mixed_behav_game_efg(), "Player 1", 3.0, False), - (games.create_mixed_behav_game_efg(), "Player 2", 3.0, False), - (games.create_mixed_behav_game_efg(), "Player 3", 3.25, False), - (games.create_mixed_behav_game_efg(), "Player 1", "3", True), - (games.create_mixed_behav_game_efg(), "Player 2", "3", True), - (games.create_mixed_behav_game_efg(), "Player 3", "13/4", True), - (games.create_stripped_down_poker_efg(), "Alice", -0.25, False), - (games.create_stripped_down_poker_efg(), "Bob", 0.25, False), - (games.create_stripped_down_poker_efg(), "Alice", "-1/4", True), - (games.create_stripped_down_poker_efg(), "Bob", "1/4", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "Player 1", 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "Player 2", 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "Player 3", 3.25, False), + (games.read_from_file("mixed_behavior_game.efg"), "Player 1", "3", True), + (games.read_from_file("mixed_behavior_game.efg"), "Player 2", "3", True), + (games.read_from_file("mixed_behavior_game.efg"), "Player 3", "13/4", True), + (games.create_stripped_down_poker_efg(), "Alice", -0.25, False), + (games.create_stripped_down_poker_efg(), "Bob", 0.25, False), + (games.create_stripped_down_poker_efg(), "Alice", "-1/4", True), + (games.create_stripped_down_poker_efg(), "Bob", "1/4", True), + ], ) -def test_payoff_by_label_reference(game: gbt.Game, label: str, payoff: str | float, - rational_flag: bool): +def test_payoff_by_label_reference( + game: gbt.Game, label: str, payoff: str | float, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) payoff = gbt.Rational(payoff) if rational_flag else payoff assert profile.payoff(label) == payoff @@ -64,11 +68,12 @@ def test_payoff_by_label_reference(game: gbt.Game, label: str, payoff: str | flo @pytest.mark.parametrize( "game,rational_flag", - [(games.create_mixed_behav_game_efg(), False), - (games.create_mixed_behav_game_efg(), True), - (games.create_stripped_down_poker_efg(), False), - (games.create_stripped_down_poker_efg(), True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), False), + (games.read_from_file("mixed_behavior_game.efg"), True), + (games.create_stripped_down_poker_efg(), False), + (games.create_stripped_down_poker_efg(), True), + ], ) def test_is_defined_at(game: gbt.Game, rational_flag: bool): """Test to check if infoset are all defined""" @@ -79,19 +84,20 @@ def test_is_defined_at(game: gbt.Game, rational_flag: bool): @pytest.mark.parametrize( "game,label,rational_flag", - [(games.create_mixed_behav_game_efg(), "Infoset 1:1", False), - (games.create_mixed_behav_game_efg(), "Infoset 2:1", False), - (games.create_mixed_behav_game_efg(), "Infoset 3:1", False), - (games.create_mixed_behav_game_efg(), "Infoset 1:1", True), - (games.create_mixed_behav_game_efg(), "Infoset 2:1", True), - (games.create_mixed_behav_game_efg(), "Infoset 3:1", True), - (games.create_stripped_down_poker_efg(), "Alice has King", False), - (games.create_stripped_down_poker_efg(), "Alice has Queen", False), - (games.create_stripped_down_poker_efg(), "Bob's response", False), - (games.create_stripped_down_poker_efg(), "Alice has King", True), - (games.create_stripped_down_poker_efg(), "Alice has Queen", True), - (games.create_stripped_down_poker_efg(), "Bob's response", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 2:1", False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 3:1", False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", True), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 2:1", True), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 3:1", True), + (games.create_stripped_down_poker_efg(), "Alice has King", False), + (games.create_stripped_down_poker_efg(), "Alice has Queen", False), + (games.create_stripped_down_poker_efg(), "Bob's response", False), + (games.create_stripped_down_poker_efg(), "Alice has King", True), + (games.create_stripped_down_poker_efg(), "Alice has Queen", True), + (games.create_stripped_down_poker_efg(), "Bob's response", True), + ], ) def test_is_defined_at_by_label(game: gbt.Game, label: str, rational_flag: bool): """Test to check if an infoset is defined by string labels""" @@ -101,37 +107,41 @@ def test_is_defined_at_by_label(game: gbt.Game, label: str, rational_flag: bool) @pytest.mark.parametrize( "game,player_idx,infoset_idx,action_idx,prob,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, 0, 0, 0.5, False), - (games.create_mixed_behav_game_efg(), 0, 0, 1, 0.5, False), - (games.create_mixed_behav_game_efg(), 1, 0, 0, 0.5, False), - (games.create_mixed_behav_game_efg(), 1, 0, 1, 0.5, False), - (games.create_mixed_behav_game_efg(), 2, 0, 0, 0.5, False), - (games.create_mixed_behav_game_efg(), 2, 0, 1, 0.5, False), - (games.create_mixed_behav_game_efg(), 0, 0, 0, "1/2", True), - (games.create_mixed_behav_game_efg(), 0, 0, 1, "1/2", True), - (games.create_mixed_behav_game_efg(), 1, 0, 0, "1/2", True), - (games.create_mixed_behav_game_efg(), 1, 0, 1, "1/2", True), - (games.create_mixed_behav_game_efg(), 2, 0, 0, "1/2", True), - (games.create_mixed_behav_game_efg(), 2, 0, 1, "1/2", True), - (games.create_stripped_down_poker_efg(), 0, 0, 0, 0.5, False), - (games.create_stripped_down_poker_efg(), 0, 0, 1, 0.5, False), - (games.create_stripped_down_poker_efg(), 0, 1, 0, 0.5, False), - (games.create_stripped_down_poker_efg(), 0, 1, 1, 0.5, False), - (games.create_stripped_down_poker_efg(), 1, 0, 0, 0.5, False), - (games.create_stripped_down_poker_efg(), 1, 0, 1, 0.5, False), - (games.create_stripped_down_poker_efg(), 0, 0, 0, "1/2", True), - (games.create_stripped_down_poker_efg(), 0, 0, 1, "1/2", True), - (games.create_stripped_down_poker_efg(), 0, 1, 0, "1/2", True), - (games.create_stripped_down_poker_efg(), 0, 1, 1, "1/2", True), - (games.create_stripped_down_poker_efg(), 1, 0, 0, "1/2", True), - (games.create_stripped_down_poker_efg(), 1, 0, 1, "1/2", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 0, 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 1, 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 0, 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 1, 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 0, 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 1, 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 0, "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 1, "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 0, "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 1, "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 0, "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 1, "1/2", True), + (games.create_stripped_down_poker_efg(), 0, 0, 0, 0.5, False), + (games.create_stripped_down_poker_efg(), 0, 0, 1, 0.5, False), + (games.create_stripped_down_poker_efg(), 0, 1, 0, 0.5, False), + (games.create_stripped_down_poker_efg(), 0, 1, 1, 0.5, False), + (games.create_stripped_down_poker_efg(), 1, 0, 0, 0.5, False), + (games.create_stripped_down_poker_efg(), 1, 0, 1, 0.5, False), + (games.create_stripped_down_poker_efg(), 0, 0, 0, "1/2", True), + (games.create_stripped_down_poker_efg(), 0, 0, 1, "1/2", True), + (games.create_stripped_down_poker_efg(), 0, 1, 0, "1/2", True), + (games.create_stripped_down_poker_efg(), 0, 1, 1, "1/2", True), + (games.create_stripped_down_poker_efg(), 1, 0, 0, "1/2", True), + (games.create_stripped_down_poker_efg(), 1, 0, 1, "1/2", True), + ], ) -def test_profile_indexing_by_player_infoset_action_idx_reference(game: gbt.Game, player_idx: int, - infoset_idx: int, - action_idx: int, - prob: str | float, - rational_flag: bool): +def test_profile_indexing_by_player_infoset_action_idx_reference( + game: gbt.Game, + player_idx: int, + infoset_idx: int, + action_idx: int, + prob: str | float, + rational_flag: bool, +): profile = game.mixed_behavior_profile(rational=rational_flag) action = game.players[player_idx].infosets[infoset_idx].actions[action_idx] prob = gbt.Rational(prob) if rational_flag else prob @@ -140,25 +150,26 @@ def test_profile_indexing_by_player_infoset_action_idx_reference(game: gbt.Game, @pytest.mark.parametrize( "game,action_label,prob,rational_flag", - [(games.create_mixed_behav_game_efg(), "U1", 0.5, False), - (games.create_mixed_behav_game_efg(), "D1", 0.5, False), - (games.create_mixed_behav_game_efg(), "U2", 0.5, False), - (games.create_mixed_behav_game_efg(), "D2", 0.5, False), - (games.create_mixed_behav_game_efg(), "U3", 0.5, False), - (games.create_mixed_behav_game_efg(), "D3", 0.5, False), - (games.create_mixed_behav_game_efg(), "U1", "1/2", True), - (games.create_mixed_behav_game_efg(), "D1", "1/2", True), - (games.create_mixed_behav_game_efg(), "U2", "1/2", True), - (games.create_mixed_behav_game_efg(), "D2", "1/2", True), - (games.create_mixed_behav_game_efg(), "U3", "1/2", True), - (games.create_mixed_behav_game_efg(), "D3", "1/2", True), - (games.create_stripped_down_poker_efg(), "Call", 0.5, False), - (games.create_stripped_down_poker_efg(), "Call", "1/2", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "U1", 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), "D1", 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), "U2", 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), "D2", 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), "U3", 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), "D3", 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), "U1", "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), "D1", "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), "U2", "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), "D2", "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), "U3", "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), "D3", "1/2", True), + (games.create_stripped_down_poker_efg(), "Call", 0.5, False), + (games.create_stripped_down_poker_efg(), "Call", "1/2", True), + ], ) -def test_profile_indexing_by_action_label_reference(game: gbt.Game, action_label: str, - prob: str | float, - rational_flag: bool): +def test_profile_indexing_by_action_label_reference( + game: gbt.Game, action_label: str, prob: str | float, rational_flag: bool +): """Here we only use the action label, which are all valid""" profile = game.mixed_behavior_profile(rational=rational_flag) prob = gbt.Rational(prob) if rational_flag else prob @@ -167,36 +178,33 @@ def test_profile_indexing_by_action_label_reference(game: gbt.Game, action_label @pytest.mark.parametrize( "game,action_label,rational_flag,error", - [(games.create_mixed_behav_game_efg(), "U4", True, KeyError), - (games.create_mixed_behav_game_efg(), "U4", False, KeyError), - (games.create_stripped_down_poker_efg(), "Bet", True, ValueError), - (games.create_stripped_down_poker_efg(), "Bet", False, ValueError), - (games.create_stripped_down_poker_efg(), "Fold", True, ValueError), - (games.create_stripped_down_poker_efg(), "Fold", False, ValueError), - (games.create_stripped_down_poker_efg(), "BetFold", True, KeyError), - (games.create_stripped_down_poker_efg(), "BetFold", False, KeyError), - (games.create_stripped_down_poker_efg(), "MISSING", True, KeyError), - (games.create_stripped_down_poker_efg(), "MISSING", False, KeyError), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "U4", True, KeyError), + (games.read_from_file("mixed_behavior_game.efg"), "U4", False, KeyError), + (games.create_stripped_down_poker_efg(), "Bet", True, ValueError), + (games.create_stripped_down_poker_efg(), "Bet", False, ValueError), + (games.create_stripped_down_poker_efg(), "Fold", True, ValueError), + (games.create_stripped_down_poker_efg(), "Fold", False, ValueError), + (games.create_stripped_down_poker_efg(), "BetFold", True, KeyError), + (games.create_stripped_down_poker_efg(), "BetFold", False, KeyError), + (games.create_stripped_down_poker_efg(), "MISSING", True, KeyError), + (games.create_stripped_down_poker_efg(), "MISSING", False, KeyError), + ], ) -def test_profile_indexing_by_invalid_action_label(game: gbt.Game, action_label: str, - rational_flag: bool, - error: ValueError | KeyError): - """Test that we get a KeyError for a missing label, and a ValueError for an ambigiuous label - """ +def test_profile_indexing_by_invalid_action_label( + game: gbt.Game, action_label: str, rational_flag: bool, error: ValueError | KeyError +): + """Test that we get a KeyError for a missing label, and a ValueError for an ambigiuous label""" with pytest.raises(error): game.mixed_behavior_profile(rational=rational_flag)[action_label] -@pytest.mark.parametrize( - "rational_flag", - [True, False] - ) +@pytest.mark.parametrize("rational_flag", [True, False]) def test_profile_indexing_by_invalid_infoset_label(rational_flag: bool): """Create a duplicate infoset label and check we get a ValueError for this ambiguous label, and a KeyError for the now missing label that was overwritten """ - game = games.create_mixed_behav_game_efg() + game = games.read_from_file("mixed_behavior_game.efg") profile = game.mixed_behavior_profile(rational=rational_flag) assert profile["Infoset 1:1"] game.infosets["Infoset 1:1"].label = "Infoset 2:1" @@ -208,25 +216,24 @@ def test_profile_indexing_by_invalid_infoset_label(rational_flag: bool): @pytest.mark.parametrize( "game,infoset_label,action_label,prob,rational_flag", - [(games.create_mixed_behav_game_efg(), "Infoset 1:1", "U1", 0.5, False), - (games.create_mixed_behav_game_efg(), "Infoset 1:1", "D1", 0.5, False), - (games.create_mixed_behav_game_efg(), "Infoset 1:1", "U1", "1/2", True), - (games.create_mixed_behav_game_efg(), "Infoset 1:1", "D1", "1/2", True), - (games.create_stripped_down_poker_efg(), "Alice has King", "Bet", 0.5, False), - (games.create_stripped_down_poker_efg(), "Alice has King", "Fold", 0.5, False), - (games.create_stripped_down_poker_efg(), "Alice has Queen", "Bet", 0.5, False), - (games.create_stripped_down_poker_efg(), "Alice has Queen", "Fold", 0.5, False), - (games.create_stripped_down_poker_efg(), "Bob's response", "Call", 0.5, False), - (games.create_stripped_down_poker_efg(), "Bob's response", "Fold", 0.5, False), - (games.create_stripped_down_poker_efg(), "Bob's response", "Call", "1/2", True), - (games.create_stripped_down_poker_efg(), "Bob's response", "Fold", "1/2", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", "U1", 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", "D1", 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", "U1", "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", "D1", "1/2", True), + (games.create_stripped_down_poker_efg(), "Alice has King", "Bet", 0.5, False), + (games.create_stripped_down_poker_efg(), "Alice has King", "Fold", 0.5, False), + (games.create_stripped_down_poker_efg(), "Alice has Queen", "Bet", 0.5, False), + (games.create_stripped_down_poker_efg(), "Alice has Queen", "Fold", 0.5, False), + (games.create_stripped_down_poker_efg(), "Bob's response", "Call", 0.5, False), + (games.create_stripped_down_poker_efg(), "Bob's response", "Fold", 0.5, False), + (games.create_stripped_down_poker_efg(), "Bob's response", "Call", "1/2", True), + (games.create_stripped_down_poker_efg(), "Bob's response", "Fold", "1/2", True), + ], ) -def test_profile_indexing_by_infoset_and_action_labels_reference(game: gbt.Game, - infoset_label: str, - action_label: str, - prob: str | float, - rational_flag: bool): +def test_profile_indexing_by_infoset_and_action_labels_reference( + game: gbt.Game, infoset_label: str, action_label: str, prob: str | float, rational_flag: bool +): """Here we use the infoset label and action label, with some examples where the action label alone throws a ValueError (checked in a separate test) """ @@ -237,26 +244,57 @@ def test_profile_indexing_by_infoset_and_action_labels_reference(game: gbt.Game, @pytest.mark.parametrize( "game,player_label,infoset_label,action_label,prob,rational_flag", - [(games.create_mixed_behav_game_efg(), "Player 1", "Infoset 1:1", "U1", 0.5, False), - (games.create_mixed_behav_game_efg(), "Player 1", "Infoset 1:1", "D1", 0.5, False), - (games.create_mixed_behav_game_efg(), "Player 1", "Infoset 1:1", "U1", "1/2", True), - (games.create_mixed_behav_game_efg(), "Player 1", "Infoset 1:1", "D1", "1/2", True), - (games.create_stripped_down_poker_efg(), "Alice", "Alice has King", "Bet", 0.5, False), - (games.create_stripped_down_poker_efg(), "Alice", "Alice has King", "Fold", 0.5, False), - (games.create_stripped_down_poker_efg(), "Alice", "Alice has Queen", "Bet", 0.5, False), - (games.create_stripped_down_poker_efg(), "Alice", "Alice has Queen", "Fold", 0.5, False), - (games.create_stripped_down_poker_efg(), "Bob", "Bob's response", "Call", 0.5, False), - (games.create_stripped_down_poker_efg(), "Bob", "Bob's response", "Fold", 0.5, False), - (games.create_stripped_down_poker_efg(), "Bob", "Bob's response", "Call", "1/2", True), - (games.create_stripped_down_poker_efg(), "Bob", "Bob's response", "Fold", "1/2", True), - ] + [ + ( + games.read_from_file("mixed_behavior_game.efg"), + "Player 1", + "Infoset 1:1", + "U1", + 0.5, + False, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + "Player 1", + "Infoset 1:1", + "D1", + 0.5, + False, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + "Player 1", + "Infoset 1:1", + "U1", + "1/2", + True, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + "Player 1", + "Infoset 1:1", + "D1", + "1/2", + True, + ), + (games.create_stripped_down_poker_efg(), "Alice", "Alice has King", "Bet", 0.5, False), + (games.create_stripped_down_poker_efg(), "Alice", "Alice has King", "Fold", 0.5, False), + (games.create_stripped_down_poker_efg(), "Alice", "Alice has Queen", "Bet", 0.5, False), + (games.create_stripped_down_poker_efg(), "Alice", "Alice has Queen", "Fold", 0.5, False), + (games.create_stripped_down_poker_efg(), "Bob", "Bob's response", "Call", 0.5, False), + (games.create_stripped_down_poker_efg(), "Bob", "Bob's response", "Fold", 0.5, False), + (games.create_stripped_down_poker_efg(), "Bob", "Bob's response", "Call", "1/2", True), + (games.create_stripped_down_poker_efg(), "Bob", "Bob's response", "Fold", "1/2", True), + ], ) -def test_profile_indexing_by_player_infoset_action_labels_reference(game: gbt.Game, - player_label: str, - infoset_label: str, - action_label: str, - prob: str | float, - rational_flag: bool): +def test_profile_indexing_by_player_infoset_action_labels_reference( + game: gbt.Game, + player_label: str, + infoset_label: str, + action_label: str, + prob: str | float, + rational_flag: bool, +): """Here we use the infoset label and action label, with some examples where the action label alone throws a ValueError (checked in a separate test) """ @@ -267,41 +305,52 @@ def test_profile_indexing_by_player_infoset_action_labels_reference(game: gbt.Ga @pytest.mark.parametrize( "game,infoset_label,action_label,rational_flag", - [(games.create_mixed_behav_game_efg(), "1:1", "U2", True), # U2 is at a different iset - (games.create_mixed_behav_game_efg(), "1:1", "U2", False), - (games.create_mixed_behav_game_efg(), "1:1", "U4", True), # U4 isn't in the game - (games.create_mixed_behav_game_efg(), "1:1", "U4", False), - (games.create_stripped_down_poker_efg(), "Alice has King", "MEET", True), - (games.create_stripped_down_poker_efg(), "Alice has King", "MEET", False), - ] + [ + ( + games.read_from_file("mixed_behavior_game.efg"), + "1:1", + "U2", + True, + ), # U2 is at a different iset + (games.read_from_file("mixed_behavior_game.efg"), "1:1", "U2", False), + ( + games.read_from_file("mixed_behavior_game.efg"), + "1:1", + "U4", + True, + ), # U4 isn't in the game + (games.read_from_file("mixed_behavior_game.efg"), "1:1", "U4", False), + (games.create_stripped_down_poker_efg(), "Alice has King", "MEET", True), + (games.create_stripped_down_poker_efg(), "Alice has King", "MEET", False), + ], ) -def test_profile_indexing_by_invalid_infoset_or_action_label(game: gbt.Game, infoset_label: str, - action_label: str, - rational_flag: bool): +def test_profile_indexing_by_invalid_infoset_or_action_label( + game: gbt.Game, infoset_label: str, action_label: str, rational_flag: bool +): with pytest.raises(KeyError): game.mixed_behavior_profile(rational=rational_flag)[infoset_label][action_label] @pytest.mark.parametrize( "game,player_idx,infoset_idx,probs,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, 0, [0.5, 0.5], False), - (games.create_mixed_behav_game_efg(), 1, 0, [0.5, 0.5], False), - (games.create_mixed_behav_game_efg(), 2, 0, [0.5, 0.5], False), - (games.create_mixed_behav_game_efg(), 0, 0, ["1/2", "1/2"], True), - (games.create_mixed_behav_game_efg(), 1, 0, ["1/2", "1/2"], True), - (games.create_mixed_behav_game_efg(), 2, 0, ["1/2", "1/2"], True), - (games.create_stripped_down_poker_efg(), 0, 0, [0.5, 0.5], False), - (games.create_stripped_down_poker_efg(), 0, 1, [0.5, 0.5], False), - (games.create_stripped_down_poker_efg(), 1, 0, [0.5, 0.5], False), - (games.create_stripped_down_poker_efg(), 0, 0, ["1/2", "1/2"], True), - (games.create_stripped_down_poker_efg(), 0, 1, ["1/2", "1/2"], True), - (games.create_stripped_down_poker_efg(), 1, 0, ["1/2", "1/2"], True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, [0.5, 0.5], False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, [0.5, 0.5], False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, [0.5, 0.5], False), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, ["1/2", "1/2"], True), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, ["1/2", "1/2"], True), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, ["1/2", "1/2"], True), + (games.create_stripped_down_poker_efg(), 0, 0, [0.5, 0.5], False), + (games.create_stripped_down_poker_efg(), 0, 1, [0.5, 0.5], False), + (games.create_stripped_down_poker_efg(), 1, 0, [0.5, 0.5], False), + (games.create_stripped_down_poker_efg(), 0, 0, ["1/2", "1/2"], True), + (games.create_stripped_down_poker_efg(), 0, 1, ["1/2", "1/2"], True), + (games.create_stripped_down_poker_efg(), 1, 0, ["1/2", "1/2"], True), + ], ) -def test_profile_indexing_by_player_and_infoset_idx_reference(game: gbt.Game, - player_idx: int, - infoset_idx: int, - probs: list, rational_flag: bool): +def test_profile_indexing_by_player_and_infoset_idx_reference( + game: gbt.Game, player_idx: int, infoset_idx: int, probs: list, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) infoset = game.players[player_idx].infosets[infoset_idx] probs = [gbt.Rational(prob) for prob in probs] if rational_flag else probs @@ -310,23 +359,24 @@ def test_profile_indexing_by_player_and_infoset_idx_reference(game: gbt.Game, @pytest.mark.parametrize( "game,player_idx,infoset_label,probs,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, "Infoset 1:1", [0.5, 0.5], False), - (games.create_mixed_behav_game_efg(), 1, "Infoset 2:1", [0.5, 0.5], False), - (games.create_mixed_behav_game_efg(), 2, "Infoset 3:1", [0.5, 0.5], False), - (games.create_mixed_behav_game_efg(), 0, "Infoset 1:1", ["1/2", "1/2"], True), - (games.create_mixed_behav_game_efg(), 1, "Infoset 2:1", ["1/2", "1/2"], True), - (games.create_mixed_behav_game_efg(), 2, "Infoset 3:1", ["1/2", "1/2"], True), - (games.create_stripped_down_poker_efg(), 0, "Alice has King", [0.5, 0.5], False), - (games.create_stripped_down_poker_efg(), 0, "Alice has Queen", [0.5, 0.5], False), - (games.create_stripped_down_poker_efg(), 1, "Bob's response", [0.5, 0.5], False), - (games.create_stripped_down_poker_efg(), 0, "Alice has King", ["1/2", "1/2"], True), - (games.create_stripped_down_poker_efg(), 0, "Alice has Queen", ["1/2", "1/2"], True), - (games.create_stripped_down_poker_efg(), 1, "Bob's response", ["1/2", "1/2"], True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, "Infoset 1:1", [0.5, 0.5], False), + (games.read_from_file("mixed_behavior_game.efg"), 1, "Infoset 2:1", [0.5, 0.5], False), + (games.read_from_file("mixed_behavior_game.efg"), 2, "Infoset 3:1", [0.5, 0.5], False), + (games.read_from_file("mixed_behavior_game.efg"), 0, "Infoset 1:1", ["1/2", "1/2"], True), + (games.read_from_file("mixed_behavior_game.efg"), 1, "Infoset 2:1", ["1/2", "1/2"], True), + (games.read_from_file("mixed_behavior_game.efg"), 2, "Infoset 3:1", ["1/2", "1/2"], True), + (games.create_stripped_down_poker_efg(), 0, "Alice has King", [0.5, 0.5], False), + (games.create_stripped_down_poker_efg(), 0, "Alice has Queen", [0.5, 0.5], False), + (games.create_stripped_down_poker_efg(), 1, "Bob's response", [0.5, 0.5], False), + (games.create_stripped_down_poker_efg(), 0, "Alice has King", ["1/2", "1/2"], True), + (games.create_stripped_down_poker_efg(), 0, "Alice has Queen", ["1/2", "1/2"], True), + (games.create_stripped_down_poker_efg(), 1, "Bob's response", ["1/2", "1/2"], True), + ], ) -def test_profile_indexing_by_player_idx_infoset_label_reference(game: gbt.Game, player_idx: int, - infoset_label: str, probs: list, - rational_flag: bool): +def test_profile_indexing_by_player_idx_infoset_label_reference( + game: gbt.Game, player_idx: int, infoset_label: str, probs: list, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) player = game.players[player_idx] probs = [gbt.Rational(prob) for prob in probs] if rational_flag else probs @@ -336,16 +386,21 @@ def test_profile_indexing_by_player_idx_infoset_label_reference(game: gbt.Game, @pytest.mark.parametrize( "game,player_label,infoset_label,rational_flag", - [(games.create_mixed_behav_game_efg(), "Player 1", "1:1", True), # correct: "Infoset 1:1" - (games.create_mixed_behav_game_efg(), "Player 1", "1:1", False), - (games.create_stripped_down_poker_efg(), "Player 1", "(2,1)", True), # wrong player - (games.create_stripped_down_poker_efg(), "Player 1", "(2,1)", False), - ] + [ + ( + games.read_from_file("mixed_behavior_game.efg"), + "Player 1", + "1:1", + True, + ), # correct: "Infoset 1:1" + (games.read_from_file("mixed_behavior_game.efg"), "Player 1", "1:1", False), + (games.create_stripped_down_poker_efg(), "Player 1", "(2,1)", True), # wrong player + (games.create_stripped_down_poker_efg(), "Player 1", "(2,1)", False), + ], ) -def test_profile_indexing_by_player_and_invalid_infoset_label(game: gbt.Game, - player_label: str, - infoset_label: str, - rational_flag: bool): +def test_profile_indexing_by_player_and_invalid_infoset_label( + game: gbt.Game, player_label: str, infoset_label: str, rational_flag: bool +): """Test that we get a KeyError and that "player" appears in the error message""" with pytest.raises(KeyError, match="player"): game.mixed_behavior_profile(rational=rational_flag)[player_label][infoset_label] @@ -353,16 +408,16 @@ def test_profile_indexing_by_player_and_invalid_infoset_label(game: gbt.Game, @pytest.mark.parametrize( "game,player_label,action_label,rational_flag", - [(games.create_mixed_behav_game_efg(), "Player 1", "U2", True), - (games.create_mixed_behav_game_efg(), "Player 1", "U2", False), - (games.create_stripped_down_poker_efg(), "Player 1", "MEET", True), - (games.create_stripped_down_poker_efg(), "Player 1", "MEET", False), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "Player 1", "U2", True), + (games.read_from_file("mixed_behavior_game.efg"), "Player 1", "U2", False), + (games.create_stripped_down_poker_efg(), "Player 1", "MEET", True), + (games.create_stripped_down_poker_efg(), "Player 1", "MEET", False), + ], ) -def test_profile_indexing_by_player_and_invalid_action_label(game: gbt.Game, - player_label: str, - action_label: str, - rational_flag: bool): +def test_profile_indexing_by_player_and_invalid_action_label( + game: gbt.Game, player_label: str, action_label: str, rational_flag: bool +): """Test that we get a KeyError and that "player" appears in the error message""" with pytest.raises(KeyError, match="player"): game.mixed_behavior_profile(rational=rational_flag)[player_label][action_label] @@ -370,21 +425,22 @@ def test_profile_indexing_by_player_and_invalid_action_label(game: gbt.Game, @pytest.mark.parametrize( "game,player_idx,behav_data,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, [[0.5, 0.5]], False), - (games.create_mixed_behav_game_efg(), 1, [[0.5, 0.5]], False), - (games.create_mixed_behav_game_efg(), 2, [[0.5, 0.5]], False), - (games.create_mixed_behav_game_efg(), 0, [["1/2", "1/2"]], True), - (games.create_mixed_behav_game_efg(), 1, [["1/2", "1/2"]], True), - (games.create_mixed_behav_game_efg(), 2, [["1/2", "1/2"]], True), - (games.create_stripped_down_poker_efg(), 0, [[0.5, 0.5], [0.5, 0.5]], False), - (games.create_stripped_down_poker_efg(), 1, [[0.5, 0.5]], False), - (games.create_stripped_down_poker_efg(), 0, [["1/2", "1/2"], ["1/2", "1/2"]], True), - (games.create_stripped_down_poker_efg(), 1, [["1/2", "1/2"]], True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, [[0.5, 0.5]], False), + (games.read_from_file("mixed_behavior_game.efg"), 1, [[0.5, 0.5]], False), + (games.read_from_file("mixed_behavior_game.efg"), 2, [[0.5, 0.5]], False), + (games.read_from_file("mixed_behavior_game.efg"), 0, [["1/2", "1/2"]], True), + (games.read_from_file("mixed_behavior_game.efg"), 1, [["1/2", "1/2"]], True), + (games.read_from_file("mixed_behavior_game.efg"), 2, [["1/2", "1/2"]], True), + (games.create_stripped_down_poker_efg(), 0, [[0.5, 0.5], [0.5, 0.5]], False), + (games.create_stripped_down_poker_efg(), 1, [[0.5, 0.5]], False), + (games.create_stripped_down_poker_efg(), 0, [["1/2", "1/2"], ["1/2", "1/2"]], True), + (games.create_stripped_down_poker_efg(), 1, [["1/2", "1/2"]], True), + ], ) -def test_profile_indexing_by_player_idx_reference(game: gbt.Game, player_idx: int, - behav_data: list, - rational_flag: bool): +def test_profile_indexing_by_player_idx_reference( + game: gbt.Game, player_idx: int, behav_data: list, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) player = game.players[player_idx] if rational_flag: @@ -394,21 +450,22 @@ def test_profile_indexing_by_player_idx_reference(game: gbt.Game, player_idx: in @pytest.mark.parametrize( "game,player_label,behav_data,rational_flag", - [(games.create_mixed_behav_game_efg(), "Player 1", [[0.5, 0.5]], False), - (games.create_mixed_behav_game_efg(), "Player 2", [[0.5, 0.5]], False), - (games.create_mixed_behav_game_efg(), "Player 3", [[0.5, 0.5]], False), - (games.create_mixed_behav_game_efg(), "Player 1", [["1/2", "1/2"]], True), - (games.create_mixed_behav_game_efg(), "Player 2", [["1/2", "1/2"]], True), - (games.create_mixed_behav_game_efg(), "Player 3", [["1/2", "1/2"]], True), - (games.create_stripped_down_poker_efg(), "Alice", [[0.5, 0.5], [0.5, 0.5]], False), - (games.create_stripped_down_poker_efg(), "Bob", [[0.5, 0.5]], False), - (games.create_stripped_down_poker_efg(), "Alice", [["1/2", "1/2"], ["1/2", "1/2"]], - True), - (games.create_stripped_down_poker_efg(), "Bob", [["1/2", "1/2"]], True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "Player 1", [[0.5, 0.5]], False), + (games.read_from_file("mixed_behavior_game.efg"), "Player 2", [[0.5, 0.5]], False), + (games.read_from_file("mixed_behavior_game.efg"), "Player 3", [[0.5, 0.5]], False), + (games.read_from_file("mixed_behavior_game.efg"), "Player 1", [["1/2", "1/2"]], True), + (games.read_from_file("mixed_behavior_game.efg"), "Player 2", [["1/2", "1/2"]], True), + (games.read_from_file("mixed_behavior_game.efg"), "Player 3", [["1/2", "1/2"]], True), + (games.create_stripped_down_poker_efg(), "Alice", [[0.5, 0.5], [0.5, 0.5]], False), + (games.create_stripped_down_poker_efg(), "Bob", [[0.5, 0.5]], False), + (games.create_stripped_down_poker_efg(), "Alice", [["1/2", "1/2"], ["1/2", "1/2"]], True), + (games.create_stripped_down_poker_efg(), "Bob", [["1/2", "1/2"]], True), + ], ) -def test_profile_indexing_by_player_label_reference(game: gbt.Game, player_label: str, - behav_data: list, rational_flag: bool): +def test_profile_indexing_by_player_label_reference( + game: gbt.Game, player_label: str, behav_data: list, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) if rational_flag: behav_data = [[gbt.Rational(prob) for prob in probs] for probs in behav_data] @@ -417,34 +474,36 @@ def test_profile_indexing_by_player_label_reference(game: gbt.Game, player_label @pytest.mark.parametrize( "game,action_idx,prob,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, 0.72, False), - (games.create_mixed_behav_game_efg(), 1, 0.28, False), - (games.create_mixed_behav_game_efg(), 2, 0.42, False), - (games.create_mixed_behav_game_efg(), 3, 0.58, False), - (games.create_mixed_behav_game_efg(), 4, 0.02, False), - (games.create_mixed_behav_game_efg(), 5, 0.98, False), - (games.create_mixed_behav_game_efg(), 0, "2/9", True), - (games.create_mixed_behav_game_efg(), 1, "7/9", True), - (games.create_mixed_behav_game_efg(), 2, "4/13", True), - (games.create_mixed_behav_game_efg(), 3, "9/13", True), - (games.create_mixed_behav_game_efg(), 4, "1/98", True), - (games.create_mixed_behav_game_efg(), 5, "97/98", True), - (games.create_stripped_down_poker_efg(), 0, 0.1, False), - (games.create_stripped_down_poker_efg(), 1, 0.2, False), - (games.create_stripped_down_poker_efg(), 2, 0.3, False), - (games.create_stripped_down_poker_efg(), 3, 0.4, False), - (games.create_stripped_down_poker_efg(), 4, 0.5, False), - (games.create_stripped_down_poker_efg(), 5, 0.6, False), - (games.create_stripped_down_poker_efg(), 0, "1/10", True), - (games.create_stripped_down_poker_efg(), 1, "2/10", True), - (games.create_stripped_down_poker_efg(), 2, "3/10", True), - (games.create_stripped_down_poker_efg(), 3, "4/10", True), - (games.create_stripped_down_poker_efg(), 4, "5/10", True), - (games.create_stripped_down_poker_efg(), 5, "6/10", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, 0.72, False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0.28, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0.42, False), + (games.read_from_file("mixed_behavior_game.efg"), 3, 0.58, False), + (games.read_from_file("mixed_behavior_game.efg"), 4, 0.02, False), + (games.read_from_file("mixed_behavior_game.efg"), 5, 0.98, False), + (games.read_from_file("mixed_behavior_game.efg"), 0, "2/9", True), + (games.read_from_file("mixed_behavior_game.efg"), 1, "7/9", True), + (games.read_from_file("mixed_behavior_game.efg"), 2, "4/13", True), + (games.read_from_file("mixed_behavior_game.efg"), 3, "9/13", True), + (games.read_from_file("mixed_behavior_game.efg"), 4, "1/98", True), + (games.read_from_file("mixed_behavior_game.efg"), 5, "97/98", True), + (games.create_stripped_down_poker_efg(), 0, 0.1, False), + (games.create_stripped_down_poker_efg(), 1, 0.2, False), + (games.create_stripped_down_poker_efg(), 2, 0.3, False), + (games.create_stripped_down_poker_efg(), 3, 0.4, False), + (games.create_stripped_down_poker_efg(), 4, 0.5, False), + (games.create_stripped_down_poker_efg(), 5, 0.6, False), + (games.create_stripped_down_poker_efg(), 0, "1/10", True), + (games.create_stripped_down_poker_efg(), 1, "2/10", True), + (games.create_stripped_down_poker_efg(), 2, "3/10", True), + (games.create_stripped_down_poker_efg(), 3, "4/10", True), + (games.create_stripped_down_poker_efg(), 4, "5/10", True), + (games.create_stripped_down_poker_efg(), 5, "6/10", True), + ], ) -def test_set_probabilities_action(game: gbt.Game, action_idx: int, prob: str | float, - rational_flag: bool): +def test_set_probabilities_action( + game: gbt.Game, action_idx: int, prob: str | float, rational_flag: bool +): """Test to set probabilities of actions by action index""" profile = game.mixed_behavior_profile(rational=rational_flag) prob = gbt.Rational(prob) if rational_flag else prob @@ -455,24 +514,26 @@ def test_set_probabilities_action(game: gbt.Game, action_idx: int, prob: str | f @pytest.mark.parametrize( "game,label,prob,rational_flag", - [(games.create_mixed_behav_game_efg(), "U1", 0.72, False), - (games.create_mixed_behav_game_efg(), "D1", 0.28, False), - (games.create_mixed_behav_game_efg(), "U2", 0.42, False), - (games.create_mixed_behav_game_efg(), "D2", 0.58, False), - (games.create_mixed_behav_game_efg(), "U3", 0.02, False), - (games.create_mixed_behav_game_efg(), "D3", 0.98, False), - (games.create_mixed_behav_game_efg(), "U1", "2/9", True), - (games.create_mixed_behav_game_efg(), "D1", "7/9", True), - (games.create_mixed_behav_game_efg(), "U2", "4/13", True), - (games.create_mixed_behav_game_efg(), "D2", "9/13", True), - (games.create_mixed_behav_game_efg(), "U3", "1/98", True), - (games.create_mixed_behav_game_efg(), "D3", "97/98", True), - (games.create_stripped_down_poker_efg(), "Call", 0.3, False), - (games.create_stripped_down_poker_efg(), "Call", "3/10", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "U1", 0.72, False), + (games.read_from_file("mixed_behavior_game.efg"), "D1", 0.28, False), + (games.read_from_file("mixed_behavior_game.efg"), "U2", 0.42, False), + (games.read_from_file("mixed_behavior_game.efg"), "D2", 0.58, False), + (games.read_from_file("mixed_behavior_game.efg"), "U3", 0.02, False), + (games.read_from_file("mixed_behavior_game.efg"), "D3", 0.98, False), + (games.read_from_file("mixed_behavior_game.efg"), "U1", "2/9", True), + (games.read_from_file("mixed_behavior_game.efg"), "D1", "7/9", True), + (games.read_from_file("mixed_behavior_game.efg"), "U2", "4/13", True), + (games.read_from_file("mixed_behavior_game.efg"), "D2", "9/13", True), + (games.read_from_file("mixed_behavior_game.efg"), "U3", "1/98", True), + (games.read_from_file("mixed_behavior_game.efg"), "D3", "97/98", True), + (games.create_stripped_down_poker_efg(), "Call", 0.3, False), + (games.create_stripped_down_poker_efg(), "Call", "3/10", True), + ], ) -def test_set_probabilities_action_by_label(game: gbt.Game, label: str, - prob: str | float, rational_flag: bool): +def test_set_probabilities_action_by_label( + game: gbt.Game, label: str, prob: str | float, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) prob = gbt.Rational(prob) if rational_flag else prob profile[label] = prob @@ -481,22 +542,24 @@ def test_set_probabilities_action_by_label(game: gbt.Game, label: str, @pytest.mark.parametrize( "game,player_idx,infoset_idx,probs,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, 0, [0.72, 0.28], False), - (games.create_mixed_behav_game_efg(), 1, 0, [0.42, 0.58], False), - (games.create_mixed_behav_game_efg(), 2, 0, [0.02, 0.98], False), - (games.create_mixed_behav_game_efg(), 0, 0, ["7/9", "2/9"], True), - (games.create_mixed_behav_game_efg(), 1, 0, ["4/13", "9/13"], True), - (games.create_mixed_behav_game_efg(), 2, 0, ["1/98", "97/98"], True), - (games.create_stripped_down_poker_efg(), 0, 0, [0.1, 0.9], False), - (games.create_stripped_down_poker_efg(), 0, 1, [0.2, 0.8], False), - (games.create_stripped_down_poker_efg(), 1, 0, [0.3, 0.7], False), - (games.create_stripped_down_poker_efg(), 0, 0, ["1/10", "9/10"], True), - (games.create_stripped_down_poker_efg(), 0, 1, ["2/10", "8/10"], True), - (games.create_stripped_down_poker_efg(), 1, 0, ["3/10", "7/10"], True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, [0.72, 0.28], False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, [0.42, 0.58], False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, [0.02, 0.98], False), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, ["7/9", "2/9"], True), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, ["4/13", "9/13"], True), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, ["1/98", "97/98"], True), + (games.create_stripped_down_poker_efg(), 0, 0, [0.1, 0.9], False), + (games.create_stripped_down_poker_efg(), 0, 1, [0.2, 0.8], False), + (games.create_stripped_down_poker_efg(), 1, 0, [0.3, 0.7], False), + (games.create_stripped_down_poker_efg(), 0, 0, ["1/10", "9/10"], True), + (games.create_stripped_down_poker_efg(), 0, 1, ["2/10", "8/10"], True), + (games.create_stripped_down_poker_efg(), 1, 0, ["3/10", "7/10"], True), + ], ) -def test_set_probabilities_infoset(game: gbt.Game, player_idx: int, infoset_idx: int, probs: list, - rational_flag: bool): +def test_set_probabilities_infoset( + game: gbt.Game, player_idx: int, infoset_idx: int, probs: list, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) if rational_flag: probs = [gbt.Rational(p) for p in probs] @@ -507,22 +570,24 @@ def test_set_probabilities_infoset(game: gbt.Game, player_idx: int, infoset_idx: @pytest.mark.parametrize( "game,infoset_label,probs,rational_flag", - [(games.create_mixed_behav_game_efg(), "Infoset 1:1", [0.72, 0.28], False), - (games.create_mixed_behav_game_efg(), "Infoset 2:1", [0.42, 0.58], False), - (games.create_mixed_behav_game_efg(), "Infoset 3:1", [0.02, 0.98], False), - (games.create_mixed_behav_game_efg(), "Infoset 1:1", ["7/9", "2/9"], True), - (games.create_mixed_behav_game_efg(), "Infoset 2:1", ["4/13", "9/13"], True), - (games.create_mixed_behav_game_efg(), "Infoset 3:1", ["1/98", "97/98"], True), - (games.create_stripped_down_poker_efg(), "Alice has King", [0.1, 0.9], False), - (games.create_stripped_down_poker_efg(), "Alice has Queen", [0.2, 0.8], False), - (games.create_stripped_down_poker_efg(), "Bob's response", [0.3, 0.7], False), - (games.create_stripped_down_poker_efg(), "Alice has King", ["1/10", "9/10"], True), - (games.create_stripped_down_poker_efg(), "Alice has Queen", ["2/10", "8/10"], True), - (games.create_stripped_down_poker_efg(), "Bob's response", ["3/10", "7/10"], True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", [0.72, 0.28], False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 2:1", [0.42, 0.58], False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 3:1", [0.02, 0.98], False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", ["7/9", "2/9"], True), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 2:1", ["4/13", "9/13"], True), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 3:1", ["1/98", "97/98"], True), + (games.create_stripped_down_poker_efg(), "Alice has King", [0.1, 0.9], False), + (games.create_stripped_down_poker_efg(), "Alice has Queen", [0.2, 0.8], False), + (games.create_stripped_down_poker_efg(), "Bob's response", [0.3, 0.7], False), + (games.create_stripped_down_poker_efg(), "Alice has King", ["1/10", "9/10"], True), + (games.create_stripped_down_poker_efg(), "Alice has Queen", ["2/10", "8/10"], True), + (games.create_stripped_down_poker_efg(), "Bob's response", ["3/10", "7/10"], True), + ], ) -def test_set_probabilities_infoset_by_label(game: gbt.Game, infoset_label: str, probs: list, - rational_flag: bool): +def test_set_probabilities_infoset_by_label( + game: gbt.Game, infoset_label: str, probs: list, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) if rational_flag: probs = [gbt.Rational(p) for p in probs] @@ -532,20 +597,22 @@ def test_set_probabilities_infoset_by_label(game: gbt.Game, infoset_label: str, @pytest.mark.parametrize( "game,player_idx,behav_data,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, [[0.72, 0.28]], False), - (games.create_mixed_behav_game_efg(), 1, [[0.42, 0.58]], False), - (games.create_mixed_behav_game_efg(), 2, [[0.02, 0.98]], False), - (games.create_mixed_behav_game_efg(), 0, [["7/9", "2/9"]], True), - (games.create_mixed_behav_game_efg(), 1, [["4/13", "9/13"]], True), - (games.create_mixed_behav_game_efg(), 2, [["1/98", "97/98"]], True), - (games.create_stripped_down_poker_efg(), 0, [[0.1, 0.9], [0.5, 0.5]], False), - (games.create_stripped_down_poker_efg(), 1, [[0.6, 0.4]], False), - (games.create_stripped_down_poker_efg(), 0, [["1/3", "2/3"], ["1/2", "1/2"]], True), - (games.create_stripped_down_poker_efg(), 1, [["2/3", "1/3"]], True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, [[0.72, 0.28]], False), + (games.read_from_file("mixed_behavior_game.efg"), 1, [[0.42, 0.58]], False), + (games.read_from_file("mixed_behavior_game.efg"), 2, [[0.02, 0.98]], False), + (games.read_from_file("mixed_behavior_game.efg"), 0, [["7/9", "2/9"]], True), + (games.read_from_file("mixed_behavior_game.efg"), 1, [["4/13", "9/13"]], True), + (games.read_from_file("mixed_behavior_game.efg"), 2, [["1/98", "97/98"]], True), + (games.create_stripped_down_poker_efg(), 0, [[0.1, 0.9], [0.5, 0.5]], False), + (games.create_stripped_down_poker_efg(), 1, [[0.6, 0.4]], False), + (games.create_stripped_down_poker_efg(), 0, [["1/3", "2/3"], ["1/2", "1/2"]], True), + (games.create_stripped_down_poker_efg(), 1, [["2/3", "1/3"]], True), + ], ) -def test_set_probabilities_player(game: gbt.Game, player_idx: int, behav_data: list, - rational_flag: bool): +def test_set_probabilities_player( + game: gbt.Game, player_idx: int, behav_data: list, rational_flag: bool +): player = game.players[player_idx] profile = game.mixed_behavior_profile(rational=rational_flag) if rational_flag: @@ -556,21 +623,22 @@ def test_set_probabilities_player(game: gbt.Game, player_idx: int, behav_data: l @pytest.mark.parametrize( "game,player_label,behav_data,rational_flag", - [(games.create_mixed_behav_game_efg(), "Player 1", [[0.72, 0.28]], False), - (games.create_mixed_behav_game_efg(), "Player 2", [[0.42, 0.58]], False), - (games.create_mixed_behav_game_efg(), "Player 3", [[0.02, 0.98]], False), - (games.create_mixed_behav_game_efg(), "Player 1", [["7/9", "2/9"]], True), - (games.create_mixed_behav_game_efg(), "Player 2", [["4/13", "9/13"]], True), - (games.create_mixed_behav_game_efg(), "Player 3", [["1/98", "97/98"]], True), - (games.create_stripped_down_poker_efg(), "Alice", [[0.1, 0.9], [0.5, 0.5]], False), - (games.create_stripped_down_poker_efg(), "Bob", [[0.6, 0.4]], False), - (games.create_stripped_down_poker_efg(), "Alice", [["1/3", "2/3"], ["1/2", "1/2"]], - True), - (games.create_stripped_down_poker_efg(), "Bob", [["2/3", "1/3"]], True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "Player 1", [[0.72, 0.28]], False), + (games.read_from_file("mixed_behavior_game.efg"), "Player 2", [[0.42, 0.58]], False), + (games.read_from_file("mixed_behavior_game.efg"), "Player 3", [[0.02, 0.98]], False), + (games.read_from_file("mixed_behavior_game.efg"), "Player 1", [["7/9", "2/9"]], True), + (games.read_from_file("mixed_behavior_game.efg"), "Player 2", [["4/13", "9/13"]], True), + (games.read_from_file("mixed_behavior_game.efg"), "Player 3", [["1/98", "97/98"]], True), + (games.create_stripped_down_poker_efg(), "Alice", [[0.1, 0.9], [0.5, 0.5]], False), + (games.create_stripped_down_poker_efg(), "Bob", [[0.6, 0.4]], False), + (games.create_stripped_down_poker_efg(), "Alice", [["1/3", "2/3"], ["1/2", "1/2"]], True), + (games.create_stripped_down_poker_efg(), "Bob", [["2/3", "1/3"]], True), + ], ) -def test_set_probabilities_player_by_label(game: gbt.Game, player_label: str, behav_data: list, - rational_flag: bool): +def test_set_probabilities_player_by_label( + game: gbt.Game, player_label: str, behav_data: list, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) if rational_flag: behav_data = [[gbt.Rational(prob) for prob in probs] for probs in behav_data] @@ -580,85 +648,90 @@ def test_set_probabilities_player_by_label(game: gbt.Game, player_label: str, be @pytest.mark.parametrize( "game,node_idx,realiz_prob,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, "1", True), - (games.create_mixed_behav_game_efg(), 1, "1/2", True), - (games.create_mixed_behav_game_efg(), 2, "1/4", True), - (games.create_mixed_behav_game_efg(), 3, "1/8", True), - (games.create_mixed_behav_game_efg(), 4, "1/8", True), - (games.create_mixed_behav_game_efg(), 5, "1/4", True), - (games.create_mixed_behav_game_efg(), 6, "1/8", True), - (games.create_mixed_behav_game_efg(), 7, "1/8", True), - (games.create_mixed_behav_game_efg(), 8, "1/2", True), - (games.create_mixed_behav_game_efg(), 9, "1/4", True), - (games.create_mixed_behav_game_efg(), 10, "1/8", True), - (games.create_mixed_behav_game_efg(), 11, "1/8", True), - (games.create_mixed_behav_game_efg(), 12, "1/4", True), - (games.create_mixed_behav_game_efg(), 13, "1/8", True), - (games.create_mixed_behav_game_efg(), 14, "1/8", True), - (games.create_mixed_behav_game_efg(), 0, 1.0, False), - (games.create_mixed_behav_game_efg(), 1, 0.5, False), - (games.create_mixed_behav_game_efg(), 2, 0.25, False), - (games.create_mixed_behav_game_efg(), 3, 0.125, False), - (games.create_mixed_behav_game_efg(), 4, 0.125, False), - (games.create_mixed_behav_game_efg(), 5, 0.25, False), - (games.create_mixed_behav_game_efg(), 6, 0.125, False), - (games.create_mixed_behav_game_efg(), 7, 0.125, False), - (games.create_mixed_behav_game_efg(), 8, 0.5, False), - (games.create_mixed_behav_game_efg(), 9, 0.25, False), - (games.create_mixed_behav_game_efg(), 10, 0.125, False), - (games.create_mixed_behav_game_efg(), 11, 0.125, False), - (games.create_mixed_behav_game_efg(), 12, 0.25, False), - (games.create_mixed_behav_game_efg(), 13, 0.125, False), - (games.create_mixed_behav_game_efg(), 14, 0.125, False), - (games.create_stripped_down_poker_efg(), 0, "1", True), - (games.create_stripped_down_poker_efg(), 1, "1/2", True), - (games.create_stripped_down_poker_efg(), 2, "1/4", True), - (games.create_stripped_down_poker_efg(), 3, "1/8", True), - (games.create_stripped_down_poker_efg(), 4, "1/8", True), - (games.create_stripped_down_poker_efg(), 5, "1/4", True), - (games.create_stripped_down_poker_efg(), 6, "1/2", True), - (games.create_stripped_down_poker_efg(), 7, "1/4", True), - (games.create_stripped_down_poker_efg(), 8, "1/8", True), - (games.create_stripped_down_poker_efg(), 9, "1/8", True), - (games.create_stripped_down_poker_efg(), 10, "1/4", True), - (games.create_stripped_down_poker_efg(), 0, 1.0, False), - (games.create_stripped_down_poker_efg(), 1, 0.5, False), - (games.create_stripped_down_poker_efg(), 2, 0.25, False), - (games.create_stripped_down_poker_efg(), 3, 0.125, False), - (games.create_stripped_down_poker_efg(), 4, 0.125, False), - (games.create_stripped_down_poker_efg(), 5, 0.25, False), - (games.create_stripped_down_poker_efg(), 6, 0.5, False), - (games.create_stripped_down_poker_efg(), 7, 0.25, False), - (games.create_stripped_down_poker_efg(), 8, 0.125, False), - (games.create_stripped_down_poker_efg(), 9, 0.125, False), - (games.create_stripped_down_poker_efg(), 10, 0.25, False)] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, "1", True), + (games.read_from_file("mixed_behavior_game.efg"), 1, "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), 2, "1/4", True), + (games.read_from_file("mixed_behavior_game.efg"), 3, "1/8", True), + (games.read_from_file("mixed_behavior_game.efg"), 4, "1/8", True), + (games.read_from_file("mixed_behavior_game.efg"), 5, "1/4", True), + (games.read_from_file("mixed_behavior_game.efg"), 6, "1/8", True), + (games.read_from_file("mixed_behavior_game.efg"), 7, "1/8", True), + (games.read_from_file("mixed_behavior_game.efg"), 8, "1/2", True), + (games.read_from_file("mixed_behavior_game.efg"), 9, "1/4", True), + (games.read_from_file("mixed_behavior_game.efg"), 10, "1/8", True), + (games.read_from_file("mixed_behavior_game.efg"), 11, "1/8", True), + (games.read_from_file("mixed_behavior_game.efg"), 12, "1/4", True), + (games.read_from_file("mixed_behavior_game.efg"), 13, "1/8", True), + (games.read_from_file("mixed_behavior_game.efg"), 14, "1/8", True), + (games.read_from_file("mixed_behavior_game.efg"), 0, 1.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0.25, False), + (games.read_from_file("mixed_behavior_game.efg"), 3, 0.125, False), + (games.read_from_file("mixed_behavior_game.efg"), 4, 0.125, False), + (games.read_from_file("mixed_behavior_game.efg"), 5, 0.25, False), + (games.read_from_file("mixed_behavior_game.efg"), 6, 0.125, False), + (games.read_from_file("mixed_behavior_game.efg"), 7, 0.125, False), + (games.read_from_file("mixed_behavior_game.efg"), 8, 0.5, False), + (games.read_from_file("mixed_behavior_game.efg"), 9, 0.25, False), + (games.read_from_file("mixed_behavior_game.efg"), 10, 0.125, False), + (games.read_from_file("mixed_behavior_game.efg"), 11, 0.125, False), + (games.read_from_file("mixed_behavior_game.efg"), 12, 0.25, False), + (games.read_from_file("mixed_behavior_game.efg"), 13, 0.125, False), + (games.read_from_file("mixed_behavior_game.efg"), 14, 0.125, False), + (games.create_stripped_down_poker_efg(), 0, "1", True), + (games.create_stripped_down_poker_efg(), 1, "1/2", True), + (games.create_stripped_down_poker_efg(), 2, "1/4", True), + (games.create_stripped_down_poker_efg(), 3, "1/8", True), + (games.create_stripped_down_poker_efg(), 4, "1/8", True), + (games.create_stripped_down_poker_efg(), 5, "1/4", True), + (games.create_stripped_down_poker_efg(), 6, "1/2", True), + (games.create_stripped_down_poker_efg(), 7, "1/4", True), + (games.create_stripped_down_poker_efg(), 8, "1/8", True), + (games.create_stripped_down_poker_efg(), 9, "1/8", True), + (games.create_stripped_down_poker_efg(), 10, "1/4", True), + (games.create_stripped_down_poker_efg(), 0, 1.0, False), + (games.create_stripped_down_poker_efg(), 1, 0.5, False), + (games.create_stripped_down_poker_efg(), 2, 0.25, False), + (games.create_stripped_down_poker_efg(), 3, 0.125, False), + (games.create_stripped_down_poker_efg(), 4, 0.125, False), + (games.create_stripped_down_poker_efg(), 5, 0.25, False), + (games.create_stripped_down_poker_efg(), 6, 0.5, False), + (games.create_stripped_down_poker_efg(), 7, 0.25, False), + (games.create_stripped_down_poker_efg(), 8, 0.125, False), + (games.create_stripped_down_poker_efg(), 9, 0.125, False), + (games.create_stripped_down_poker_efg(), 10, 0.25, False), + ], ) -def test_realiz_prob_nodes_reference(game: gbt.Game, node_idx: int, - realiz_prob: str | float, rational_flag: bool): +def test_realiz_prob_nodes_reference( + game: gbt.Game, node_idx: int, realiz_prob: str | float, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) - realiz_prob = (gbt.Rational(realiz_prob) if rational_flag else realiz_prob) + realiz_prob = gbt.Rational(realiz_prob) if rational_flag else realiz_prob node = list(game.nodes)[node_idx] assert profile.realiz_prob(node) == realiz_prob @pytest.mark.parametrize( "game,player_idx,infoset_idx,prob,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, 0, 1.0, False), - (games.create_mixed_behav_game_efg(), 1, 0, 1.0, False), - (games.create_mixed_behav_game_efg(), 2, 0, 1.0, False), - (games.create_mixed_behav_game_efg(), 0, 0, "1", True), - (games.create_mixed_behav_game_efg(), 1, 0, "1", True), - (games.create_mixed_behav_game_efg(), 2, 0, "1", True), - (games.create_stripped_down_poker_efg(), 0, 0, 0.5, False), - (games.create_stripped_down_poker_efg(), 0, 1, 0.5, False), - (games.create_stripped_down_poker_efg(), 1, 0, 0.5, False), - (games.create_stripped_down_poker_efg(), 0, 0, "1/2", True), - (games.create_stripped_down_poker_efg(), 0, 1, "1/2", True), - (games.create_stripped_down_poker_efg(), 1, 0, "1/2", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 1.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 1.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 1.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, "1", True), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, "1", True), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, "1", True), + (games.create_stripped_down_poker_efg(), 0, 0, 0.5, False), + (games.create_stripped_down_poker_efg(), 0, 1, 0.5, False), + (games.create_stripped_down_poker_efg(), 1, 0, 0.5, False), + (games.create_stripped_down_poker_efg(), 0, 0, "1/2", True), + (games.create_stripped_down_poker_efg(), 0, 1, "1/2", True), + (games.create_stripped_down_poker_efg(), 1, 0, "1/2", True), + ], ) -def test_infoset_prob_reference(game: gbt.Game, player_idx: int, infoset_idx: int, - prob: str | float, rational_flag: bool): +def test_infoset_prob_reference( + game: gbt.Game, player_idx: int, infoset_idx: int, prob: str | float, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) ip = profile.infoset_prob(game.players[player_idx].infosets[infoset_idx]) assert ip == (gbt.Rational(prob) if rational_flag else prob) @@ -666,44 +739,48 @@ def test_infoset_prob_reference(game: gbt.Game, player_idx: int, infoset_idx: in @pytest.mark.parametrize( "game,label,prob,rational_flag,", - [(games.create_mixed_behav_game_efg(), "Infoset 1:1", 1.0, False), - (games.create_mixed_behav_game_efg(), "Infoset 2:1", 1.0, False), - (games.create_mixed_behav_game_efg(), "Infoset 3:1", 1.0, False), - (games.create_mixed_behav_game_efg(), "Infoset 1:1", "1", True), - (games.create_mixed_behav_game_efg(), "Infoset 2:1", "1", True), - (games.create_mixed_behav_game_efg(), "Infoset 3:1", "1", True), - (games.create_stripped_down_poker_efg(), "Alice has King", 0.5, False), - (games.create_stripped_down_poker_efg(), "Alice has Queen", 0.5, False), - (games.create_stripped_down_poker_efg(), "Bob's response", 0.5, False), - (games.create_stripped_down_poker_efg(), "Alice has King", "1/2", True), - (games.create_stripped_down_poker_efg(), "Alice has Queen", "1/2", True), - (games.create_stripped_down_poker_efg(), "Bob's response", "1/2", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", 1.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 2:1", 1.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 3:1", 1.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", "1", True), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 2:1", "1", True), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 3:1", "1", True), + (games.create_stripped_down_poker_efg(), "Alice has King", 0.5, False), + (games.create_stripped_down_poker_efg(), "Alice has Queen", 0.5, False), + (games.create_stripped_down_poker_efg(), "Bob's response", 0.5, False), + (games.create_stripped_down_poker_efg(), "Alice has King", "1/2", True), + (games.create_stripped_down_poker_efg(), "Alice has Queen", "1/2", True), + (games.create_stripped_down_poker_efg(), "Bob's response", "1/2", True), + ], ) -def test_infoset_prob_by_label_reference(game: gbt.Game, label: str, - prob: str | float, rational_flag: bool): +def test_infoset_prob_by_label_reference( + game: gbt.Game, label: str, prob: str | float, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) assert profile.infoset_prob(label) == (gbt.Rational(prob) if rational_flag else prob) @pytest.mark.parametrize( "game,player_idx,infoset_idx,payoff,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, 0, 3.0, False), - (games.create_mixed_behav_game_efg(), 1, 0, 3.0, False), - (games.create_mixed_behav_game_efg(), 2, 0, 3.25, False), - (games.create_mixed_behav_game_efg(), 0, 0, "3", True), - (games.create_mixed_behav_game_efg(), 1, 0, "3", True), - (games.create_mixed_behav_game_efg(), 2, 0, "13/4", True), - (games.create_stripped_down_poker_efg(), 0, 0, 0.25, False), - (games.create_stripped_down_poker_efg(), 0, 1, -0.75, False), - (games.create_stripped_down_poker_efg(), 1, 0, -0.5, False), - (games.create_stripped_down_poker_efg(), 0, 0, "1/4", True), - (games.create_stripped_down_poker_efg(), 0, 1, "-3/4", True), - (games.create_stripped_down_poker_efg(), 1, 0, "-1/2", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 3.25, False), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, "3", True), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, "3", True), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, "13/4", True), + (games.create_stripped_down_poker_efg(), 0, 0, 0.25, False), + (games.create_stripped_down_poker_efg(), 0, 1, -0.75, False), + (games.create_stripped_down_poker_efg(), 1, 0, -0.5, False), + (games.create_stripped_down_poker_efg(), 0, 0, "1/4", True), + (games.create_stripped_down_poker_efg(), 0, 1, "-3/4", True), + (games.create_stripped_down_poker_efg(), 1, 0, "-1/2", True), + ], ) -def test_infoset_payoff_reference(game: gbt.Game, player_idx: int, infoset_idx: int, - payoff: str | float, rational_flag: bool): +def test_infoset_payoff_reference( + game: gbt.Game, player_idx: int, infoset_idx: int, payoff: str | float, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) iv = profile.infoset_value(game.players[player_idx].infosets[infoset_idx]) assert iv == (gbt.Rational(payoff) if rational_flag else payoff) @@ -711,58 +788,66 @@ def test_infoset_payoff_reference(game: gbt.Game, player_idx: int, infoset_idx: @pytest.mark.parametrize( "game,label,payoff,rational_flag", - [(games.create_mixed_behav_game_efg(), "Infoset 1:1", 3.0, False), - (games.create_mixed_behav_game_efg(), "Infoset 2:1", 3.0, False), - (games.create_mixed_behav_game_efg(), "Infoset 3:1", 3.25, False), - (games.create_mixed_behav_game_efg(), "Infoset 1:1", "3", True), - (games.create_mixed_behav_game_efg(), "Infoset 2:1", "3", True), - (games.create_mixed_behav_game_efg(), "Infoset 3:1", "13/4", True), - (games.create_stripped_down_poker_efg(), "Alice has King", 0.25, False), - (games.create_stripped_down_poker_efg(), "Alice has Queen", -0.75, False), - (games.create_stripped_down_poker_efg(), "Bob's response", -0.5, False), - (games.create_stripped_down_poker_efg(), "Alice has King", "1/4", True), - (games.create_stripped_down_poker_efg(), "Alice has Queen", "-3/4", True), - (games.create_stripped_down_poker_efg(), "Bob's response", "-1/2", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 2:1", 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 3:1", 3.25, False), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 1:1", "3", True), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 2:1", "3", True), + (games.read_from_file("mixed_behavior_game.efg"), "Infoset 3:1", "13/4", True), + (games.create_stripped_down_poker_efg(), "Alice has King", 0.25, False), + (games.create_stripped_down_poker_efg(), "Alice has Queen", -0.75, False), + (games.create_stripped_down_poker_efg(), "Bob's response", -0.5, False), + (games.create_stripped_down_poker_efg(), "Alice has King", "1/4", True), + (games.create_stripped_down_poker_efg(), "Alice has Queen", "-3/4", True), + (games.create_stripped_down_poker_efg(), "Bob's response", "-1/2", True), + ], ) -def test_infoset_payoff_by_label_reference(game: gbt.Game, label: str, - payoff: str | float, rational_flag: bool): +def test_infoset_payoff_by_label_reference( + game: gbt.Game, label: str, payoff: str | float, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) assert profile.infoset_value(label) == (gbt.Rational(payoff) if rational_flag else payoff) @pytest.mark.parametrize( "game,player_idx,infoset_idx,action_idx,payoff,rational_flag", - [(games.create_mixed_behav_game_efg(), 0, 0, 0, 3.0, False), - (games.create_mixed_behav_game_efg(), 0, 0, 1, 3.0, False), - (games.create_mixed_behav_game_efg(), 1, 0, 0, 3.0, False), - (games.create_mixed_behav_game_efg(), 1, 0, 1, 3.0, False), - (games.create_mixed_behav_game_efg(), 2, 0, 0, 3.5, False), - (games.create_mixed_behav_game_efg(), 2, 0, 1, 3.0, False), - (games.create_mixed_behav_game_efg(), 2, 0, 1, 3.0, False), - (games.create_mixed_behav_game_efg(), 0, 0, 0, "3/1", True), - (games.create_mixed_behav_game_efg(), 0, 0, 1, "3/1", True), - (games.create_mixed_behav_game_efg(), 1, 0, 0, "3/1", True), - (games.create_mixed_behav_game_efg(), 1, 0, 1, "3/1", True), - (games.create_mixed_behav_game_efg(), 2, 0, 0, "7/2", True), - (games.create_mixed_behav_game_efg(), 2, 0, 1, "3/1", True), - (games.create_stripped_down_poker_efg(), 0, 0, 0, 1.5, False), - (games.create_stripped_down_poker_efg(), 0, 0, 1, -1, False), - (games.create_stripped_down_poker_efg(), 0, 1, 0, -0.5, False), - (games.create_stripped_down_poker_efg(), 0, 1, 1, -1, False), - (games.create_stripped_down_poker_efg(), 1, 0, 0, 0, False), - (games.create_stripped_down_poker_efg(), 1, 0, 1, -1, False), - (games.create_stripped_down_poker_efg(), 0, 0, 0, "3/2", True), - (games.create_stripped_down_poker_efg(), 0, 0, 1, -1, True), - (games.create_stripped_down_poker_efg(), 0, 1, 0, "-1/2", True), - (games.create_stripped_down_poker_efg(), 0, 1, 1, -1, True), - (games.create_stripped_down_poker_efg(), 1, 0, 0, 0, True), - (games.create_stripped_down_poker_efg(), 1, 0, 1, -1, True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 0, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 1, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 0, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 1, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 0, 3.5, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 1, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 1, 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 0, "3/1", True), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 1, "3/1", True), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 0, "3/1", True), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 1, "3/1", True), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 0, "7/2", True), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 1, "3/1", True), + (games.create_stripped_down_poker_efg(), 0, 0, 0, 1.5, False), + (games.create_stripped_down_poker_efg(), 0, 0, 1, -1, False), + (games.create_stripped_down_poker_efg(), 0, 1, 0, -0.5, False), + (games.create_stripped_down_poker_efg(), 0, 1, 1, -1, False), + (games.create_stripped_down_poker_efg(), 1, 0, 0, 0, False), + (games.create_stripped_down_poker_efg(), 1, 0, 1, -1, False), + (games.create_stripped_down_poker_efg(), 0, 0, 0, "3/2", True), + (games.create_stripped_down_poker_efg(), 0, 0, 1, -1, True), + (games.create_stripped_down_poker_efg(), 0, 1, 0, "-1/2", True), + (games.create_stripped_down_poker_efg(), 0, 1, 1, -1, True), + (games.create_stripped_down_poker_efg(), 1, 0, 0, 0, True), + (games.create_stripped_down_poker_efg(), 1, 0, 1, -1, True), + ], ) -def test_action_payoff_reference(game: gbt.Game, player_idx: int, infoset_idx: int, - action_idx: int, payoff: str | float, - rational_flag: bool): +def test_action_payoff_reference( + game: gbt.Game, + player_idx: int, + infoset_idx: int, + action_idx: int, + payoff: str | float, + rational_flag: bool, +): profile = game.mixed_behavior_profile(rational=rational_flag) av = profile.action_value(game.players[player_idx].infosets[infoset_idx].actions[action_idx]) assert av == (gbt.Rational(payoff) if rational_flag else payoff) @@ -770,82 +855,83 @@ def test_action_payoff_reference(game: gbt.Game, player_idx: int, infoset_idx: i @pytest.mark.parametrize( "game,label,payoff,rational_flag", - [(games.create_mixed_behav_game_efg(), "U1", 3.0, False), - (games.create_mixed_behav_game_efg(), "D1", 3.0, False), - (games.create_mixed_behav_game_efg(), "U2", 3.0, False), - (games.create_mixed_behav_game_efg(), "D2", 3.0, False), - (games.create_mixed_behav_game_efg(), "U3", 3.5, False), - (games.create_mixed_behav_game_efg(), "D3", 3.0, False), - (games.create_mixed_behav_game_efg(), "U1", "3", True), - (games.create_mixed_behav_game_efg(), "D1", "3", True), - (games.create_mixed_behav_game_efg(), "U2", "3", True), - (games.create_mixed_behav_game_efg(), "D2", "3", True), - (games.create_mixed_behav_game_efg(), "U3", "7/2", True), - (games.create_mixed_behav_game_efg(), "D3", "3", True), - (games.create_stripped_down_poker_efg(), "Call", 0, False), - (games.create_stripped_down_poker_efg(), "Call", "0", True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), "U1", 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "D1", 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "U2", 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "D2", 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "U3", 3.5, False), + (games.read_from_file("mixed_behavior_game.efg"), "D3", 3.0, False), + (games.read_from_file("mixed_behavior_game.efg"), "U1", "3", True), + (games.read_from_file("mixed_behavior_game.efg"), "D1", "3", True), + (games.read_from_file("mixed_behavior_game.efg"), "U2", "3", True), + (games.read_from_file("mixed_behavior_game.efg"), "D2", "3", True), + (games.read_from_file("mixed_behavior_game.efg"), "U3", "7/2", True), + (games.read_from_file("mixed_behavior_game.efg"), "D3", "3", True), + (games.create_stripped_down_poker_efg(), "Call", 0, False), + (games.create_stripped_down_poker_efg(), "Call", "0", True), + ], ) -def test_action_value_by_label_reference(game: gbt.Game, label: str, - payoff: str | float, rational_flag: bool): +def test_action_value_by_label_reference( + game: gbt.Game, label: str, payoff: str | float, rational_flag: bool +): profile = game.mixed_behavior_profile(rational=rational_flag) assert profile.action_value(label) == (gbt.Rational(payoff) if rational_flag else payoff) @pytest.mark.parametrize( "game,rational_flag", - [(games.create_mixed_behav_game_efg(), False), - (games.create_mixed_behav_game_efg(), True), - (games.create_stripped_down_poker_efg(), False), - (games.create_stripped_down_poker_efg(), True), - (games.create_kuhn_poker_efg(), False), - (games.create_kuhn_poker_efg(), True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), False), + (games.read_from_file("mixed_behavior_game.efg"), True), + (games.create_stripped_down_poker_efg(), False), + (games.create_stripped_down_poker_efg(), True), + (games.create_kuhn_poker_efg(), False), + (games.create_kuhn_poker_efg(), True), + ], ) def test_action_regret_consistency(game: gbt.Game, rational_flag: bool): profile = game.mixed_behavior_profile(rational=rational_flag) for player in game.players: for infoset in player.infosets: for action in infoset.actions: - assert ( - profile.action_regret(action) == - max(profile.action_value(a) for a in infoset.actions) - - profile.action_value(action) - ) + assert profile.action_regret(action) == max( + profile.action_value(a) for a in infoset.actions + ) - profile.action_value(action) @pytest.mark.parametrize( "game,rational_flag", - [(games.create_mixed_behav_game_efg(), False), - (games.create_mixed_behav_game_efg(), True), - (games.create_stripped_down_poker_efg(), False), - (games.create_stripped_down_poker_efg(), True), - (games.create_kuhn_poker_efg(), False), - (games.create_kuhn_poker_efg(), True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), False), + (games.read_from_file("mixed_behavior_game.efg"), True), + (games.create_stripped_down_poker_efg(), False), + (games.create_stripped_down_poker_efg(), True), + (games.create_kuhn_poker_efg(), False), + (games.create_kuhn_poker_efg(), True), + ], ) def test_infoset_regret_consistency(game: gbt.Game, rational_flag: bool): profile = game.mixed_behavior_profile(rational=rational_flag) for player in game.players: for infoset in player.infosets: - assert ( - profile.infoset_regret(infoset) == - max(profile.action_value(a) for a in infoset.actions) - - profile.infoset_value(infoset) - ) + assert profile.infoset_regret(infoset) == max( + profile.action_value(a) for a in infoset.actions + ) - profile.infoset_value(infoset) @pytest.mark.parametrize( "game,rational_flag", - [(games.create_mixed_behav_game_efg(), False), - (games.create_mixed_behav_game_efg(), True), - (games.create_stripped_down_poker_efg(), False), - (games.create_stripped_down_poker_efg(), True), - (games.create_kuhn_poker_efg(), False), - (games.create_kuhn_poker_efg(), True), - (games.create_3_player_with_internal_outcomes_efg(), False), - (games.create_3_player_with_internal_outcomes_efg(), True) - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), False), + (games.read_from_file("mixed_behavior_game.efg"), True), + (games.create_stripped_down_poker_efg(), False), + (games.create_stripped_down_poker_efg(), True), + (games.create_kuhn_poker_efg(), False), + (games.create_kuhn_poker_efg(), True), + (games.read_from_file("3_player.efg"), False), + (games.read_from_file("3_player.efg"), True), + ], ) def test_max_regret_consistency(game: gbt.Game, rational_flag: bool): profile = game.mixed_behavior_profile(rational=rational_flag) @@ -854,71 +940,223 @@ def test_max_regret_consistency(game: gbt.Game, rational_flag: bool): @pytest.mark.parametrize( "game,rational_flag", - [(games.create_mixed_behav_game_efg(), False), - (games.create_mixed_behav_game_efg(), True), - (games.create_stripped_down_poker_efg(), False), - (games.create_stripped_down_poker_efg(), True), - (games.create_kuhn_poker_efg(), False), - (games.create_kuhn_poker_efg(), True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), False), + (games.read_from_file("mixed_behavior_game.efg"), True), + (games.create_stripped_down_poker_efg(), False), + (games.create_stripped_down_poker_efg(), True), + (games.create_kuhn_poker_efg(), False), + (games.create_kuhn_poker_efg(), True), + ], ) def test_agent_max_regret_consistency(game: gbt.Game, rational_flag: bool): profile = game.mixed_behavior_profile(rational=rational_flag) - assert ( - profile.agent_max_regret() == - max([profile.infoset_regret(infoset) for infoset in game.infosets]) + assert profile.agent_max_regret() == max( + [profile.infoset_regret(infoset) for infoset in game.infosets] ) @pytest.mark.parametrize( "game,player_idx,infoset_idx,action_idx,action_probs,rational_flag,tol,value", [ - # uniform - (games.create_mixed_behav_game_efg(), 0, 0, 0, None, False, TOL, 0), - (games.create_mixed_behav_game_efg(), 0, 0, 1, None, False, TOL, 0), - (games.create_mixed_behav_game_efg(), 1, 0, 0, None, False, TOL, 0), - (games.create_mixed_behav_game_efg(), 1, 0, 1, None, False, TOL, 0), - (games.create_mixed_behav_game_efg(), 2, 0, 0, None, False, TOL, 0), - (games.create_mixed_behav_game_efg(), 2, 0, 1, None, False, TOL, 0.5), # 3.5 - 3 - # U1 U2 U3 - (games.create_mixed_behav_game_efg(), 0, 0, 0, [1, 0, 1, 0, 1, 0], False, TOL, 0), - (games.create_mixed_behav_game_efg(), 0, 0, 0, [1, 0, 1, 0, 1, 0], True, ZERO, 0), - (games.create_mixed_behav_game_efg(), 0, 0, 1, [1, 0, 1, 0, 1, 0], False, TOL, 9), - (games.create_mixed_behav_game_efg(), 0, 0, 1, [1, 0, 1, 0, 1, 0], True, ZERO, 9), - (games.create_mixed_behav_game_efg(), 1, 0, 0, [1, 0, 1, 0, 1, 0], False, TOL, 0), - (games.create_mixed_behav_game_efg(), 1, 0, 0, [1, 0, 1, 0, 1, 0], True, ZERO, 0), - (games.create_mixed_behav_game_efg(), 1, 0, 1, [1, 0, 1, 0, 1, 0], False, TOL, 8), - (games.create_mixed_behav_game_efg(), 1, 0, 1, [1, 0, 1, 0, 1, 0], True, ZERO, 8), - # Mixed Nash equilibrium - (games.create_mixed_behav_game_efg(), 0, 0, 0, ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], - True, ZERO, 0), - (games.create_mixed_behav_game_efg(), 0, 0, 1, ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], - True, ZERO, 0), - (games.create_mixed_behav_game_efg(), 1, 0, 0, ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], - True, ZERO, 0), - (games.create_mixed_behav_game_efg(), 1, 0, 1, ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], - True, ZERO, 0), - (games.create_mixed_behav_game_efg(), 2, 0, 0, ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], - True, ZERO, 0), - (games.create_mixed_behav_game_efg(), 2, 0, 1, ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], - True, ZERO, 0), - # uniform - (games.create_stripped_down_poker_efg(), 0, 0, 0, None, False, TOL, 0), - (games.create_stripped_down_poker_efg(), 0, 0, 1, None, False, TOL, 2.5), # 1.5 - (-1) - (games.create_stripped_down_poker_efg(), 0, 1, 0, None, False, TOL, 0), - (games.create_stripped_down_poker_efg(), 0, 1, 1, None, False, TOL, 0.5), # -0.5 - (-1) - (games.create_stripped_down_poker_efg(), 1, 0, 0, None, False, TOL, 0), - (games.create_stripped_down_poker_efg(), 1, 0, 1, None, False, TOL, 1), # -0 - (-1) - # mixed Nash equilibrium - (games.create_stripped_down_poker_efg(), 0, 0, 0, ["1", "0", "1/3", "2/3", "2/3", "1/3"], - True, ZERO, 0), - (games.create_stripped_down_poker_efg(), 0, 0, 1, ["1", "0", "1/3", "2/3", "2/3", "1/3"], - True, ZERO, "8/3"), # (2/3*2 + 1/3*1) - (-1) - ] + # uniform + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 0, None, False, TOL, 0), + (games.read_from_file("mixed_behavior_game.efg"), 0, 0, 1, None, False, TOL, 0), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 0, None, False, TOL, 0), + (games.read_from_file("mixed_behavior_game.efg"), 1, 0, 1, None, False, TOL, 0), + (games.read_from_file("mixed_behavior_game.efg"), 2, 0, 0, None, False, TOL, 0), + ( + games.read_from_file("mixed_behavior_game.efg"), + 2, + 0, + 1, + None, + False, + TOL, + 0.5, + ), # 3.5 - 3 + # U1 U2 U3 + ( + games.read_from_file("mixed_behavior_game.efg"), + 0, + 0, + 0, + [1, 0, 1, 0, 1, 0], + False, + TOL, + 0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 0, + 0, + 0, + [1, 0, 1, 0, 1, 0], + True, + ZERO, + 0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 0, + 0, + 1, + [1, 0, 1, 0, 1, 0], + False, + TOL, + 9, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 0, + 0, + 1, + [1, 0, 1, 0, 1, 0], + True, + ZERO, + 9, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 1, + 0, + 0, + [1, 0, 1, 0, 1, 0], + False, + TOL, + 0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 1, + 0, + 0, + [1, 0, 1, 0, 1, 0], + True, + ZERO, + 0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 1, + 0, + 1, + [1, 0, 1, 0, 1, 0], + False, + TOL, + 8, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 1, + 0, + 1, + [1, 0, 1, 0, 1, 0], + True, + ZERO, + 8, + ), + # Mixed Nash equilibrium + ( + games.read_from_file("mixed_behavior_game.efg"), + 0, + 0, + 0, + ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], + True, + ZERO, + 0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 0, + 0, + 1, + ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], + True, + ZERO, + 0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 1, + 0, + 0, + ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], + True, + ZERO, + 0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 1, + 0, + 1, + ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], + True, + ZERO, + 0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 2, + 0, + 0, + ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], + True, + ZERO, + 0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + 2, + 0, + 1, + ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], + True, + ZERO, + 0, + ), + # uniform + (games.create_stripped_down_poker_efg(), 0, 0, 0, None, False, TOL, 0), + (games.create_stripped_down_poker_efg(), 0, 0, 1, None, False, TOL, 2.5), # 1.5 - (-1) + (games.create_stripped_down_poker_efg(), 0, 1, 0, None, False, TOL, 0), + (games.create_stripped_down_poker_efg(), 0, 1, 1, None, False, TOL, 0.5), # -0.5 - (-1) + (games.create_stripped_down_poker_efg(), 1, 0, 0, None, False, TOL, 0), + (games.create_stripped_down_poker_efg(), 1, 0, 1, None, False, TOL, 1), # -0 - (-1) + # mixed Nash equilibrium + ( + games.create_stripped_down_poker_efg(), + 0, + 0, + 0, + ["1", "0", "1/3", "2/3", "2/3", "1/3"], + True, + ZERO, + 0, + ), + ( + games.create_stripped_down_poker_efg(), + 0, + 0, + 1, + ["1", "0", "1/3", "2/3", "2/3", "1/3"], + True, + ZERO, + "8/3", + ), # (2/3*2 + 1/3*1) - (-1) + ], ) -def test_action_regret_reference(game: gbt.Game, player_idx: int, infoset_idx: int, - action_idx: int, action_probs: None | list, rational_flag: bool, - tol: gbt.Rational | float, value: str | float): +def test_action_regret_reference( + game: gbt.Game, + player_idx: int, + infoset_idx: int, + action_idx: int, + action_probs: None | list, + rational_flag: bool, + tol: gbt.Rational | float, + value: str | float, +): action = game.players[player_idx].infosets[infoset_idx].actions[action_idx] profile = game.mixed_behavior_profile(rational=rational_flag) if action_probs: @@ -930,11 +1168,12 @@ def test_action_regret_reference(game: gbt.Game, player_idx: int, infoset_idx: i @pytest.mark.parametrize( "game,rational_flag", - [(games.create_mixed_behav_game_efg(), False), - (games.create_mixed_behav_game_efg(), True), - (games.create_stripped_down_poker_efg(), False), - (games.create_stripped_down_poker_efg(), True), - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), False), + (games.read_from_file("mixed_behavior_game.efg"), True), + (games.create_stripped_down_poker_efg(), False), + (games.create_stripped_down_poker_efg(), True), + ], ) def test_martingale_property_of_node_value(game: gbt.Game, rational_flag: bool): """Loops over all nodes and for non-chance, non-terminal nodes, this checks that the node @@ -955,10 +1194,12 @@ def test_martingale_property_of_node_value(game: gbt.Game, rational_flag: bool): @pytest.mark.parametrize( "game,rational_flag", - [(games.create_mixed_behav_game_efg(), False), - (games.create_mixed_behav_game_efg(), True), - (games.create_stripped_down_poker_efg(), False), - (games.create_stripped_down_poker_efg(), True)] + [ + (games.read_from_file("mixed_behavior_game.efg"), False), + (games.read_from_file("mixed_behavior_game.efg"), True), + (games.create_stripped_down_poker_efg(), False), + (games.create_stripped_down_poker_efg(), True), + ], ) def test_node_value_consistency(game: gbt.Game, rational_flag: bool): """Test that the profile's node value at the root for each player matches the profile's payoff @@ -971,114 +1212,281 @@ def test_node_value_consistency(game: gbt.Game, rational_flag: bool): @pytest.mark.parametrize( "game,action_probs,rational_flag,expected_value", [ - # uniform (non-Nash): - (games.create_mixed_behav_game_efg(), None, True, "1/16"), - (games.create_mixed_behav_game_efg(), None, False, 0.0625), - # four pure Nash equilibria: - (games.create_mixed_behav_game_efg(), [1.0, 0.0, 1.0, 0.0, 1.0, 0.0], False, 0), # U1 U2 U3 - (games.create_mixed_behav_game_efg(), ["1", "0", "1", "0", "1", "0"], True, 0), - (games.create_mixed_behav_game_efg(), ["1", "0", "0", "1", "0", "1"], True, 0), # U1 D2 D3 - (games.create_mixed_behav_game_efg(), [1.0, 0.0, 0.0, 1.0, 0, 1.0], False, 0), - (games.create_mixed_behav_game_efg(), ["0", "1", "1", "0", "0", "1"], True, 0), # D1 U2 D3 - (games.create_mixed_behav_game_efg(), [0.0, 1.0, 1.0, 0.0, 0, 1.0], False, 0), - (games.create_mixed_behav_game_efg(), ["0", "1", "0", "1", "1", "0"], True, 0), # D1 D2 U3 - (games.create_mixed_behav_game_efg(), [0.0, 1.0, 0.0, 1.0, 1.0, 0], False, 0), - # mixed Nash equilibrium (only rational tested): - (games.create_mixed_behav_game_efg(), ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], True, 0), - # non-Nash pure profile: D1 D2 D3 - (games.create_mixed_behav_game_efg(), [0.0, 1.0, 0.0, 1.0, 0.0, 1.0], False, 29.0), - (games.create_mixed_behav_game_efg(), ["0", "1", "0", "1", "0", "1"], True, "29"), - # uniform (non-Nash): - (games.create_stripped_down_poker_efg(), None, True, "15/8"), - (games.create_stripped_down_poker_efg(), None, False, 1.875), - # mixed Nash equilibrium (only rational tested): - (games.create_stripped_down_poker_efg(), ["1", "0", "1/3", "2/3", "2/3", "1/3"], True, 0), - # non-Nash pure profile: - # Raise at 1:1, Raise at 1:2, Meet at 2:1 - (games.create_stripped_down_poker_efg(), ["1", "0", "1", "0", "1", "0"], True, 1), - (games.create_stripped_down_poker_efg(), [1.0, 0.0, 1.0, 0.0, 1.0, 0.0], False, 1.0), - ] + # uniform (non-Nash): + (games.read_from_file("mixed_behavior_game.efg"), None, True, "1/16"), + (games.read_from_file("mixed_behavior_game.efg"), None, False, 0.0625), + # four pure Nash equilibria: + ( + games.read_from_file("mixed_behavior_game.efg"), + [1.0, 0.0, 1.0, 0.0, 1.0, 0.0], + False, + 0, + ), # U1 U2 U3 + (games.read_from_file("mixed_behavior_game.efg"), ["1", "0", "1", "0", "1", "0"], True, 0), + ( + games.read_from_file("mixed_behavior_game.efg"), + ["1", "0", "0", "1", "0", "1"], + True, + 0, + ), # U1 D2 D3 + (games.read_from_file("mixed_behavior_game.efg"), [1.0, 0.0, 0.0, 1.0, 0, 1.0], False, 0), + ( + games.read_from_file("mixed_behavior_game.efg"), + ["0", "1", "1", "0", "0", "1"], + True, + 0, + ), # D1 U2 D3 + (games.read_from_file("mixed_behavior_game.efg"), [0.0, 1.0, 1.0, 0.0, 0, 1.0], False, 0), + ( + games.read_from_file("mixed_behavior_game.efg"), + ["0", "1", "0", "1", "1", "0"], + True, + 0, + ), # D1 D2 U3 + (games.read_from_file("mixed_behavior_game.efg"), [0.0, 1.0, 0.0, 1.0, 1.0, 0], False, 0), + # mixed Nash equilibrium (only rational tested): + ( + games.read_from_file("mixed_behavior_game.efg"), + ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"], + True, + 0, + ), + # non-Nash pure profile: D1 D2 D3 + ( + games.read_from_file("mixed_behavior_game.efg"), + [0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + False, + 29.0, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + ["0", "1", "0", "1", "0", "1"], + True, + "29", + ), + # uniform (non-Nash): + (games.create_stripped_down_poker_efg(), None, True, "15/8"), + (games.create_stripped_down_poker_efg(), None, False, 1.875), + # mixed Nash equilibrium (only rational tested): + (games.create_stripped_down_poker_efg(), ["1", "0", "1/3", "2/3", "2/3", "1/3"], True, 0), + # non-Nash pure profile: + # Raise at 1:1, Raise at 1:2, Meet at 2:1 + (games.create_stripped_down_poker_efg(), ["1", "0", "1", "0", "1", "0"], True, 1), + (games.create_stripped_down_poker_efg(), [1.0, 0.0, 1.0, 0.0, 1.0, 0.0], False, 1.0), + ], ) -def test_agent_liap_value_reference(game: gbt.Game, action_probs: None | list, - rational_flag: bool, expected_value: str | float): +def test_agent_liap_value_reference( + game: gbt.Game, action_probs: None | list, rational_flag: bool, expected_value: str | float +): """Tests agent_liap_value under profile given by action_probs (which will be uniform if action_probs is None) """ profile = game.mixed_behavior_profile(rational=rational_flag) if action_probs: _set_action_probs(profile, action_probs, rational_flag) - assert ( - profile.agent_liap_value() == (gbt.Rational(expected_value) - if rational_flag else expected_value) + assert profile.agent_liap_value() == ( + gbt.Rational(expected_value) if rational_flag else expected_value ) @pytest.mark.parametrize( "game,action_probs,rational_flag,max_regret,agent_max_regret,liap_value,agent_liap_value", [ - # uniform (non-Nash): - (games.create_mixed_behav_game_efg(), None, True, "1/4", "1/4", "1/16", "1/16"), - (games.create_mixed_behav_game_efg(), None, False, 0.25, 0.25, 0.0625, 0.0625), - # Myerson fig 4.2 - (games.read_from_file("myerson_fig_4_2.efg"), [0, 1, 0, 1, 1, 0], True, 1, 0, 1, 0), - ] + # uniform (non-Nash): + ( + games.read_from_file("mixed_behavior_game.efg"), + None, + True, + "1/4", + "1/4", + "1/16", + "1/16", + ), + (games.read_from_file("mixed_behavior_game.efg"), None, False, 0.25, 0.25, 0.0625, 0.0625), + # Myerson fig 4.2 + (games.read_from_file("myerson_fig_4_2.efg"), [0, 1, 0, 1, 1, 0], True, 1, 0, 1, 0), + ], ) -def test_agent_max_regret_versus_non_agent(game: gbt.Game, action_probs: None | list, - rational_flag: bool, - max_regret: str | float, - agent_max_regret: str | float, - agent_liap_value: str | float, - liap_value: str | float, - ): +def test_agent_max_regret_versus_non_agent( + game: gbt.Game, + action_probs: None | list, + rational_flag: bool, + max_regret: str | float, + agent_max_regret: str | float, + agent_liap_value: str | float, + liap_value: str | float, +): profile = game.mixed_behavior_profile(rational=rational_flag) if action_probs: _set_action_probs(profile, action_probs, rational_flag) - assert (profile.max_regret() == (gbt.Rational(max_regret) if rational_flag else max_regret)) - assert ( - profile.agent_max_regret() == (gbt.Rational(agent_max_regret) - if rational_flag else agent_max_regret) + assert profile.max_regret() == (gbt.Rational(max_regret) if rational_flag else max_regret) + assert profile.agent_max_regret() == ( + gbt.Rational(agent_max_regret) if rational_flag else agent_max_regret ) - assert (profile.liap_value() == (gbt.Rational(liap_value) if rational_flag else liap_value)) - assert ( - profile.agent_liap_value() == (gbt.Rational(agent_liap_value) - if rational_flag else agent_liap_value) + assert profile.liap_value() == (gbt.Rational(liap_value) if rational_flag else liap_value) + assert profile.agent_liap_value() == ( + gbt.Rational(agent_liap_value) if rational_flag else agent_liap_value ) @pytest.mark.parametrize( "game,tol,probs,infoset_idx,member_idx,value,rational_flag", - [(games.create_mixed_behav_game_efg(), TOL, [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], 0, 0, 1.0, False), - (games.create_mixed_behav_game_efg(), TOL, [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], 1, 0, 0.8, False), - (games.create_mixed_behav_game_efg(), TOL, [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], 1, 1, 0.2, False), - (games.create_mixed_behav_game_efg(), TOL, [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], 2, 0, 0.32, False), - (games.create_mixed_behav_game_efg(), TOL, [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], 2, 1, 0.48, False), - (games.create_mixed_behav_game_efg(), ZERO, ["4/5", "1/5", "2/5", "3/5", "0", "1"], 0, 0, "1", - True), - (games.create_mixed_behav_game_efg(), ZERO, ["4/5", "1/5", "2/5", "3/5", "0", "1"], 1, 0, - "4/5", True), - (games.create_mixed_behav_game_efg(), ZERO, ["4/5", "1/5", "2/5", "3/5", "0", "1"], 1, 1, - "1/5", True), - (games.create_mixed_behav_game_efg(), ZERO, ["4/5", "1/5", "2/5", "3/5", "0", "1"], 2, 0, - "8/25", True), - (games.create_mixed_behav_game_efg(), ZERO, ["4/5", "1/5", "2/5", "3/5", "0", "1"], 2, 1, - "12/25", True), - (games.create_stripped_down_poker_efg(), ZERO, ["4/5", "1/5", "2/5", "3/5", "0", "1"], - 0, 0, "1", True), - (games.create_stripped_down_poker_efg(), ZERO, ["4/5", "1/5", "2/5", "3/5", "0", "1"], - 1, 0, "1", True), - (games.create_stripped_down_poker_efg(), ZERO, ["4/5", "1/5", "2/5", "3/5", "0", "1"], - 2, 0, "2/3", True), - (games.create_stripped_down_poker_efg(), ZERO, ["4/5", "1/5", "2/5", "3/5", "0", "1"], - 2, 1, "1/3", True), - (games.create_stripped_down_poker_efg(), ZERO, ["1", "0", "2/5", "3/5", "0", "1"], - 2, 0, "5/7", True), - (games.create_stripped_down_poker_efg(), ZERO, ["1", "0", "2/5", "3/5", "0", "1"], - 2, 1, "2/7", True), - ] - ) -def test_node_belief_reference(game: gbt.Game, tol: gbt.Rational | float, - probs: list, infoset_idx: int, member_idx: int, - value: str | float, rational_flag: bool): + [ + ( + games.read_from_file("mixed_behavior_game.efg"), + TOL, + [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], + 0, + 0, + 1.0, + False, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + TOL, + [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], + 1, + 0, + 0.8, + False, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + TOL, + [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], + 1, + 1, + 0.2, + False, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + TOL, + [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], + 2, + 0, + 0.32, + False, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + TOL, + [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], + 2, + 1, + 0.48, + False, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + ZERO, + ["4/5", "1/5", "2/5", "3/5", "0", "1"], + 0, + 0, + "1", + True, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + ZERO, + ["4/5", "1/5", "2/5", "3/5", "0", "1"], + 1, + 0, + "4/5", + True, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + ZERO, + ["4/5", "1/5", "2/5", "3/5", "0", "1"], + 1, + 1, + "1/5", + True, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + ZERO, + ["4/5", "1/5", "2/5", "3/5", "0", "1"], + 2, + 0, + "8/25", + True, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + ZERO, + ["4/5", "1/5", "2/5", "3/5", "0", "1"], + 2, + 1, + "12/25", + True, + ), + ( + games.create_stripped_down_poker_efg(), + ZERO, + ["4/5", "1/5", "2/5", "3/5", "0", "1"], + 0, + 0, + "1", + True, + ), + ( + games.create_stripped_down_poker_efg(), + ZERO, + ["4/5", "1/5", "2/5", "3/5", "0", "1"], + 1, + 0, + "1", + True, + ), + ( + games.create_stripped_down_poker_efg(), + ZERO, + ["4/5", "1/5", "2/5", "3/5", "0", "1"], + 2, + 0, + "2/3", + True, + ), + ( + games.create_stripped_down_poker_efg(), + ZERO, + ["4/5", "1/5", "2/5", "3/5", "0", "1"], + 2, + 1, + "1/3", + True, + ), + ( + games.create_stripped_down_poker_efg(), + ZERO, + ["1", "0", "2/5", "3/5", "0", "1"], + 2, + 0, + "5/7", + True, + ), + ( + games.create_stripped_down_poker_efg(), + ZERO, + ["1", "0", "2/5", "3/5", "0", "1"], + 2, + 1, + "2/7", + True, + ), + ], +) +def test_node_belief_reference( + game: gbt.Game, + tol: gbt.Rational | float, + probs: list, + infoset_idx: int, + member_idx: int, + value: str | float, + rational_flag: bool, +): profile = game.mixed_behavior_profile(rational=rational_flag) _set_action_probs(profile, probs, rational_flag) node = game.infosets[infoset_idx].members[member_idx] @@ -1088,9 +1496,10 @@ def test_node_belief_reference(game: gbt.Game, tol: gbt.Rational | float, @pytest.mark.parametrize( "game,rational_flag", - [(games.create_stripped_down_poker_efg(), True), - (games.create_stripped_down_poker_efg(), False), - ] + [ + (games.create_stripped_down_poker_efg(), True), + (games.create_stripped_down_poker_efg(), False), + ], ) def test_payoff_value_error_with_chance_player(game: gbt.Game, rational_flag: bool): """Ensure a value error is thrown when we call payoff for a chance player""" @@ -1101,9 +1510,10 @@ def test_payoff_value_error_with_chance_player(game: gbt.Game, rational_flag: bo @pytest.mark.parametrize( "game,rational_flag", - [(games.create_stripped_down_poker_efg(), True), - (games.create_stripped_down_poker_efg(), False), - ] + [ + (games.create_stripped_down_poker_efg(), True), + (games.create_stripped_down_poker_efg(), False), + ], ) def test_infoset_value_error_with_chance_player_infoset(game: gbt.Game, rational_flag: bool): """Ensure a value error is raised when we call action value for a chance action""" @@ -1114,9 +1524,10 @@ def test_infoset_value_error_with_chance_player_infoset(game: gbt.Game, rational @pytest.mark.parametrize( "game,rational_flag", - [(games.create_stripped_down_poker_efg(), True), - (games.create_stripped_down_poker_efg(), False), - ] + [ + (games.create_stripped_down_poker_efg(), True), + (games.create_stripped_down_poker_efg(), False), + ], ) def test_action_value_error_with_chance_player_action(game: gbt.Game, rational_flag: bool): """Ensure a value error is raised when we call action value for a chance action""" @@ -1125,9 +1536,14 @@ def test_action_value_error_with_chance_player_action(game: gbt.Game, rational_f game.mixed_behavior_profile(rational=rational_flag).action_value(chance_action) -def _get_answers_one_order(game: gbt.Game, action_probs_1st: tuple, action_probs_2nd: tuple, - rational_flag: bool, func_to_test: typing.Callable, - object_to_test_on: typing.Any): +def _get_answers_one_order( + game: gbt.Game, + action_probs_1st: tuple, + action_probs_2nd: tuple, + rational_flag: bool, + func_to_test: typing.Callable, + object_to_test_on: typing.Any, +): """helper function for the 'profile_order' caching tests""" ret = dict() profile = game.mixed_behavior_profile(rational=rational_flag) @@ -1138,14 +1554,27 @@ def _get_answers_one_order(game: gbt.Game, action_probs_1st: tuple, action_probs return ret -def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: tuple, - rational_flag: bool, func_to_test: typing.Callable, - objects_to_test_on: typing.Collection): +def _get_and_check_answers( + game: gbt.Game, + action_probs1: tuple, + action_probs2: tuple, + rational_flag: bool, + func_to_test: typing.Callable, + objects_to_test_on: typing.Collection, +): """helper function for the 'profile_order' caching tests""" - order1_answers = {o: _get_answers_one_order(game, action_probs1, action_probs2, rational_flag, - func_to_test, o) for o in objects_to_test_on} - order2_answers = {o: _get_answers_one_order(game, action_probs2, action_probs1, rational_flag, - func_to_test, o) for o in objects_to_test_on} + order1_answers = { + o: _get_answers_one_order( + game, action_probs1, action_probs2, rational_flag, func_to_test, o + ) + for o in objects_to_test_on + } + order2_answers = { + o: _get_answers_one_order( + game, action_probs2, action_probs1, rational_flag, func_to_test, o + ) + for o in objects_to_test_on + } assert order1_answers == order2_answers @@ -1162,177 +1591,481 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: @pytest.mark.parametrize( "game,action_probs1,action_probs2,rational_flag,func_to_test,objects_to_test", [ - ###################################################################################### - # belief (at nodes) - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.belief(y), lambda x: x.nodes), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.belief(y), lambda x: x.nodes), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.belief(y), lambda x: x.nodes), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.belief(y), lambda x: x.nodes), - ###################################################################################### - # realiz_prob (at nodes) - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.realiz_prob(y), lambda x: x.nodes), - ###################################################################################### - # infoset_prob - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.infoset_prob(y), lambda x: x.infosets), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.infoset_prob(y), lambda x: x.infosets), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.infoset_prob(y), lambda x: x.infosets), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.infoset_prob(y), lambda x: x.infosets), - ###################################################################################### - # infoset_value - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.infoset_value(y), lambda x: x.infosets), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.infoset_value(y), lambda x: x.infosets), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.infoset_value(y), lambda x: x.infosets), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.infoset_value(y), lambda x: x.infosets), - ###################################################################################### - # action_value - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.action_value(y), lambda x: x.actions), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.action_value(y), lambda x: x.actions), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.action_value(y), lambda x: x.actions), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.action_value(y), lambda x: x.actions), - ###################################################################################### - # regret (for actions) - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.action_regret(y), lambda x: x.actions), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.action_regret(y), lambda x: x.actions), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.action_regret(y), lambda x: x.actions), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.action_regret(y), lambda x: x.actions), - ###################################################################################### - # node_value - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes))), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes))), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes))), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.node_value(player=y[0], node=y[1]), - lambda x: list(product(x.players, x.nodes))), - ###################################################################################### - # agent_liap_value (of profile, hence [1] for objects_to_test, - # any singleton collection would do) - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.agent_liap_value(), lambda x: [1]), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.agent_liap_value(), lambda x: [1]), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.agent_liap_value(), lambda x: [1]), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.agent_liap_value(), lambda x: [1]), - ###################################################################################### - # liap_value (of profile, hence [1] for objects_to_test, - # any singleton collection would do) - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.liap_value(), lambda x: [1]), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.liap_value(), lambda x: [1]), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.liap_value(), lambda x: [1]), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.liap_value(), lambda x: [1]), - ###################################################################################### - # agent_max_regret (of profile, hence [1] for objects_to_test, - # any singleton collection would do) - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.agent_max_regret(), lambda x: [1]), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.agent_max_regret(), lambda x: [1]), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.agent_max_regret(), lambda x: [1]), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.agent_max_regret(), lambda x: [1]), - ###################################################################################### - # max_regret (of profile, hence [1] for objects_to_test, - # any singleton collection would do) - (games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda x, y: x.max_regret(), lambda x: [1]), - (games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.max_regret(), lambda x: [1]), - (games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda x, y: x.max_regret(), lambda x: [1]), - (games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda x, y: x.max_regret(), lambda x: [1]), - ] + ###################################################################################### + # belief (at nodes) + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.belief(y), + lambda x: x.nodes, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.belief(y), + lambda x: x.nodes, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.belief(y), + lambda x: x.nodes, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.belief(y), + lambda x: x.nodes, + ), + ###################################################################################### + # realiz_prob (at nodes) + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.realiz_prob(y), + lambda x: x.nodes, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.realiz_prob(y), + lambda x: x.nodes, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.realiz_prob(y), + lambda x: x.nodes, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.realiz_prob(y), + lambda x: x.nodes, + ), + ###################################################################################### + # infoset_prob + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.infoset_prob(y), + lambda x: x.infosets, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.infoset_prob(y), + lambda x: x.infosets, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.infoset_prob(y), + lambda x: x.infosets, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.infoset_prob(y), + lambda x: x.infosets, + ), + ###################################################################################### + # infoset_value + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.infoset_value(y), + lambda x: x.infosets, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.infoset_value(y), + lambda x: x.infosets, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.infoset_value(y), + lambda x: x.infosets, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.infoset_value(y), + lambda x: x.infosets, + ), + ###################################################################################### + # action_value + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.action_value(y), + lambda x: x.actions, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.action_value(y), + lambda x: x.actions, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.action_value(y), + lambda x: x.actions, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.action_value(y), + lambda x: x.actions, + ), + ###################################################################################### + # regret (for actions) + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.action_regret(y), + lambda x: x.actions, + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.action_regret(y), + lambda x: x.actions, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.action_regret(y), + lambda x: x.actions, + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.action_regret(y), + lambda x: x.actions, + ), + ###################################################################################### + # node_value + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.node_value(player=y[0], node=y[1]), + lambda x: list(product(x.players, x.nodes)), + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.node_value(player=y[0], node=y[1]), + lambda x: list(product(x.players, x.nodes)), + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.node_value(player=y[0], node=y[1]), + lambda x: list(product(x.players, x.nodes)), + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.node_value(player=y[0], node=y[1]), + lambda x: list(product(x.players, x.nodes)), + ), + ###################################################################################### + # agent_liap_value (of profile, hence [1] for objects_to_test, + # any singleton collection would do) + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.agent_liap_value(), + lambda x: [1], + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.agent_liap_value(), + lambda x: [1], + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.agent_liap_value(), + lambda x: [1], + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.agent_liap_value(), + lambda x: [1], + ), + ###################################################################################### + # liap_value (of profile, hence [1] for objects_to_test, + # any singleton collection would do) + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.liap_value(), + lambda x: [1], + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.liap_value(), + lambda x: [1], + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.liap_value(), + lambda x: [1], + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.liap_value(), + lambda x: [1], + ), + ###################################################################################### + # agent_max_regret (of profile, hence [1] for objects_to_test, + # any singleton collection would do) + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.agent_max_regret(), + lambda x: [1], + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.agent_max_regret(), + lambda x: [1], + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.agent_max_regret(), + lambda x: [1], + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.agent_max_regret(), + lambda x: [1], + ), + ###################################################################################### + # max_regret (of profile, hence [1] for objects_to_test, + # any singleton collection would do) + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda x, y: x.max_regret(), + lambda x: [1], + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.max_regret(), + lambda x: [1], + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda x, y: x.max_regret(), + lambda x: [1], + ), + ( + games.create_stripped_down_poker_efg(), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda x, y: x.max_regret(), + lambda x: [1], + ), + ], ) -def test_profile_order_consistency(game: gbt.Game, - action_probs1: tuple, - action_probs2: tuple, rational_flag: bool, - func_to_test: typing.Callable, - objects_to_test: typing.Callable): - _get_and_check_answers(game, action_probs1, action_probs2, rational_flag, func_to_test, - objects_to_test(game)) +def test_profile_order_consistency( + game: gbt.Game, + action_probs1: tuple, + action_probs2: tuple, + rational_flag: bool, + func_to_test: typing.Callable, + objects_to_test: typing.Callable, +): + _get_and_check_answers( + game, action_probs1, action_probs2, rational_flag, func_to_test, objects_to_test(game) + ) @pytest.mark.parametrize( "game,rational_flag,data", - [(games.create_mixed_behav_game_efg(), True, [[[0, 1]], [[0, 1]], [[1, 0]]]), - (games.create_mixed_behav_game_efg(), True, [[["1/5", "4/5"]], [["1/4", "3/4"]], [[1, 0]]]), - (games.create_stripped_down_poker_efg(), True, [[[1/5, 4/5], [3/5, 2/5]], [[1/4, 3/4]]]), - (games.create_mixed_behav_game_efg(), False, [[[0, 1]], [[1, 0]], [[1, 0]]]), - (games.create_mixed_behav_game_efg(), False, [[[1/5, 4/5]], [[1/4, 3/4]], [[1, 0]]]), - (games.create_stripped_down_poker_efg(), False, [[[1/5, 4/5], [3/5, 2/5]], [[1/4, 3/4]]]) - ] + [ + (games.read_from_file("mixed_behavior_game.efg"), True, [[[0, 1]], [[0, 1]], [[1, 0]]]), + ( + games.read_from_file("mixed_behavior_game.efg"), + True, + [[["1/5", "4/5"]], [["1/4", "3/4"]], [[1, 0]]], + ), + ( + games.create_stripped_down_poker_efg(), + True, + [[[1 / 5, 4 / 5], [3 / 5, 2 / 5]], [[1 / 4, 3 / 4]]], + ), + (games.read_from_file("mixed_behavior_game.efg"), False, [[[0, 1]], [[1, 0]], [[1, 0]]]), + ( + games.read_from_file("mixed_behavior_game.efg"), + False, + [[[1 / 5, 4 / 5]], [[1 / 4, 3 / 4]], [[1, 0]]], + ), + ( + games.create_stripped_down_poker_efg(), + False, + [[[1 / 5, 4 / 5], [3 / 5, 2 / 5]], [[1 / 4, 3 / 4]]], + ), + ], ) def test_specific_profile(game: gbt.Game, rational_flag: bool, data: list): """Test that the mixed behavior profile is initialized from a specific distribution for each player over his actions. """ profile = game.mixed_behavior_profile(rational=rational_flag, data=data) - for (action, prob) in zip(game.actions, [k for i in data for j in i for k in j], strict=True): + for action, prob in zip(game.actions, [k for i in data for j in i for k in j], strict=True): assert profile[action] == (gbt.Rational(prob) if rational_flag else prob) @pytest.mark.parametrize( "game,rational_flag,data", - [(games.create_mixed_behav_game_efg(), True, - [[[0, 1, 0]], [[1, 0]], [["1/2", "1/2"]]]), - (games.create_mixed_behav_game_efg(), True, - [[[0, 1]], [[1, 0]], [[1, 0]], [[0, 1]]]), - (games.create_stripped_down_poker_efg(), True, - [[["1/5", "4/5"], ["3/5", "2/5"]], [["1/4", "3/4"], ["1/4", "3/4"]]]), - (games.create_el_farol_bar_game_efg(), True, - [[4/9, 5/9], [0], [1/2, 1/2], [11/12, 1/12], [1/2, 1/2]]), - (games.create_el_farol_bar_game_efg(), True, - [[1/2, 1/2]]), - (games.create_mixed_behav_game_efg(), False, - [[[0, 1, 0]], [[1, 0]], [[1, 0]]]), - (games.create_mixed_behav_game_efg(), False, - [[[0, 1]], [[1, 0]], [[1, 0]], [[0, 1]]]), - (games.create_stripped_down_poker_efg(), False, - [[[1/5, 4/5], [3/5, 2/5]], [[1/4, 3/4], [1/4, 3/4]]]), - (games.create_el_farol_bar_game_efg(), False, - [[4/9, 5/9], [0], [1/2, 1/2], [11/12, 1/12], [1/2, 1/2]]), - (games.create_el_farol_bar_game_efg(), False, - [[1/2, 1/2]]) - ] + [ + ( + games.read_from_file("mixed_behavior_game.efg"), + True, + [[[0, 1, 0]], [[1, 0]], [["1/2", "1/2"]]], + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + True, + [[[0, 1]], [[1, 0]], [[1, 0]], [[0, 1]]], + ), + ( + games.create_stripped_down_poker_efg(), + True, + [[["1/5", "4/5"], ["3/5", "2/5"]], [["1/4", "3/4"], ["1/4", "3/4"]]], + ), + ( + games.read_from_file("el_farol_bar.efg"), + True, + [[4 / 9, 5 / 9], [0], [1 / 2, 1 / 2], [11 / 12, 1 / 12], [1 / 2, 1 / 2]], + ), + (games.read_from_file("el_farol_bar.efg"), True, [[1 / 2, 1 / 2]]), + ( + games.read_from_file("mixed_behavior_game.efg"), + False, + [[[0, 1, 0]], [[1, 0]], [[1, 0]]], + ), + ( + games.read_from_file("mixed_behavior_game.efg"), + False, + [[[0, 1]], [[1, 0]], [[1, 0]], [[0, 1]]], + ), + ( + games.create_stripped_down_poker_efg(), + False, + [[[1 / 5, 4 / 5], [3 / 5, 2 / 5]], [[1 / 4, 3 / 4], [1 / 4, 3 / 4]]], + ), + ( + games.read_from_file("el_farol_bar.efg"), + False, + [[4 / 9, 5 / 9], [0], [1 / 2, 1 / 2], [11 / 12, 1 / 12], [1 / 2, 1 / 2]], + ), + (games.read_from_file("el_farol_bar.efg"), False, [[1 / 2, 1 / 2]]), + ], ) def test_profile_data_error(game: gbt.Game, rational_flag: bool, data: list): """Test to ensure a pygambit.ValueError is raised when the data do not @@ -1345,11 +2078,18 @@ def test_profile_data_error(game: gbt.Game, rational_flag: bool, data: list): @pytest.mark.parametrize( "game,rational_flag,data", - [(games.create_coord_4x4_nfg(), True, - [["1/5", "2/5", 0, "2/5"], ["1/4", "3/8", "1/4", "3/8"]]), - (games.create_coord_4x4_nfg(), False, - [[1/5, 2/5, 0/5, 2/5], [1/4, 3/8, 1/4, 3/8]]), - ] + [ + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + True, + [["1/5", "2/5", 0, "2/5"], ["1/4", "3/8", "1/4", "3/8"]], + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + False, + [[1 / 5, 2 / 5, 0 / 5, 2 / 5], [1 / 4, 3 / 8, 1 / 4, 3 / 8]], + ), + ], ) def test_tree_representation_error(game: gbt.Game, rational_flag: bool, data: list): """Test to ensure a pygambit.UndefinedOperationError is raised when the game diff --git a/tests/test_extensive.py b/tests/test_extensive.py index 41528afbe..f97584844 100644 --- a/tests/test_extensive.py +++ b/tests/test_extensive.py @@ -123,7 +123,7 @@ def test_outcome_index_exception_label(): ############################################################################### # 1 player; reduction; generic payoffs ( - games.create_reduction_one_player_generic_payoffs_efg(), + games.read_from_file("reduction_one_player_generic_payoffs.efg"), [["11", "12", "2*", "3*", "4*"]], [np.array(range(1, 6))], ), @@ -168,7 +168,7 @@ def test_outcome_index_exception_label(): ), # 2-player (zero-sum) game; reduction for both players; generic payoffs ( - games.create_reduction_generic_payoffs_efg(), + games.read_from_file("reduction_generic_payoffs.efg"), [ ["1*1", "1*2", "211", "212", "221", "222"], ["11*", "12*", "2**", "3*1", "3*2", "4**"], @@ -210,7 +210,7 @@ def test_outcome_index_exception_label(): ), # # 2-player game from GTE survey; reduction for both players; payoff ties ( - games.create_reduction_both_players_payoff_ties_efg(), + games.read_from_file("reduction_both_players_payoff_ties_GTE_survey.efg"), [ ["1*", "2*", "31", "32", "4*"], [ @@ -401,28 +401,28 @@ def test_reduced_strategic_form( "standard,modified", [ ( - games.create_two_player_perfect_info_win_lose_efg(), - games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True) + games.read_from_file("two_player_perfect_info_win_lose.efg"), + games.read_from_file("two_player_perfect_info_win_lose_with_nonterm_outcomes.efg") ), ( - games.create_3_player_with_internal_outcomes_efg(), - games.create_3_player_with_internal_outcomes_efg(nonterm_outcomes=True) + games.read_from_file("3_player.efg"), + games.read_from_file("3_player_with_nonterm_outcomes.efg") ), ( - games.create_chance_in_middle_efg(), - games.create_chance_in_middle_efg(nonterm_outcomes=True) + games.read_from_file("chance_in_middle.efg"), + games.read_from_file("chance_in_middle_with_nonterm_outcomes.efg") ), ( - games.create_non_zero_sum_lacking_outcome_efg(), - games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True) + games.read_from_file("2_player_non_zero_sum.efg"), + games.read_from_file("2_player_non_zero_sum_missing_term_outcome.efg"), ), ( - games.create_entry_accomodation_efg(), - games.create_entry_accomodation_efg(nonterm_outcomes=True) + games.read_from_file("entry_accommodation.efg"), + games.read_from_file("entry_accommodation_with_nonterm_outcomes.efg") ), ( - games.create_three_action_internal_outcomes_efg(), - games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True) + games.read_from_file("2_player_chance.efg"), + games.read_from_file("2_player_chance_nonterm_outcomes_and_missing_term_outcomes.efg"), ), ( games.create_kuhn_poker_efg(), diff --git a/tests/test_games/2_player_chance.efg b/tests/test_games/2_player_chance.efg new file mode 100644 index 000000000..3362390d5 --- /dev/null +++ b/tests/test_games/2_player_chance.efg @@ -0,0 +1,24 @@ +EFG 2 R "" { "1" "2" } +"" + +c "" 1 "" { "H" 1/2 "L" 1/2 } 0 +p "" 1 1 "" { "A" "B" "C" } 0 +p "" 2 1 "" { "X" "Y" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +p "" 2 2 "" { "X" "Y" } 0 +t "" 2 "-1" { -1, 1 } +t "" 1 "1" { 1, -1 } +p "" 2 3 "" { "X" "Y" } 0 +t "" 1 "1" { 1, -1 } +t "" 5 "0" { 0, 0 } +p "" 1 2 "" { "A" "B" "C" } 0 +p "" 2 1 "" { "X" "Y" } 0 +t "" 5 "0" { 0, 0 } +t "" 1 "1" { 1, -1 } +p "" 2 2 "" { "X" "Y" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +p "" 2 3 "" { "X" "Y" } 0 +t "" 2 "-1" { -1, 1 } +t "" 1 "1" { 1, -1 } diff --git a/tests/test_games/2_player_chance_nonterm_outcomes_and_missing_term_outcomes.efg b/tests/test_games/2_player_chance_nonterm_outcomes_and_missing_term_outcomes.efg new file mode 100644 index 000000000..2a9099273 --- /dev/null +++ b/tests/test_games/2_player_chance_nonterm_outcomes_and_missing_term_outcomes.efg @@ -0,0 +1,24 @@ +EFG 2 R "" { "1" "2" } +"" + +c "" 1 "" { "H" 1/2 "L" 1/2 } 0 +p "" 1 1 "" { "A" "B" "C" } 0 +p "" 2 1 "" { "X" "Y" } 1 "1" { 1, -1 } +t "" 0 +t "" 4 "-2" { -2, 2 } +p "" 2 2 "" { "X" "Y" } 0 +t "" 2 "-1" { -1, 1 } +t "" 1 "1" { 1, -1 } +p "" 2 3 "" { "X" "Y" } 0 +t "" 1 "1" { 1, -1 } +t "" 0 +p "" 1 2 "" { "A" "B" "C" } 0 +p "" 2 1 "" { "X" "Y" } 0 +t "" 0 +t "" 1 "1" { 1, -1 } +p "" 2 2 "" { "X" "Y" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +p "" 2 3 "" { "X" "Y" } 2 "-1" { -1, 1 } +t "" 0 +t "" 3 "2" { 2, -2 } diff --git a/tests/test_games/2_player_non_zero_sum.efg b/tests/test_games/2_player_non_zero_sum.efg new file mode 100644 index 000000000..87a9e7adf --- /dev/null +++ b/tests/test_games/2_player_non_zero_sum.efg @@ -0,0 +1,18 @@ +EFG 2 R "Non constant-sum game lacking outcome" { "1" "2" } +"" + +c "" 1 "" { "H" 1/2 "T" 1/2 } 0 +p "" 1 1 "" { "A" "B" } 0 +p "" 2 1 "" { "X" "Y" } 0 +t "" 1 "" { 2, 1 } +t "" 2 "" { -1, 2 } +p "" 2 1 "" { "X" "Y" } 0 +t "" 3 "" { 1, -1 } +t "" 4 "" { 0, 0 } +p "" 1 1 "" { "A" "B" } 0 +p "" 2 1 "" { "X" "Y" } 0 +t "" 5 "" { 1, 0 } +t "" 6 "" { 0, 1 } +p "" 2 1 "" { "X" "Y" } 0 +t "" 7 "" { -1, 1 } +t "" 8 "" { 2, -1 } diff --git a/tests/test_games/2_player_non_zero_sum_missing_term_outcome.efg b/tests/test_games/2_player_non_zero_sum_missing_term_outcome.efg new file mode 100644 index 000000000..a7fb080e1 --- /dev/null +++ b/tests/test_games/2_player_non_zero_sum_missing_term_outcome.efg @@ -0,0 +1,18 @@ +EFG 2 R "Non constant-sum game lacking outcome" { "1" "2" } +"" + +c "" 1 "" { "H" 1/2 "T" 1/2 } 0 +p "" 1 1 "" { "A" "B" } 0 +p "" 2 1 "" { "X" "Y" } 0 +t "" 1 "" { 2, 1 } +t "" 2 "" { -1, 2 } +p "" 2 1 "" { "X" "Y" } 0 +t "" 3 "" { 1, -1 } +t "" 0 +p "" 1 1 "" { "A" "B" } 0 +p "" 2 1 "" { "X" "Y" } 0 +t "" 4 "" { 1, 0 } +t "" 5 "" { 0, 1 } +p "" 2 1 "" { "X" "Y" } 0 +t "" 6 "" { -1, 1 } +t "" 7 "" { 2, -1 } diff --git a/tests/test_games/2x2_bimatrix_all_zero_payoffs.nfg b/tests/test_games/2x2_bimatrix_all_zero_payoffs.nfg new file mode 100644 index 000000000..7a290820d --- /dev/null +++ b/tests/test_games/2x2_bimatrix_all_zero_payoffs.nfg @@ -0,0 +1,14 @@ +NFG 1 R "2x2 bimatrix game with all zero payoffs, with strategy labels" { "Joe" "Dan" } + +{ { "cooperate" "defect" } +{ "defect" "defect" } +} +"" + +{ +{ "" 0, 0 } +{ "" 0, 0 } +{ "" 0, 0 } +{ "" 0, 0 } +} +1 2 3 4 diff --git a/tests/test_games/2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg b/tests/test_games/2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg new file mode 100644 index 000000000..f28f234f6 --- /dev/null +++ b/tests/test_games/2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg @@ -0,0 +1,20 @@ +NFG 1 R "2x2x2 game with 2 pure and 1 mixed equilibrium" +{ "Player 1" "Player 2" "Player 3" } { 2 2 2 } +" +- This comes from a local max cut instance: + players {1,2,3} are nodes; edge weight{1,2} = 2; weight{1,3} = -1; weight{2,3} = 2 +- Pure strategies {a,b} encode if respective player is on left or right of the cut +- The payoff to a player is the sum of their incident edges across the implied cut +- Pure equilibrium iff local max cuts; in addition, uniform mixture is an equilibrium +- Equilibrium analysis for pure profiles: + a a a: 0 0 0 -- Not Nash (regrets: 1, 4, 1) + b a a: 1 2 -1 -- Not Nash (regrets: 0, 0, 3) + a b a: 2 4 2 -- Nash (global max cut) + b b a: -1 2 1 -- Not Nash (regrets: 3, 0, 0) + a a b: -1 2 1 -- Not Nash (regrets: 3, 0, 0) + b a b: 2 4 2 -- Nash (global max cut) + a b b: 1 2 -1 -- Not Nash (regrets: 0, 0, 3) + b b b: 0 0 0 -- Not Nash (regrets: 1, 4, 1) +" + +0 0 0 1 2 -1 2 4 2 -1 2 1 -1 2 1 2 4 2 1 2 -1 0 0 0 diff --git a/tests/test_games/2x2x2_nfg_with_two_pure_one_mixed_eq.nfg b/tests/test_games/2x2x2_nfg_with_two_pure_one_mixed_eq.nfg deleted file mode 100644 index 8c4e999d3..000000000 --- a/tests/test_games/2x2x2_nfg_with_two_pure_one_mixed_eq.nfg +++ /dev/null @@ -1,4 +0,0 @@ -NFG 1 R "2x2x2 game with 2 pure and 1 mixed equilibrium" -{ "Player 1" "Player 2" "Player 3" } { 2 2 2 } - -0 0 0 1 2 -1 2 4 2 -1 2 1 -1 2 1 2 4 2 1 2 -1 0 0 0 diff --git a/tests/test_games/3_player.efg b/tests/test_games/3_player.efg new file mode 100644 index 000000000..c1c89ab57 --- /dev/null +++ b/tests/test_games/3_player.efg @@ -0,0 +1,24 @@ +EFG 2 R "3 player game" { "1" "2" "3" } +"" + +c "" 1 "" { "H" 1/2 "T" 1/2 } 0 +p "" 1 1 "" { "a" "b" } 0 +p "" 2 1 "" { "A" "B" } 0 +p "" 3 2 "" { "Y" "Z" } 0 +t "" 1 "" { 3, 1, 4 } +t "" 2 "" { 4, 0, 1 } +p "" 3 2 "" { "Y" "Z" } 0 +t "" 10 "" { 0, 0, 0 } +t "" 10 "" { 0, 0, 0 } +p "" 3 1 "" { "W" "X" } 0 +t "" 3 "" { 1, 3, 2 } +p "" 2 2 "" { "C" "D" } 0 +t "" 4 "" { 2, 4, 1 } +t "" 5 "" { 4, 1, 3 } +p "" 1 2 "" { "c" "d" } 0 +p "" 2 1 "" { "A" "B" } 0 +t "" 6 "" { 2, 2, 4 } +t "" 7 "" { 3, 1, 1 } +p "" 3 1 "" { "W" "X" } 0 +t "" 8 "" { 0, 4, 2 } +t "" 9 "" { 1, 2, 3 } diff --git a/tests/test_games/3_player_with_nonterm_outcomes.efg b/tests/test_games/3_player_with_nonterm_outcomes.efg new file mode 100644 index 000000000..85c11d24a --- /dev/null +++ b/tests/test_games/3_player_with_nonterm_outcomes.efg @@ -0,0 +1,24 @@ +EFG 2 R "3 player game" { "1" "2" "3" } +"" + +c "" 1 "" { "H" 1/2 "T" 1/2 } 0 +p "" 1 1 "" { "a" "b" } 0 +p "" 2 1 "" { "A" "B" } 0 +p "" 3 2 "" { "Y" "Z" } 0 +t "" 1 "" { 3, 1, 4 } +t "" 2 "" { 4, 0, 1 } +p "" 3 2 "" { "Y" "Z" } 0 +t "" 0 +t "" 0 +p "" 3 1 "" { "W" "X" } 0 +t "" 3 "" { 1, 3, 2 } +p "" 2 2 "" { "C" "D" } 0 +t "" 4 "" { 2, 4, 1 } +t "" 5 "" { 4, 1, 3 } +p "" 1 2 "" { "c" "d" } 6 "" { 1, 2, 3 } +p "" 2 1 "" { "A" "B" } 0 +t "" 7 "" { 1, 0, 1 } +t "" 8 "" { 2, -1, -2 } +p "" 3 1 "" { "W" "X" } 0 +t "" 9 "" { -1, 2, -1 } +t "" 0 diff --git a/tests/test_games/chance_in_middle.efg b/tests/test_games/chance_in_middle.efg new file mode 100644 index 000000000..c874b3f99 --- /dev/null +++ b/tests/test_games/chance_in_middle.efg @@ -0,0 +1,34 @@ +EFG 2 R "Chance in middle game" { "1" "2" } +"" + +p "" 1 1 "" { "A" "B" } 0 +c "" 1 "" { "H" 1/5 "L" 4/5 } 0 +p "" 2 1 "" { "X" "Y" } 0 +p "" 1 2 "" { "C" "D" } 0 +t "" 6 "0" { 0, 0 } +t "" 3 "-2" { -2, 2 } +p "" 1 3 "" { "C" "D" } 0 +t "" 5 "-0.5" { -1/2, 1/2 } +t "" 7 "-1.5" { -3/2, 3/2 } +p "" 2 2 "" { "X" "Y" } 0 +p "" 1 2 "" { "C" "D" } 0 +t "" 4 "0.5" { 1/2, -1/2 } +t "" 5 "-0.5" { -1/2, 1/2 } +p "" 1 3 "" { "C" "D" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +c "" 2 "" { "H" 7/10 "L" 3/10 } 0 +p "" 2 1 "" { "X" "Y" } 0 +p "" 1 4 "" { "C" "D" } 0 +t "" 4 "0.5" { 1/2, -1/2 } +t "" 5 "-0.5" { -1/2, 1/2 } +p "" 1 5 "" { "C" "D" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +p "" 2 2 "" { "X" "Y" } 0 +p "" 1 4 "" { "C" "D" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +p "" 1 5 "" { "C" "D" } 0 +t "" 4 "0.5" { 1/2, -1/2 } +t "" 5 "-0.5" { -1/2, 1/2 } diff --git a/tests/test_games/chance_in_middle_with_nonterm_outcomes.efg b/tests/test_games/chance_in_middle_with_nonterm_outcomes.efg new file mode 100644 index 000000000..7f7cd2132 --- /dev/null +++ b/tests/test_games/chance_in_middle_with_nonterm_outcomes.efg @@ -0,0 +1,34 @@ +EFG 2 R "Chance in middle game" { "1" "2" } +"" + +p "" 1 1 "" { "A" "B" } 0 +c "" 1 "" { "H" 1/5 "L" 4/5 } 0 +p "" 2 1 "" { "X" "Y" } 8 "a" { -1, 1 } +p "" 1 2 "" { "C" "D" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +p "" 1 3 "" { "C" "D" } 0 +t "" 4 "0.5" { 1/2, -1/2 } +t "" 5 "-0.5" { -1/2, 1/2 } +p "" 2 2 "" { "X" "Y" } 0 +p "" 1 2 "" { "C" "D" } 0 +t "" 4 "0.5" { 1/2, -1/2 } +t "" 5 "-0.5" { -1/2, 1/2 } +p "" 1 3 "" { "C" "D" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +c "" 2 "" { "H" 7/10 "L" 3/10 } 0 +p "" 2 1 "" { "X" "Y" } 0 +p "" 1 4 "" { "C" "D" } 0 +t "" 4 "0.5" { 1/2, -1/2 } +t "" 5 "-0.5" { -1/2, 1/2 } +p "" 1 5 "" { "C" "D" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +p "" 2 2 "" { "X" "Y" } 0 +p "" 1 4 "" { "C" "D" } 0 +t "" 1 "1" { 1, -1 } +t "" 2 "-1" { -1, 1 } +p "" 1 5 "" { "C" "D" } 0 +t "" 4 "0.5" { 1/2, -1/2 } +t "" 5 "-0.5" { -1/2, 1/2 } diff --git a/tests/test_games/entry_accommodation.efg b/tests/test_games/entry_accommodation.efg new file mode 100644 index 000000000..dfd383ba7 --- /dev/null +++ b/tests/test_games/entry_accommodation.efg @@ -0,0 +1,14 @@ +EFG 2 R "Entry-accomodation game" { "1" "2" } +"" + +p "" 1 1 "" { "S" "T" } 0 +p "" 2 1 "" { "E" "O" } 0 +p "" 1 2 "" { "A" "F" } 0 +t "" 1 "" { 3, 2 } +t "" 2 "" { 0, 1 } +t "" 3 "" { 1, 3 } +p "" 2 1 "" { "E" "O" } 0 +p "" 1 3 "" { "A" "F" } 0 +t "" 4 "" { 2, 3 } +t "" 5 "" { 1, 0 } +t "" 6 "" { 3, 1 } diff --git a/tests/test_games/entry_accommodation_with_nonterm_outcomes.efg b/tests/test_games/entry_accommodation_with_nonterm_outcomes.efg new file mode 100644 index 000000000..9453a4ff8 --- /dev/null +++ b/tests/test_games/entry_accommodation_with_nonterm_outcomes.efg @@ -0,0 +1,14 @@ +EFG 2 R "Entry-accomodation game" { "1" "2" } +"" + +p "" 1 1 "" { "S" "T" } 0 +p "" 2 1 "" { "E" "O" } 1 "" { 3, 2 } +p "" 1 2 "" { "A" "F" } 0 +t "" 0 +t "" 2 "" { -3, -1 } +t "" 3 "" { -2, 1 } +p "" 2 1 "" { "E" "O" } 0 +p "" 1 3 "" { "A" "F" } 0 +t "" 4 "" { 2, 3 } +t "" 5 "" { 1, 0 } +t "" 6 "" { 3, 1 } diff --git a/tests/test_games/large_payoff_game.efg b/tests/test_games/large_payoff_game.efg new file mode 100644 index 000000000..51d304418 --- /dev/null +++ b/tests/test_games/large_payoff_game.efg @@ -0,0 +1,18 @@ +EFG 2 R "Large payoff game" { "1" "2" } +"" + +c "" 1 "" { "L" 1/2 "R" 1/2 } 0 +p "" 1 1 "" { "A" "B" } 0 +p "" 2 1 "" { "X" "Y" } 0 +t "" 1 "large payoff" { 10000000000000000000, -10000000000000000000 } +t "" 2 "1" { 1, -1 } +p "" 2 2 "" { "X" "Y" } 0 +t "" 3 "-1" { -1, 1 } +t "" 4 "0" { 0, 0 } +p "" 1 2 "" { "A" "B" } 0 +p "" 2 1 "" { "X" "Y" } 0 +t "" 3 "-1" { -1, 1 } +t "" 2 "1" { 1, -1 } +p "" 2 2 "" { "X" "Y" } 0 +t "" 4 "0" { 0, 0 } +t "" 1 "large payoff" { 10000000000000000000, -10000000000000000000 } diff --git a/tests/test_games/mixed_behavior_game.efg b/tests/test_games/mixed_behavior_game.efg index d6de35662..ff93b5732 100644 --- a/tests/test_games/mixed_behavior_game.efg +++ b/tests/test_games/mixed_behavior_game.efg @@ -1,5 +1,10 @@ EFG 2 R "Test Extensive Form Game" { "Player 1" "Player 2" "Player 3" } -"" +" +Three-player extensive form game: binary tree with 3 infomation sets, one per player, +with 1, 2, and 4 nodes respectively. + +Since no information is revealed this is directly equivalent to a simultaneous move game. +" p "" 1 1 "Infoset 1:1" { "U1" "D1" } 0 p "" 2 1 "Infoset 2:1" { "U2" "D2" } 0 diff --git a/tests/test_games/perfect_info_with_chance.efg b/tests/test_games/perfect_info_with_chance.efg new file mode 100644 index 000000000..4b7c58ea0 --- /dev/null +++ b/tests/test_games/perfect_info_with_chance.efg @@ -0,0 +1,12 @@ +EFG 2 R "2 player perfect info with chance" { "1" "2" } +"" + +p "" 1 1 "" { "a" "b" } 0 +c "" 1 "" { "L" 1/2 "R" 1/2 } 0 +p "" 2 1 "" { "A" "B" } 0 +t "" 1 "aLA" { -2, 2 } +t "" 2 "aLB" { -2, 2 } +p "" 2 2 "" { "C" "D" } 0 +t "" 3 "aRC" { -2, 2 } +t "" 4 "aRD" { -2, 2 } +t "" 5 "b" { -1, 1 } diff --git a/tests/test_games/reduction_both_players_payoff_ties_GTE_survey.efg b/tests/test_games/reduction_both_players_payoff_ties_GTE_survey.efg new file mode 100644 index 000000000..1c4adf21c --- /dev/null +++ b/tests/test_games/reduction_both_players_payoff_ties_GTE_survey.efg @@ -0,0 +1,20 @@ +EFG 2 R "From GTE survey" { "1" "2" } +"A reduction in pure strategies is possible for both players" + +p "" 1 1 "" { "A" "B" "C" "D" } 0 +p "" 2 1 "" { "a" "b" } 0 +t "" 1 "" { 2, 8 } +p "" 2 4 "" { "g" "h" } 0 +t "" 2 "" { 0, 1 } +t "" 3 "" { 5, 2 } +p "" 2 2 "" { "c" "d" } 0 +t "" 4 "" { 7, 6 } +t "" 5 "" { 4, 2 } +p "" 2 3 "" { "e" "f" } 0 +p "" 1 2 "" { "E" "F" } 0 +t "" 6 "" { 3, 7 } +t "" 7 "" { 8, 3 } +p "" 1 2 "" { "E" "F" } 0 +t "" 8 "" { 7, 8 } +t "" 9 "" { 2, 2 } +t "" 10 "" { 6, 4 } diff --git a/tests/test_games/reduction_generic_payoffs.efg b/tests/test_games/reduction_generic_payoffs.efg new file mode 100644 index 000000000..7dcac053a --- /dev/null +++ b/tests/test_games/reduction_generic_payoffs.efg @@ -0,0 +1,24 @@ +EFG 2 R "2 player reduction generic payoffs" { "1" "2" } +"" + +p "" 2 1 "" { "a" "b" "c" "d" } 0 +p "" 1 1 "" { "L" "R" } 0 +t "" 1 "aL" { 1, -1 } +p "" 2 3 "" { "p" "q" } 0 +p "" 1 3 "" { "U" "V" } 0 +t "" 2 "aRpU" { 2, -2 } +t "" 3 "aRpV" { 3, -3 } +p "" 1 3 "" { "U" "V" } 0 +t "" 4 "aRqU" { 4, -4 } +t "" 5 "aRqV" { 5, -5 } +p "" 1 2 "" { "C" "D" } 0 +t "" 6 "bC" { 6, -6 } +t "" 7 "bD" { 7, -7 } +p "" 1 2 "" { "C" "D" } 0 +p "" 2 2 "" { "s" "t" } 0 +t "" 8 "cCs" { 8, -8 } +t "" 9 "cCt" { 9, -9 } +p "" 2 2 "" { "s" "t" } 0 +t "" 10 "cDs" { 10, -10 } +t "" 11 "cDt" { 11, -11 } +t "" 12 "d" { 12, -12 } diff --git a/tests/test_games/reduction_one_player_generic_payoffs.efg b/tests/test_games/reduction_one_player_generic_payoffs.efg new file mode 100644 index 000000000..7844ed6c0 --- /dev/null +++ b/tests/test_games/reduction_one_player_generic_payoffs.efg @@ -0,0 +1,10 @@ +EFG 2 R "One player reduction generic payoffs" { "1" } +"" + +p "" 1 1 "" { "a" "b" "c" "d" } 0 +p "" 1 2 "" { "e" "f" } 0 +t "" 1 "" { 1 } +t "" 2 "" { 2 } +t "" 3 "" { 3 } +t "" 4 "" { 4 } +t "" 5 "" { 5 } diff --git a/tests/test_games/two_player_perfect_info_win_lose.efg b/tests/test_games/two_player_perfect_info_win_lose.efg new file mode 100644 index 000000000..aeb5fa62e --- /dev/null +++ b/tests/test_games/two_player_perfect_info_win_lose.efg @@ -0,0 +1,12 @@ +EFG 2 R "2 player perfect info win lose" { "1" "2" } +"" + +p "" 2 1 "" { "a" "b" } 0 +p "" 1 1 "" { "L" "R" } 0 +p "" 2 2 "" { "l" "r" } 0 +t "" 1 "aLl" { 1, -1 } +t "" 2 "aLr" { -1, 1 } +t "" 3 "aR" { 1, -1 } +p "" 1 2 "" { "L" "R" } 0 +t "" 4 "bL" { 1, -1 } +t "" 5 "bR" { -1, 1 } diff --git a/tests/test_games/two_player_perfect_info_win_lose_with_nonterm_outcomes.efg b/tests/test_games/two_player_perfect_info_win_lose_with_nonterm_outcomes.efg new file mode 100644 index 000000000..d2ebceb41 --- /dev/null +++ b/tests/test_games/two_player_perfect_info_win_lose_with_nonterm_outcomes.efg @@ -0,0 +1,12 @@ +EFG 2 R "2 player perfect info win lose" { "1" "2" } +"" + +p "" 2 1 "" { "a" "b" } 0 +p "" 1 1 "" { "L" "R" } 1 "a" { -100, 50 } +p "" 2 2 "" { "l" "r" } 0 +t "" 2 "aLl" { 101, -51 } +t "" 3 "aLr" { 99, -49 } +t "" 4 "aR" { 101, -51 } +p "" 1 2 "" { "L" "R" } 0 +t "" 5 "bL" { 1, -1 } +t "" 6 "bR" { -1, 1 } diff --git a/tests/test_games/zerosum_efg_from_sequence_form_STOC94_paper.efg b/tests/test_games/zerosum_efg_from_sequence_form_STOC94_paper.efg new file mode 100644 index 000000000..750ff2de7 --- /dev/null +++ b/tests/test_games/zerosum_efg_from_sequence_form_STOC94_paper.efg @@ -0,0 +1,29 @@ +EFG 2 R "Two-player zero-sum EFG from sequence form STOC'94 paper" { "1" "2" } +" +Example from + +Fast Algorithms for Finding Randomized Strategies in Game Trees (STOC 1994) +Koller, Megiddo, von Stengel +" + +c "" 1 "" { "1" 0.2 "2" 0.2 "3" 0.2 "4" 0.4 } 0 +p "" 1 1 "0" { "l" "r" } 0 +t "" 1 "l" { 5, -5 } +p "" 2 1 "01" { "p" "q" } 0 +p "" 1 3 "010" { "L" "R" } 0 +t "" 2 "rpL" { 10, -10 } +t "" 3 "rpR" { 15, -15 } +p "" 1 3 "010" { "L" "R" } 0 +t "" 4 "rqL" { 20, -20 } +t "" 5 "rqR" { -5, 5 } +p "" 1 2 "1" { "c" "d" } 0 +t "" 6 "c" { 10, -10 } +t "" 7 "d" { 20, -20 } +p "" 1 2 "1" { "c" "d" } 0 +p "" 2 2 "20" { "s" "t" } 0 +t "" 8 "cs" { 20, -20 } +t "" 9 "ct" { 50, -50 } +p "" 2 2 "20" { "s" "t" } 0 +t "" 10 "ds" { 30, -30 } +t "" 11 "dt" { 15, -15 } +t "" 12 "nothing" { 5, -5 } diff --git a/tests/test_io.py b/tests/test_io.py index 1a6c328de..3016c224b 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -6,7 +6,7 @@ import pygambit as gbt -from .games import create_2x2_zero_nfg, create_selten_horse_game_efg +from . import games @pytest.mark.parametrize("game_path", glob(os.path.join("tests", "test_games", "*.efg"))) @@ -16,7 +16,9 @@ def test_read_efg(game_path): def test_read_efg_invalid(): - game_path = os.path.join("tests", "test_games", "2x2x2_nfg_with_two_pure_one_mixed_eq.nfg") + game_path = os.path.join( + "tests", "test_games", "2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg" + ) with pytest.raises(ValueError): gbt.read_efg(game_path) @@ -40,13 +42,17 @@ def test_read_agg(game_path): def test_read_agg_invalid(): - game_path = os.path.join("tests", "test_games", "2x2x2_nfg_with_two_pure_one_mixed_eq.nfg") + game_path = os.path.join( + "tests", "test_games", "2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg" + ) with pytest.raises(ValueError): gbt.read_agg(game_path) def test_read_gbt_invalid(): - game_path = os.path.join("tests", "test_games", "2x2x2_nfg_with_two_pure_one_mixed_eq.nfg") + game_path = os.path.join( + "tests", "test_games", "2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg" + ) with pytest.raises(ValueError): gbt.read_gbt(game_path) @@ -106,7 +112,7 @@ def test_write_latex(): def test_read_write_efg(): - efg_game = create_selten_horse_game_efg() + efg_game = games.read_from_file("e01.efg") serialized_efg_game = efg_game.to_efg() deserialized_efg_game = gbt.read_efg(io.BytesIO(serialized_efg_game.encode())) double_serialized_efg_game = deserialized_efg_game.to_efg() @@ -114,18 +120,20 @@ def test_read_write_efg(): def test_read_write_nfg(): - nfg_game = create_2x2_zero_nfg() + nfg_game = games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg") serialized_nfg_game = nfg_game.to_nfg() - deserialized_nfg_game = gbt.read_nfg(io.BytesIO(serialized_nfg_game.encode()), - normalize_labels=False) + deserialized_nfg_game = gbt.read_nfg( + io.BytesIO(serialized_nfg_game.encode()), normalize_labels=False + ) double_serialized_nfg_game = deserialized_nfg_game.to_nfg() assert serialized_nfg_game == double_serialized_nfg_game def test_read_write_nfg_normalize(): - nfg_game = create_2x2_zero_nfg() + nfg_game = games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg") serialized_nfg_game = nfg_game.to_nfg() - deserialized_nfg_game = gbt.read_nfg(io.BytesIO(serialized_nfg_game.encode()), - normalize_labels=True) + deserialized_nfg_game = gbt.read_nfg( + io.BytesIO(serialized_nfg_game.encode()), normalize_labels=True + ) double_serialized_nfg_game = deserialized_nfg_game.to_nfg() assert serialized_nfg_game != double_serialized_nfg_game diff --git a/tests/test_mixed.py b/tests/test_mixed.py index be6935634..287ee9eac 100644 --- a/tests/test_mixed.py +++ b/tests/test_mixed.py @@ -28,16 +28,28 @@ def _set_action_probs(profile: gbt.MixedStrategyProfile, probs: list, rational_f [ ############################################################################### # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), [[0, 0, 0, 0], ["1/3", "1/3", "1/3", 0]], True), - (games.create_coord_4x4_nfg(), [[1, 0, 0, 0], [0, 0, 0, 0]], True), - (games.create_coord_4x4_nfg(), [[0, 0, 0, 0], [1.0, 1.0, 1.0, 1.0]], False), - (games.create_coord_4x4_nfg(), [[1.0, 1.0, 1.0, 1.0], [0, 0, 0, 0]], False), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[0, 0, 0, 0], ["1/3", "1/3", "1/3", 0]], + True, + ), + (games.read_from_file("coordination_4x4_payoff.nfg"), [[1, 0, 0, 0], [0, 0, 0, 0]], True), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[0, 0, 0, 0], [1.0, 1.0, 1.0, 1.0]], + False, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1.0, 1.0, 1.0, 1.0], [0, 0, 0, 0]], + False, + ), ############################################################################### # centipede with chance efg - (games.create_centipede_game_with_chance_efg(), [[0, 0, 0, 0], [1, 0, 0, 0]], True), - (games.create_centipede_game_with_chance_efg(), [[1, 0, 0, 0], [0, 0, 0, 0]], True), - (games.create_centipede_game_with_chance_efg(), [[0, 0, 0, 0], [1, 0, 0, 0]], False), - (games.create_centipede_game_with_chance_efg(), [[1, 0, 0, 0], [0, 0, 0, 0]], False), + (games.read_from_file("cent3.efg"), [[0, 0, 0, 0], [1, 0, 0, 0]], True), + (games.read_from_file("cent3.efg"), [[1, 0, 0, 0], [0, 0, 0, 0]], True), + (games.read_from_file("cent3.efg"), [[0, 0, 0, 0], [1, 0, 0, 0]], False), + (games.read_from_file("cent3.efg"), [[1, 0, 0, 0], [0, 0, 0, 0]], False), ], ) def test_normalize_zero_value_error(game, profile_data, rational_flag): @@ -51,18 +63,30 @@ def test_normalize_zero_value_error(game, profile_data, rational_flag): [ ############################################################################### # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), [[1, 1, 0, -1], ["1/3", "1/3", "1/3", 0]], True), - (games.create_coord_4x4_nfg(), [[0, 0, 0, -1.0], [1.0, 1.0, 1.0, 1.0]], False), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1, 1, 0, -1], ["1/3", "1/3", "1/3", 0]], + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[0, 0, 0, -1.0], [1.0, 1.0, 1.0, 1.0]], + False, + ), ############################################################################### # zero matrix nfg - (games.create_2x2_zero_nfg(), [[1, 0], [0, -1]], True), - (games.create_2x2_zero_nfg(), [[1.0, 1.0], [0, -1.0]], False), + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), [[1, 0], [0, -1]], True), + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [[1.0, 1.0], [0, -1.0]], + False, + ), ############################################################################### # centipede with chance efg - (games.create_centipede_game_with_chance_efg(), [[-1, 0, 0, 0], [1, 0, 0, 0]], True), - (games.create_centipede_game_with_chance_efg(), [[1, 0, 0, 0], [-1, 0, 0, 0]], True), - (games.create_centipede_game_with_chance_efg(), [[-1, 0, 0, 0], [1, 0, 0, 0]], False), - (games.create_centipede_game_with_chance_efg(), [[1, 0, 0, 0], [-1, 0, 0, 0]], False), + (games.read_from_file("cent3.efg"), [[-1, 0, 0, 0], [1, 0, 0, 0]], True), + (games.read_from_file("cent3.efg"), [[1, 0, 0, 0], [-1, 0, 0, 0]], True), + (games.read_from_file("cent3.efg"), [[-1, 0, 0, 0], [1, 0, 0, 0]], False), + (games.read_from_file("cent3.efg"), [[1, 0, 0, 0], [-1, 0, 0, 0]], False), ], ) def test_normalize_neg_entry_value_error(game, profile_data, rational_flag): @@ -76,49 +100,64 @@ def test_normalize_neg_entry_value_error(game, profile_data, rational_flag): [ ############################################################################### # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), [[1, 2, 3, 14], [1, 1, 1, 1]], - [["1/20", "2/20", "3/20", "14/20"], ["1/4", "1/4", "1/4", "1/4"]], True), - (games.create_coord_4x4_nfg(), [[1.0, 2.0, 3.0, 14.0], [1, 1, 1, 1]], - [[1 / 20, 2 / 20, 3 / 20, 14 / 20], [0.25, 0.25, 0.25, 0.25]], False), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1, 2, 3, 14], [1, 1, 1, 1]], + [["1/20", "2/20", "3/20", "14/20"], ["1/4", "1/4", "1/4", "1/4"]], + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1.0, 2.0, 3.0, 14.0], [1, 1, 1, 1]], + [[1 / 20, 2 / 20, 3 / 20, 14 / 20], [0.25, 0.25, 0.25, 0.25]], + False, + ), ############################################################################### # centipede with chance efg - (games.create_centipede_game_with_chance_efg(), [[1, 2, 3, 14], [1, 1, 1, 1]], - [["1/20", "2/20", "3/20", "14/20"], ["1/4", "1/4", "1/4", "1/4"]], True), - (games.create_centipede_game_with_chance_efg(), [[1.0, 2.0, 3.0, 14.0], [1, 1, 1, 1]], - [[1 / 20, 2 / 20, 3 / 20, 14 / 20], [0.25, 0.25, 0.25, 0.25]], False), + ( + games.read_from_file("cent3.efg"), + [[1, 2, 3, 14], [1, 1, 1, 1]], + [["1/20", "2/20", "3/20", "14/20"], ["1/4", "1/4", "1/4", "1/4"]], + True, + ), + ( + games.read_from_file("cent3.efg"), + [[1.0, 2.0, 3.0, 14.0], [1, 1, 1, 1]], + [[1 / 20, 2 / 20, 3 / 20, 14 / 20], [0.25, 0.25, 0.25, 0.25]], + False, + ), ], ) def test_normalize(game, profile_data, expected_data, rational_flag): - assert ( - game.mixed_strategy_profile(data=profile_data, rational=rational_flag).normalize() == - game.mixed_strategy_profile(data=expected_data, rational=rational_flag) - ) + assert game.mixed_strategy_profile( + data=profile_data, rational=rational_flag + ).normalize() == game.mixed_strategy_profile(data=expected_data, rational=rational_flag) @pytest.mark.parametrize( "game,strategy_label,rational_flag,prob", [ - ############################################################################## - # zero matrix nfg - (games.create_2x2_zero_nfg(), "cooperate", False, 0.72), - (games.create_2x2_zero_nfg(), "cooperate", True, "7/9"), - ############################################################################### - # coordination 4x4 nfg outcome version with strategy labels - (games.create_coord_4x4_nfg(outcome_version=True), "1-1", 0.25, False), - (games.create_coord_4x4_nfg(outcome_version=True), "1-1", "1/4", True), - ############################################################################### - # stripped-down poker efg - (games.create_stripped_down_poker_efg(), "11", 0.25, False), - (games.create_stripped_down_poker_efg(), "12", 0.15, False), - (games.create_stripped_down_poker_efg(), "21", 0.99, False), - (games.create_stripped_down_poker_efg(), "11", "1/4", True), - (games.create_stripped_down_poker_efg(), "12", "3/4", True), - (games.create_stripped_down_poker_efg(), "21", "7/9", True), + ############################################################################## + # zero matrix nfg + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), "cooperate", False, 0.72), + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), "cooperate", True, "7/9"), + ############################################################################### + # coordination 4x4 nfg outcome version with strategy labels + (games.read_from_file("coordination_4x4_outcome.nfg"), "1-1", 0.25, False), + (games.read_from_file("coordination_4x4_outcome.nfg"), "1-1", "1/4", True), + ############################################################################### + # stripped-down poker efg + (games.create_stripped_down_poker_efg(), "11", 0.25, False), + (games.create_stripped_down_poker_efg(), "12", 0.15, False), + (games.create_stripped_down_poker_efg(), "21", 0.99, False), + (games.create_stripped_down_poker_efg(), "11", "1/4", True), + (games.create_stripped_down_poker_efg(), "12", "3/4", True), + (games.create_stripped_down_poker_efg(), "21", "7/9", True), ], ) -def test_set_and_get_probability_by_strategy_label(game: gbt.Game, strategy_label: str, - rational_flag: bool, - prob: float | str): +def test_set_and_get_probability_by_strategy_label( + game: gbt.Game, strategy_label: str, rational_flag: bool, prob: float | str +): profile = game.mixed_strategy_profile(rational=rational_flag) prob = gbt.Rational(prob) if rational_flag else prob profile[strategy_label] = prob @@ -128,24 +167,25 @@ def test_set_and_get_probability_by_strategy_label(game: gbt.Game, strategy_labe @pytest.mark.parametrize( "game,player_label,rational_flag,profile_data", [ - ############################################################################## - # zero matrix nfg - (games.create_2x2_zero_nfg(), "Joe", False, [0.72, 0.28]), - (games.create_2x2_zero_nfg(), "Joe", True, ["7/9", "2/9"]), - ############################################################################## - # coordination 4x4 nfg outcome version with strategy labels - (games.create_coord_4x4_nfg(), P1, False, [0.25, 0, 0, 0.75]), - (games.create_coord_4x4_nfg(), P1, True, ["1/4", 0, 0, "3/4"]), - ############################################################################## - # stripped-down poker efg - (games.create_stripped_down_poker_efg(), "Alice", False, [0.25, 0.75, 0, 0]), - (games.create_stripped_down_poker_efg(), "Bob", False, [1, 0]), - (games.create_stripped_down_poker_efg(), "Alice", True, ["1/4", "3/4", 0, 0]), - (games.create_stripped_down_poker_efg(), "Bob", True, [1, 0]), + ############################################################################## + # zero matrix nfg + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), "Joe", False, [0.72, 0.28]), + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), "Joe", True, ["7/9", "2/9"]), + ############################################################################## + # coordination 4x4 nfg outcome version with strategy labels + (games.read_from_file("coordination_4x4_payoff.nfg"), P1, False, [0.25, 0, 0, 0.75]), + (games.read_from_file("coordination_4x4_payoff.nfg"), P1, True, ["1/4", 0, 0, "3/4"]), + ############################################################################## + # stripped-down poker efg + (games.create_stripped_down_poker_efg(), "Alice", False, [0.25, 0.75, 0, 0]), + (games.create_stripped_down_poker_efg(), "Bob", False, [1, 0]), + (games.create_stripped_down_poker_efg(), "Alice", True, ["1/4", "3/4", 0, 0]), + (games.create_stripped_down_poker_efg(), "Bob", True, [1, 0]), ], ) -def test_set_and_get_probabilities_by_player_label(game: gbt.Game, player_label: str, - rational_flag: bool, profile_data: list): +def test_set_and_get_probabilities_by_player_label( + game: gbt.Game, player_label: str, rational_flag: bool, profile_data: list +): profile_data = [gbt.Rational(p) for p in profile_data] if rational_flag else profile_data profile = game.mixed_strategy_profile(rational=rational_flag) profile[player_label] = profile_data @@ -155,32 +195,31 @@ def test_set_and_get_probabilities_by_player_label(game: gbt.Game, player_label: @pytest.mark.parametrize( "game,player_label,strategy_label,prob,rational_flag", [ - ############################################################################## - # stripped-down poker efg - # Player 1 - (games.create_stripped_down_poker_efg(), "Alice", "11", 0.25, False), - (games.create_stripped_down_poker_efg(), "Alice", "12", 0.25, False), - (games.create_stripped_down_poker_efg(), "Alice", "21", 0.25, False), - (games.create_stripped_down_poker_efg(), "Alice", "22", 0.25, False), - (games.create_stripped_down_poker_efg(), "Alice", "11", "1/4", True), - (games.create_stripped_down_poker_efg(), "Alice", "12", "1/4", True), - (games.create_stripped_down_poker_efg(), "Alice", "21", "1/4", True), - (games.create_stripped_down_poker_efg(), "Alice", "22", "1/4", True), - # Player 2 - (games.create_stripped_down_poker_efg(), "Bob", "1", 0.5, False), - (games.create_stripped_down_poker_efg(), "Bob", "2", 0.5, False), - (games.create_stripped_down_poker_efg(), "Bob", "1", "1/2", True), - (games.create_stripped_down_poker_efg(), "Bob", "2", "1/2", True), - ############################################################################## - # coordination 4x4 nfg outcome version with strategy labels - (games.create_coord_4x4_nfg(outcome_version=True), P1, "1-1", "1/4", True), - (games.create_coord_4x4_nfg(outcome_version=True), P2, "2-1", "1/4", True), - ] + ############################################################################## + # stripped-down poker efg + # Player 1 + (games.create_stripped_down_poker_efg(), "Alice", "11", 0.25, False), + (games.create_stripped_down_poker_efg(), "Alice", "12", 0.25, False), + (games.create_stripped_down_poker_efg(), "Alice", "21", 0.25, False), + (games.create_stripped_down_poker_efg(), "Alice", "22", 0.25, False), + (games.create_stripped_down_poker_efg(), "Alice", "11", "1/4", True), + (games.create_stripped_down_poker_efg(), "Alice", "12", "1/4", True), + (games.create_stripped_down_poker_efg(), "Alice", "21", "1/4", True), + (games.create_stripped_down_poker_efg(), "Alice", "22", "1/4", True), + # Player 2 + (games.create_stripped_down_poker_efg(), "Bob", "1", 0.5, False), + (games.create_stripped_down_poker_efg(), "Bob", "2", 0.5, False), + (games.create_stripped_down_poker_efg(), "Bob", "1", "1/2", True), + (games.create_stripped_down_poker_efg(), "Bob", "2", "1/2", True), + ############################################################################## + # coordination 4x4 nfg outcome version with strategy labels + (games.read_from_file("coordination_4x4_outcome.nfg"), P1, "1-1", "1/4", True), + (games.read_from_file("coordination_4x4_outcome.nfg"), P2, "2-1", "1/4", True), + ], ) -def test_profile_indexing_by_player_and_strategy_label_reference(game: gbt.Game, player_label: str, - strategy_label: str, - prob: str | float, - rational_flag: bool): +def test_profile_indexing_by_player_and_strategy_label_reference( + game: gbt.Game, player_label: str, strategy_label: str, prob: str | float, rational_flag: bool +): profile = game.mixed_strategy_profile(rational=rational_flag) prob = gbt.Rational(prob) if rational_flag else prob assert profile[player_label][strategy_label] == prob @@ -189,24 +228,23 @@ def test_profile_indexing_by_player_and_strategy_label_reference(game: gbt.Game, @pytest.mark.parametrize( "game,player_label,strategy_label,rational_flag", [ - ############################################################################## - # stripped-down poker efg - (games.create_stripped_down_poker_efg(), "Bob", "11", True), - (games.create_stripped_down_poker_efg(), "Bob", "11", False), - (games.create_stripped_down_poker_efg(), "Alice", "1", True), - (games.create_stripped_down_poker_efg(), "Alice", "1", False), - (games.create_stripped_down_poker_efg(), "Alice", "2", True), - (games.create_stripped_down_poker_efg(), "Alice", "2", False), - ############################################################################## - # coordination 4x4 nfg outcome version with strategy labels - (games.create_coord_4x4_nfg(outcome_version=True), P1, "2-1", True), - (games.create_coord_4x4_nfg(outcome_version=True), P2, "1-1", True), - ] + ############################################################################## + # stripped-down poker efg + (games.create_stripped_down_poker_efg(), "Bob", "11", True), + (games.create_stripped_down_poker_efg(), "Bob", "11", False), + (games.create_stripped_down_poker_efg(), "Alice", "1", True), + (games.create_stripped_down_poker_efg(), "Alice", "1", False), + (games.create_stripped_down_poker_efg(), "Alice", "2", True), + (games.create_stripped_down_poker_efg(), "Alice", "2", False), + ############################################################################## + # coordination 4x4 nfg outcome version with strategy labels + (games.read_from_file("coordination_4x4_outcome.nfg"), P1, "2-1", True), + (games.read_from_file("coordination_4x4_outcome.nfg"), P2, "1-1", True), + ], ) -def test_profile_indexing_by_player_and_invalid_strategy_label(game: gbt.Game, - player_label: str, - strategy_label: str, - rational_flag: bool): +def test_profile_indexing_by_player_and_invalid_strategy_label( + game: gbt.Game, player_label: str, strategy_label: str, rational_flag: bool +): """Test that we get a KeyError and that "player" appears in the error message""" with pytest.raises(KeyError, match="for player"): game.mixed_strategy_profile(rational=rational_flag)[player_label][strategy_label] @@ -215,22 +253,55 @@ def test_profile_indexing_by_player_and_invalid_strategy_label(game: gbt.Game, @pytest.mark.parametrize( "game,strategy_label,rational_flag,error,message", [ - ############################################################################## - # stripped-down poker efg - (games.create_stripped_down_poker_efg(), "13", True, KeyError, "player or strategy"), - ############################################################################## - # coordination 4x4 nfg payoff version (default strategy labels created with duplicates) - (games.create_coord_4x4_nfg(), "1", True, ValueError, "multiple strategies"), - (games.create_coord_4x4_nfg(), "2", True, ValueError, "multiple strategies"), - (games.create_coord_4x4_nfg(), "3", True, ValueError, "multiple strategies"), - (games.create_coord_4x4_nfg(), "4", True, ValueError, "multiple strategies"), - (games.create_coord_4x4_nfg(), "5", True, KeyError, "player or strategy"), - ] + ############################################################################## + # stripped-down poker efg + (games.create_stripped_down_poker_efg(), "13", True, KeyError, "player or strategy"), + ############################################################################## + # coordination 4x4 nfg payoff version (default strategy labels created with duplicates) + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + "1", + True, + ValueError, + "multiple strategies", + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + "2", + True, + ValueError, + "multiple strategies", + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + "3", + True, + ValueError, + "multiple strategies", + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + "4", + True, + ValueError, + "multiple strategies", + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + "5", + True, + KeyError, + "player or strategy", + ), + ], ) -def test_profile_indexing_by_invalid_strategy_label(game: gbt.Game, strategy_label: str, - rational_flag: bool, - error: ValueError | KeyError, - message: str | None): +def test_profile_indexing_by_invalid_strategy_label( + game: gbt.Game, + strategy_label: str, + rational_flag: bool, + error: ValueError | KeyError, + message: str | None, +): """Check that we get a ValueError for an ambigious strategy label and a KeyError for one that is neither a player or strategy label in the game """ @@ -239,7 +310,7 @@ def test_profile_indexing_by_invalid_strategy_label(game: gbt.Game, strategy_lab def test_profile_indexing_by_player_and_duplicate_strategy_label(): - game = games.create_2x2_zero_nfg() + game = games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg") profile = game.mixed_strategy_profile() with pytest.raises(ValueError): profile["Dan"]["defect"] @@ -248,35 +319,35 @@ def test_profile_indexing_by_player_and_duplicate_strategy_label(): @pytest.mark.parametrize( "game,strategy_label,prob,rational_flag", [ - ########################################################################### - # stripped-down poker efg - # Player 1 - (games.create_stripped_down_poker_efg(), "11", 0.25, False), - (games.create_stripped_down_poker_efg(), "12", 0.25, False), - (games.create_stripped_down_poker_efg(), "21", 0.25, False), - (games.create_stripped_down_poker_efg(), "22", 0.25, False), - (games.create_stripped_down_poker_efg(), "11", "1/4", True), - (games.create_stripped_down_poker_efg(), "12", "1/4", True), - (games.create_stripped_down_poker_efg(), "21", "1/4", True), - (games.create_stripped_down_poker_efg(), "22", "1/4", True), - # Player 2 - (games.create_stripped_down_poker_efg(), "1", 0.5, False), - (games.create_stripped_down_poker_efg(), "2", 0.5, False), - (games.create_stripped_down_poker_efg(), "1", "1/2", True), - (games.create_stripped_down_poker_efg(), "2", "1/2", True), - ############################################################################ - # coordination 4x4 nfg outcome version with strategy labels - # Player 1 - (games.create_coord_4x4_nfg(outcome_version=True), "1-1", "1/4", True), - (games.create_coord_4x4_nfg(outcome_version=True), "1-1", 0.25, False), - # Player 2 - (games.create_coord_4x4_nfg(outcome_version=True), "2-1", "1/4", True), - (games.create_coord_4x4_nfg(outcome_version=True), "2-1", 0.25, False), - ] + ########################################################################### + # stripped-down poker efg + # Player 1 + (games.create_stripped_down_poker_efg(), "11", 0.25, False), + (games.create_stripped_down_poker_efg(), "12", 0.25, False), + (games.create_stripped_down_poker_efg(), "21", 0.25, False), + (games.create_stripped_down_poker_efg(), "22", 0.25, False), + (games.create_stripped_down_poker_efg(), "11", "1/4", True), + (games.create_stripped_down_poker_efg(), "12", "1/4", True), + (games.create_stripped_down_poker_efg(), "21", "1/4", True), + (games.create_stripped_down_poker_efg(), "22", "1/4", True), + # Player 2 + (games.create_stripped_down_poker_efg(), "1", 0.5, False), + (games.create_stripped_down_poker_efg(), "2", 0.5, False), + (games.create_stripped_down_poker_efg(), "1", "1/2", True), + (games.create_stripped_down_poker_efg(), "2", "1/2", True), + ############################################################################ + # coordination 4x4 nfg outcome version with strategy labels + # Player 1 + (games.read_from_file("coordination_4x4_outcome.nfg"), "1-1", "1/4", True), + (games.read_from_file("coordination_4x4_outcome.nfg"), "1-1", 0.25, False), + # Player 2 + (games.read_from_file("coordination_4x4_outcome.nfg"), "2-1", "1/4", True), + (games.read_from_file("coordination_4x4_outcome.nfg"), "2-1", 0.25, False), + ], ) -def test_profile_indexing_by_strategy_label_reference(game: gbt.Game, strategy_label: str, - prob: str | float, - rational_flag: bool): +def test_profile_indexing_by_strategy_label_reference( + game: gbt.Game, strategy_label: str, prob: str | float, rational_flag: bool +): profile = game.mixed_strategy_profile(rational=rational_flag) prob = gbt.Rational(prob) if rational_flag else prob assert profile[strategy_label] == prob @@ -285,30 +356,41 @@ def test_profile_indexing_by_strategy_label_reference(game: gbt.Game, strategy_l @pytest.mark.parametrize( "game,player_label,strategy_data,rational_flag", [ - ############################################################################ - # mixed behav efg - (games.create_mixed_behav_game_efg(), P1, [0.5, 0.5], False), - (games.create_mixed_behav_game_efg(), P2, [0.5, 0.5], False), - (games.create_mixed_behav_game_efg(), P3, [0.5, 0.5], False), - (games.create_mixed_behav_game_efg(), P1, ["1/2", "1/2"], True), - (games.create_mixed_behav_game_efg(), P2, ["1/2", "1/2"], True), - (games.create_mixed_behav_game_efg(), P3, ["1/2", "1/2"], True), - ############################################################################ - # stripped-down poker efg - (games.create_stripped_down_poker_efg(), "Alice", [0.25, 0.25, 0.25, 0.25], False), - (games.create_stripped_down_poker_efg(), "Bob", [0.5, 0.5], False), - (games.create_stripped_down_poker_efg(), "Alice", ["1/4", "1/4", "1/4", "1/4"], True), - (games.create_stripped_down_poker_efg(), "Bob", ["1/2", "1/2"], True), - ############################################################################ - # coordination 4x4 nfg - (games.create_coord_4x4_nfg(), P1, [0.25, 0.25, 0.25, 0.25], False), - (games.create_coord_4x4_nfg(), P2, [0.25, 0.25, 0.25, 0.25], False), - (games.create_coord_4x4_nfg(), P1, ["1/4", "1/4", "1/4", "1/4"], True), - (games.create_coord_4x4_nfg(), P2, ["1/4", "1/4", "1/4", "1/4"], True), - ] + ############################################################################ + # mixed behav efg + (games.read_from_file("mixed_behavior_game.efg"), P1, [0.5, 0.5], False), + (games.read_from_file("mixed_behavior_game.efg"), P2, [0.5, 0.5], False), + (games.read_from_file("mixed_behavior_game.efg"), P3, [0.5, 0.5], False), + (games.read_from_file("mixed_behavior_game.efg"), P1, ["1/2", "1/2"], True), + (games.read_from_file("mixed_behavior_game.efg"), P2, ["1/2", "1/2"], True), + (games.read_from_file("mixed_behavior_game.efg"), P3, ["1/2", "1/2"], True), + ############################################################################ + # stripped-down poker efg + (games.create_stripped_down_poker_efg(), "Alice", [0.25, 0.25, 0.25, 0.25], False), + (games.create_stripped_down_poker_efg(), "Bob", [0.5, 0.5], False), + (games.create_stripped_down_poker_efg(), "Alice", ["1/4", "1/4", "1/4", "1/4"], True), + (games.create_stripped_down_poker_efg(), "Bob", ["1/2", "1/2"], True), + ############################################################################ + # coordination 4x4 nfg + (games.read_from_file("coordination_4x4_payoff.nfg"), P1, [0.25, 0.25, 0.25, 0.25], False), + (games.read_from_file("coordination_4x4_payoff.nfg"), P2, [0.25, 0.25, 0.25, 0.25], False), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + P1, + ["1/4", "1/4", "1/4", "1/4"], + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + P2, + ["1/4", "1/4", "1/4", "1/4"], + True, + ), + ], ) -def test_profile_indexing_by_player_label_reference(game: gbt.Game, player_label: str, - strategy_data: list, rational_flag: bool): +def test_profile_indexing_by_player_label_reference( + game: gbt.Game, player_label: str, strategy_data: list, rational_flag: bool +): profile = game.mixed_strategy_profile(rational=rational_flag) if rational_flag: strategy_data = [gbt.Rational(prob) for prob in strategy_data] @@ -318,55 +400,110 @@ def test_profile_indexing_by_player_label_reference(game: gbt.Game, player_label @pytest.mark.parametrize( "game,rational_flag,profile_data,label,payoff", [ - ######################################################################### - # zero matrix nfg - (games.create_2x2_zero_nfg(), False, None, "Joe", 0), - (games.create_2x2_zero_nfg(), True, None, "Joe", 0), - ######################################################################### - # coordination 4x4 nfg - (games.create_coord_4x4_nfg(), False, None, P1, 0.25), - (games.create_coord_4x4_nfg(), True, None, P1, "1/4"), - (games.create_coord_4x4_nfg(), False, None, P2, 0.25), - (games.create_coord_4x4_nfg(), True, None, P2, "1/4"), - (games.create_coord_4x4_nfg(), False, [[1, 0, 0, 0], [1, 0, 0, 0]], P1, 1), - (games.create_coord_4x4_nfg(), True, [[1, 0, 0, 0], [1, 0, 0, 0]], P1, 1), - (games.create_coord_4x4_nfg(), False, [[1, 0, 0, 0], [1, 0, 0, 0]], P2, 1), - (games.create_coord_4x4_nfg(), True, [[1, 0, 0, 0], [1, 0, 0, 0]], P2, 1), - (games.create_coord_4x4_nfg(), False, [[1, 0, 0, 0], [0, 1, 0, 0]], P1, 0), - (games.create_coord_4x4_nfg(), True, [[1, 0, 0, 0], [0, 1, 0, 0]], P1, 0), - (games.create_coord_4x4_nfg(), False, [[1, 0, 0, 0], [0, 1, 0, 0]], P2, 0), - (games.create_coord_4x4_nfg(), True, [[1, 0, 0, 0], [0, 1, 0, 0]], P2, 0), - ######################################################################### - # stripped-down poker efg - (games.create_stripped_down_poker_efg(), False, None, "Alice", -0.25), - (games.create_stripped_down_poker_efg(), False, None, "Bob", 0.25), - (games.create_stripped_down_poker_efg(), True, None, "Alice", "-1/4"), - (games.create_stripped_down_poker_efg(), True, None, "Bob", "1/4"), - # Bet/Call - (games.create_stripped_down_poker_efg(), False, [[1, 0, 0, 0], [1, 0]], "Alice", 0), - (games.create_stripped_down_poker_efg(), False, [[1, 0, 0, 0], [1, 0]], "Bob", 0), - (games.create_stripped_down_poker_efg(), True, [[1, 0, 0, 0], [1, 0]], "Alice", 0), - (games.create_stripped_down_poker_efg(), True, [[1, 0, 0, 0], [1, 0]], "Bob", 0), - # Fold/Fold for player 1 (player 2's strategy is payoff-irrelevant) - (games.create_stripped_down_poker_efg(), False, [[0, 0, 0, 1], [1, 0]], "Alice", -1), - (games.create_stripped_down_poker_efg(), False, [[0, 0, 0, 1], [1, 0]], "Bob", 1), - (games.create_stripped_down_poker_efg(), True, [[0, 0, 0, 1], [1, 0]], "Alice", -1), - (games.create_stripped_down_poker_efg(), True, [[0, 0, 0, 1], [1, 0]], "Bob", 1), - (games.create_stripped_down_poker_efg(), False, [[0, 0, 0, 1], [0.5, 0.5]], "Alice", -1), - (games.create_stripped_down_poker_efg(), False, [[0, 0, 0, 1], [0.5, 0.5]], "Bob", 1), - (games.create_stripped_down_poker_efg(), True, [[0, 0, 0, 1], ["1/2", "1/2"]], "Alice", -1), - (games.create_stripped_down_poker_efg(), True, [[0, 0, 0, 1], ["1/2", "1/2"]], "Bob", 1), - ######################################################################### - (games.create_mixed_behav_game_efg(), False, None, P1, 3.0), - (games.create_mixed_behav_game_efg(), False, None, P2, 3.0), - (games.create_mixed_behav_game_efg(), False, None, P3, 3.25), - (games.create_mixed_behav_game_efg(), True, None, P1, 3), - (games.create_mixed_behav_game_efg(), True, None, P2, 3), - (games.create_mixed_behav_game_efg(), True, None, P3, "13/4"), - ] + ######################################################################### + # zero matrix nfg + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), False, None, "Joe", 0), + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), True, None, "Joe", 0), + ######################################################################### + # coordination 4x4 nfg + (games.read_from_file("coordination_4x4_payoff.nfg"), False, None, P1, 0.25), + (games.read_from_file("coordination_4x4_payoff.nfg"), True, None, P1, "1/4"), + (games.read_from_file("coordination_4x4_payoff.nfg"), False, None, P2, 0.25), + (games.read_from_file("coordination_4x4_payoff.nfg"), True, None, P2, "1/4"), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + False, + [[1, 0, 0, 0], [1, 0, 0, 0]], + P1, + 1, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + True, + [[1, 0, 0, 0], [1, 0, 0, 0]], + P1, + 1, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + False, + [[1, 0, 0, 0], [1, 0, 0, 0]], + P2, + 1, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + True, + [[1, 0, 0, 0], [1, 0, 0, 0]], + P2, + 1, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + False, + [[1, 0, 0, 0], [0, 1, 0, 0]], + P1, + 0, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + True, + [[1, 0, 0, 0], [0, 1, 0, 0]], + P1, + 0, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + False, + [[1, 0, 0, 0], [0, 1, 0, 0]], + P2, + 0, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + True, + [[1, 0, 0, 0], [0, 1, 0, 0]], + P2, + 0, + ), + ######################################################################### + # stripped-down poker efg + (games.create_stripped_down_poker_efg(), False, None, "Alice", -0.25), + (games.create_stripped_down_poker_efg(), False, None, "Bob", 0.25), + (games.create_stripped_down_poker_efg(), True, None, "Alice", "-1/4"), + (games.create_stripped_down_poker_efg(), True, None, "Bob", "1/4"), + # Bet/Call + (games.create_stripped_down_poker_efg(), False, [[1, 0, 0, 0], [1, 0]], "Alice", 0), + (games.create_stripped_down_poker_efg(), False, [[1, 0, 0, 0], [1, 0]], "Bob", 0), + (games.create_stripped_down_poker_efg(), True, [[1, 0, 0, 0], [1, 0]], "Alice", 0), + (games.create_stripped_down_poker_efg(), True, [[1, 0, 0, 0], [1, 0]], "Bob", 0), + # Fold/Fold for player 1 (player 2's strategy is payoff-irrelevant) + (games.create_stripped_down_poker_efg(), False, [[0, 0, 0, 1], [1, 0]], "Alice", -1), + (games.create_stripped_down_poker_efg(), False, [[0, 0, 0, 1], [1, 0]], "Bob", 1), + (games.create_stripped_down_poker_efg(), True, [[0, 0, 0, 1], [1, 0]], "Alice", -1), + (games.create_stripped_down_poker_efg(), True, [[0, 0, 0, 1], [1, 0]], "Bob", 1), + (games.create_stripped_down_poker_efg(), False, [[0, 0, 0, 1], [0.5, 0.5]], "Alice", -1), + (games.create_stripped_down_poker_efg(), False, [[0, 0, 0, 1], [0.5, 0.5]], "Bob", 1), + ( + games.create_stripped_down_poker_efg(), + True, + [[0, 0, 0, 1], ["1/2", "1/2"]], + "Alice", + -1, + ), + (games.create_stripped_down_poker_efg(), True, [[0, 0, 0, 1], ["1/2", "1/2"]], "Bob", 1), + ######################################################################### + (games.read_from_file("mixed_behavior_game.efg"), False, None, P1, 3.0), + (games.read_from_file("mixed_behavior_game.efg"), False, None, P2, 3.0), + (games.read_from_file("mixed_behavior_game.efg"), False, None, P3, 3.25), + (games.read_from_file("mixed_behavior_game.efg"), True, None, P1, 3), + (games.read_from_file("mixed_behavior_game.efg"), True, None, P2, 3), + (games.read_from_file("mixed_behavior_game.efg"), True, None, P3, "13/4"), + ], ) -def test_payoff_by_label_reference(game: gbt.Game, rational_flag: bool, profile_data: list, - label: str, payoff: float | str): +def test_payoff_by_label_reference( + game: gbt.Game, rational_flag: bool, profile_data: list, label: str, payoff: float | str +): payoff = gbt.Rational(payoff) if rational_flag else payoff profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) assert profile.payoff(label) == payoff @@ -375,28 +512,29 @@ def test_payoff_by_label_reference(game: gbt.Game, rational_flag: bool, profile_ @pytest.mark.parametrize( "game,rational_flag,label,value", [ - ############################################################################## - # zero matrix nfg - (games.create_2x2_zero_nfg(), False, "cooperate", 0), - (games.create_2x2_zero_nfg(), True, "cooperate", 0), - ############################################################################## - # coordination 4x4 nfg - (games.create_coord_4x4_nfg(outcome_version=True), False, "1-1", 0.25), - (games.create_coord_4x4_nfg(outcome_version=True), True, "1-1", "1/4"), - ############################################################################## - # stripped-down poker efg - (games.create_stripped_down_poker_efg(), False, "11", 0.5), # Bet/Bet - (games.create_stripped_down_poker_efg(), False, "12", 0.25), # Bet King/Fold Queen - (games.create_stripped_down_poker_efg(), False, "21", -0.75), # Fold King/Bet Queen - (games.create_stripped_down_poker_efg(), False, "22", -1), # Fold/Fold - (games.create_stripped_down_poker_efg(), True, "11", "1/2"), - (games.create_stripped_down_poker_efg(), True, "12", "1/4"), - (games.create_stripped_down_poker_efg(), True, "21", "-3/4"), - (games.create_stripped_down_poker_efg(), True, "22", -1), - ] + ############################################################################## + # zero matrix nfg + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), False, "cooperate", 0), + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), True, "cooperate", 0), + ############################################################################## + # coordination 4x4 nfg + (games.read_from_file("coordination_4x4_outcome.nfg"), False, "1-1", 0.25), + (games.read_from_file("coordination_4x4_outcome.nfg"), True, "1-1", "1/4"), + ############################################################################## + # stripped-down poker efg + (games.create_stripped_down_poker_efg(), False, "11", 0.5), # Bet/Bet + (games.create_stripped_down_poker_efg(), False, "12", 0.25), # Bet King/Fold Queen + (games.create_stripped_down_poker_efg(), False, "21", -0.75), # Fold King/Bet Queen + (games.create_stripped_down_poker_efg(), False, "22", -1), # Fold/Fold + (games.create_stripped_down_poker_efg(), True, "11", "1/2"), + (games.create_stripped_down_poker_efg(), True, "12", "1/4"), + (games.create_stripped_down_poker_efg(), True, "21", "-3/4"), + (games.create_stripped_down_poker_efg(), True, "22", -1), + ], ) -def test_strategy_value_by_label_reference(game: gbt.Game, rational_flag: bool, label: str, - value: float | str): +def test_strategy_value_by_label_reference( + game: gbt.Game, rational_flag: bool, label: str, value: float | str +): value = gbt.Rational(value) if rational_flag else value assert game.mixed_strategy_profile(rational=rational_flag).strategy_value(label) == value @@ -404,27 +542,26 @@ def test_strategy_value_by_label_reference(game: gbt.Game, rational_flag: bool, @pytest.mark.parametrize( "game,rational_flag", [ - (games.create_mixed_behav_game_efg(), False), - (games.create_mixed_behav_game_efg(), True), - (games.create_centipede_game_with_chance_efg(), False), - (games.create_centipede_game_with_chance_efg(), True), - ] + (games.read_from_file("mixed_behavior_game.efg"), False), + (games.read_from_file("mixed_behavior_game.efg"), True), + (games.read_from_file("cent3.efg"), False), + (games.read_from_file("cent3.efg"), True), + ], ) def test_as_behavior_roundtrip(game: gbt.Game, rational_flag: bool): - assert ( - game.mixed_strategy_profile(rational=rational_flag).as_behavior().as_strategy() == - game.mixed_strategy_profile(rational=rational_flag) - ) + assert game.mixed_strategy_profile( + rational=rational_flag + ).as_behavior().as_strategy() == game.mixed_strategy_profile(rational=rational_flag) @pytest.mark.parametrize( "game,rational_flag", [ - (games.create_2x2_zero_nfg(), False), - (games.create_2x2_zero_nfg(), True), - (games.create_coord_4x4_nfg(), False), - (games.create_coord_4x4_nfg(), True), - ] + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), False), + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), True), + (games.read_from_file("coordination_4x4_payoff.nfg"), False), + (games.read_from_file("coordination_4x4_payoff.nfg"), True), + ], ) def test_as_behavior_error(game: gbt.Game, rational_flag: bool): with pytest.raises(gbt.UndefinedOperationError): @@ -434,27 +571,56 @@ def test_as_behavior_error(game: gbt.Game, rational_flag: bool): @pytest.mark.parametrize( "game,profile_data,rational_flag,payoffs", [ - ############################################################################### - # zero matrix nfg - (games.create_2x2_zero_nfg(), None, True, (0, 0)), - ############################################################################### - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), None, False, (0.25, 0.25)), - (games.create_coord_4x4_nfg(), None, True, ("1/4", "1/4")), - (games.create_coord_4x4_nfg(), - [["1/3", "1/3", "1/3", 0], ["1/3", "1/3", "1/3", 0]], True, ("1/3", "1/3")), - (games.create_coord_4x4_nfg(), - [["1/3", "1/3", 0, "1/3"], ["1/3", "1/3", "1/3", 0]], True, ("2/9", "2/9")), - ############################################################################### - # 2x2x2 nfg - (games.create_2x2x2_nfg(), None, True, ("4/8", "16/8", "4/8")), - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], True, (0, 0, 0)), - (games.create_2x2x2_nfg(), [[0, 1], [1, 0], [1, 0]], True, (1, 2, -1)), - (games.create_2x2x2_nfg(), [["1/2", "1/2"], [1, 0], [1, 0]], True, ("1/2", 1, "-1/2")), + ############################################################################### + # zero matrix nfg + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), None, True, (0, 0)), + ############################################################################### + # 4x4 coordination nfg + (games.read_from_file("coordination_4x4_payoff.nfg"), None, False, (0.25, 0.25)), + (games.read_from_file("coordination_4x4_payoff.nfg"), None, True, ("1/4", "1/4")), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/3", "1/3", "1/3", 0], ["1/3", "1/3", "1/3", 0]], + True, + ("1/3", "1/3"), + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/3", "1/3", 0, "1/3"], ["1/3", "1/3", "1/3", 0]], + True, + ("2/9", "2/9"), + ), + ############################################################################### + # 2x2x2 nfg + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + None, + True, + ("4/8", "16/8", "4/8"), + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], [1, 0]], + True, + (0, 0, 0), + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[0, 1], [1, 0], [1, 0]], + True, + (1, 2, -1), + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [["1/2", "1/2"], [1, 0], [1, 0]], + True, + ("1/2", 1, "-1/2"), + ), ], ) -def test_payoffs_reference(game: gbt.Game, profile_data: list, rational_flag: bool, - payoffs: tuple): +def test_payoffs_reference( + game: gbt.Game, profile_data: list, rational_flag: bool, payoffs: tuple +): profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) for payoff, player in zip(payoffs, profile.game.players, strict=True): payoff = gbt.Rational(payoff) if rational_flag else payoff @@ -464,35 +630,61 @@ def test_payoffs_reference(game: gbt.Game, profile_data: list, rational_flag: bo @pytest.mark.parametrize( "game,profile_data,rational_flag,strategy_values", [ - ############################################################################### - # zero matrix nfg - (games.create_2x2_zero_nfg(), None, False, ([0, 0], [0, 0])), - (games.create_2x2_zero_nfg(), None, True, ([0, 0], [0, 0])), - ############################################################################### - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), None, False, - ([0.25, 0.25, 0.25, 0.25], [0.25, 0.25, 0.25, 0.25])), - (games.create_coord_4x4_nfg(), None, True, - ([0.25, 0.25, 0.25, 0.25], [0.25, 0.25, 0.25, 0.25])), - (games.create_coord_4x4_nfg(), [["1", "0", "0", "0"], ["1", "0", "0", "0"]], - True, (["1", "0", "0", "0"], ["1", "0", "0", "0"])), - (games.create_coord_4x4_nfg(), [["3/7", "0", "0", "4/7"], ["1/3", "1/3", "1/3", "0"]], - True, (["1/3", "1/3", "1/3", "0"], ["3/7", "0", "0", "4/7"])), - ############################################################################### - # 2x2x2 nfg - (games.create_2x2x2_nfg(), None, True, (["1/2", "1/2"], [2, 2], ["1/2", "1/2"])), - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], True, ([0, 1], [0, 4], [0, 1])), - ############################################################################### - # stripped-down poker efg - (games.create_stripped_down_poker_efg(), None, False, [(0.5, 0.25, -0.75, -1), (0.5, 0)]), - ] + ############################################################################### + # zero matrix nfg + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), None, False, ([0, 0], [0, 0])), + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), None, True, ([0, 0], [0, 0])), + ############################################################################### + # 4x4 coordination nfg + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + None, + False, + ([0.25, 0.25, 0.25, 0.25], [0.25, 0.25, 0.25, 0.25]), + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + None, + True, + ([0.25, 0.25, 0.25, 0.25], [0.25, 0.25, 0.25, 0.25]), + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1", "0", "0", "0"], ["1", "0", "0", "0"]], + True, + (["1", "0", "0", "0"], ["1", "0", "0", "0"]), + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["3/7", "0", "0", "4/7"], ["1/3", "1/3", "1/3", "0"]], + True, + (["1/3", "1/3", "1/3", "0"], ["3/7", "0", "0", "4/7"]), + ), + ############################################################################### + # 2x2x2 nfg + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + None, + True, + (["1/2", "1/2"], [2, 2], ["1/2", "1/2"]), + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], [1, 0]], + True, + ([0, 1], [0, 4], [0, 1]), + ), + ############################################################################### + # stripped-down poker efg + (games.create_stripped_down_poker_efg(), None, False, [(0.5, 0.25, -0.75, -1), (0.5, 0)]), + ], ) -def test_strategy_value_reference(game: gbt.Game, profile_data: list, rational_flag: bool, - strategy_values: list): +def test_strategy_value_reference( + game: gbt.Game, profile_data: list, rational_flag: bool, strategy_values: list +): profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) for strategy_values_for_player, player in zip( - strategy_values, profile.game.players, - strict=True + strategy_values, profile.game.players, strict=True ): for i, s in enumerate(player.strategies): sv = strategy_values_for_player[i] @@ -503,55 +695,196 @@ def test_strategy_value_reference(game: gbt.Game, profile_data: list, rational_f @pytest.mark.parametrize( "game,profile_data,liap_exp,tol,rational_flag", [ - ############################################################################## - # Zero matrix nfg, all liap_values are zero - (games.create_2x2_zero_nfg(), [["3/4", "1/4"], ["2/5", "3/5"]], 0, ZERO, True), - (games.create_2x2_zero_nfg(), [["1/2", "1/2"], ["1/2", "1/2"]], 0, ZERO, True), - (games.create_2x2_zero_nfg(), [[1, 0], [1, 0]], 0, ZERO, True), - (games.create_2x2_zero_nfg(), [[1/4, 3/4], [2/5, 3/5]], 0, TOL, False), - ############################################################################## - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), None, 0, ZERO, True), - (games.create_coord_4x4_nfg(), None, 0, TOL, False), - (games.create_coord_4x4_nfg(), [[1, 0, 0, 0], [1, 0, 0, 0]], 0, ZERO, True), - (games.create_coord_4x4_nfg(), [[1, 0, 0, 0], [1, 0, 0, 0]], 0, TOL, False), - (games.create_coord_4x4_nfg(), - [["1/3", "1/2", "1/12", "1/12"], ["3/8", "1/8", "1/4", "1/4"]], - "245/2304", ZERO, True), - (games.create_coord_4x4_nfg(), [[1/3, 1/2, 1/12, 1/12], [3/8, 1/8, 1/4, 1/4]], - 245/2304, TOL, False), - (games.create_coord_4x4_nfg(), - [["1/3", 0, 0, "2/3"], [1, 0, 0, 0]], "5/9", ZERO, True), - (games.create_coord_4x4_nfg(), - [[1/3, 0, 0, 2/3], [1, 0, 0, 0]], 5/9, TOL, False), - ############################################################################## - # El Farol bar game efg - (games.create_el_farol_bar_game_efg(), - [["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"]], - 0, ZERO, True), - (games.create_el_farol_bar_game_efg(), [[1, 0], [1, 0], [0, 1], [0, 1], [0, 1]], - 0, ZERO, True), - ############################################################################## - # # 2x2x2 nfg with 2 pure and 1 mixed eq - # Pure non-Nash eq: - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], 18, ZERO, True), # 4^2+1+1 - (games.create_2x2x2_nfg(), [[0, 1], [0, 1], [0, 1]], 18, ZERO, True), # 4^2+1+1 - (games.create_2x2x2_nfg(), [[1, 0], [0, 1], [0, 1]], 9, ZERO, True), # 3^2 - (games.create_2x2x2_nfg(), [[0, 1], [1, 0], [1, 0]], 9, ZERO, True), # 3^2 - (games.create_2x2x2_nfg(), [[1, 0], [0, 1], [0, 1]], 9, ZERO, True), # 3^2 - (games.create_2x2x2_nfg(), [[1, 1], [1, 0], [0, 0]], 9, ZERO, True), # 3^2 - # Non-pure non-Nash eq: - (games.create_2x2x2_nfg(), [["1/2", "1/2"], [1, 0], [1, 0]], "33/4", ZERO, True), - (games.create_2x2x2_nfg(), [[1, 0], ["1/2", "1/2"], [1, 0]], 4, ZERO, True), - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], ["1/2", "1/2"]], "33/4", ZERO, True), - # Nash eq: - (games.create_2x2x2_nfg(), [[1, 0], [0, 1], [1, 0]], 0, ZERO, True), - (games.create_2x2x2_nfg(), [[0, 1], [1, 0], [0, 1]], 0, ZERO, True), - (games.create_2x2x2_nfg(), None, 0, ZERO, True), # uniform is Nash - ] + ############################################################################## + # Zero matrix nfg, all liap_values are zero + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [["3/4", "1/4"], ["2/5", "3/5"]], + 0, + ZERO, + True, + ), + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [["1/2", "1/2"], ["1/2", "1/2"]], + 0, + ZERO, + True, + ), + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [[1, 0], [1, 0]], + 0, + ZERO, + True, + ), + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [[1 / 4, 3 / 4], [2 / 5, 3 / 5]], + 0, + TOL, + False, + ), + ############################################################################## + # 4x4 coordination nfg + (games.read_from_file("coordination_4x4_payoff.nfg"), None, 0, ZERO, True), + (games.read_from_file("coordination_4x4_payoff.nfg"), None, 0, TOL, False), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1, 0, 0, 0], [1, 0, 0, 0]], + 0, + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1, 0, 0, 0], [1, 0, 0, 0]], + 0, + TOL, + False, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/3", "1/2", "1/12", "1/12"], ["3/8", "1/8", "1/4", "1/4"]], + "245/2304", + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1 / 3, 1 / 2, 1 / 12, 1 / 12], [3 / 8, 1 / 8, 1 / 4, 1 / 4]], + 245 / 2304, + TOL, + False, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/3", 0, 0, "2/3"], [1, 0, 0, 0]], + "5/9", + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1 / 3, 0, 0, 2 / 3], [1, 0, 0, 0]], + 5 / 9, + TOL, + False, + ), + ############################################################################## + # El Farol bar game efg + ( + games.read_from_file("el_farol_bar.efg"), + [["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"]], + 0, + ZERO, + True, + ), + ( + games.read_from_file("el_farol_bar.efg"), + [[1, 0], [1, 0], [0, 1], [0, 1], [0, 1]], + 0, + ZERO, + True, + ), + ############################################################################## + # # 2x2x2 nfg with 2 pure and 1 mixed eq + # Pure non-Nash eq: + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], [1, 0]], + 18, + ZERO, + True, + ), # 4^2+1+1 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[0, 1], [0, 1], [0, 1]], + 18, + ZERO, + True, + ), # 4^2+1+1 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [0, 1], [0, 1]], + 9, + ZERO, + True, + ), # 3^2 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[0, 1], [1, 0], [1, 0]], + 9, + ZERO, + True, + ), # 3^2 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [0, 1], [0, 1]], + 9, + ZERO, + True, + ), # 3^2 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 1], [1, 0], [0, 0]], + 9, + ZERO, + True, + ), # 3^2 + # Non-pure non-Nash eq: + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [["1/2", "1/2"], [1, 0], [1, 0]], + "33/4", + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], ["1/2", "1/2"], [1, 0]], + 4, + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], ["1/2", "1/2"]], + "33/4", + ZERO, + True, + ), + # Nash eq: + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [0, 1], [1, 0]], + 0, + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[0, 1], [1, 0], [0, 1]], + 0, + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + None, + 0, + ZERO, + True, + ), # uniform is Nash + ], ) -def test_liap_value_reference(game: gbt.Game, profile_data: list, liap_exp: float | str, - tol: float | gbt.Rational | int, rational_flag: bool): +def test_liap_value_reference( + game: gbt.Game, + profile_data: list, + liap_exp: float | str, + tol: float | gbt.Rational | int, + rational_flag: bool, +): profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) liap_exp = gbt.Rational(liap_exp) if rational_flag else liap_exp assert abs(profile.liap_value() - liap_exp) <= tol @@ -560,57 +893,196 @@ def test_liap_value_reference(game: gbt.Game, profile_data: list, liap_exp: floa @pytest.mark.parametrize( "game,profile_data,player_regrets_exp,tol,rational_flag", [ - ############################################################################## - # Zero matrix nfg, all liap_values are zero - (games.create_2x2_zero_nfg(), [["3/4", "1/4"], ["2/5", "3/5"]], [0]*2, ZERO, True), - (games.create_2x2_zero_nfg(), [["1/2", "1/2"], ["1/2", "1/2"]], [0]*2, ZERO, True), - (games.create_2x2_zero_nfg(), [[1, 0], [1, 0]], [0]*2, ZERO, True), - (games.create_2x2_zero_nfg(), [[1/4, 3/4], [2/5, 3/5]], [0]*2, TOL, False), - ############################################################################## - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), None, [0]*2, ZERO, True), - (games.create_coord_4x4_nfg(), None, [0]*2, TOL, False), - (games.create_coord_4x4_nfg(), [[1, 0, 0, 0], [1, 0, 0, 0]], [0]*2, ZERO, True), - (games.create_coord_4x4_nfg(), [[1, 0, 0, 0], [1, 0, 0, 0]], [0]*2, TOL, False), - (games.create_coord_4x4_nfg(), - [["1/3", "1/2", "1/12", "1/12"], ["3/8", "1/8", "1/4", "1/4"]], - ["7/48", "13/48"], ZERO, True), - (games.create_coord_4x4_nfg(), [[1/3, 1/2, 1/12, 1/12], [3/8, 1/8, 1/4, 1/4]], - [7/48, 13/48], TOL, False), - (games.create_coord_4x4_nfg(), - [["1/3", 0, 0, "2/3"], [1, 0, 0, 0]], ["2/3", "1/3"], ZERO, True), - (games.create_coord_4x4_nfg(), - [[1/3, 0, 0, 2/3], [1, 0, 0, 0]], [2/3, 1/3], TOL, False), - ############################################################################## - # El Farol bar game efg - (games.create_el_farol_bar_game_efg(), - [["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"]], - [0]*5, ZERO, True), - (games.create_el_farol_bar_game_efg(), [[1, 0], [1, 0], [0, 1], [0, 1], [0, 1]], - [0]*5, ZERO, True), - ############################################################################## - # 2x2x2 nfg with 2 pure and 1 mixed eq - # Pure non-Nash - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], [1, 4, 1], ZERO, True), # 111 - (games.create_2x2x2_nfg(), [[0, 1], [0, 1], [0, 1]], [1, 4, 1], ZERO, True), # 000 - (games.create_2x2x2_nfg(), [[1, 0], [0, 1], [0, 1]], [0, 0, 3], ZERO, True), # 100 - (games.create_2x2x2_nfg(), [[0, 1], [1, 0], [1, 0]], [0, 0, 3], ZERO, True), # 011 - (games.create_2x2x2_nfg(), [[0, 1], [0, 1], [1, 0]], [3, 0, 0], ZERO, True), # 001 - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], [0, 1]], [3, 0, 0], ZERO, True), # 110 - # Mixed non-Nash - (games.create_2x2x2_nfg(), [["1/2", "1/2"], [1, 0], [1, 0]], ["1/2", 2, 2], ZERO, True), - (games.create_2x2x2_nfg(), [[1, 0], ["1/2", "1/2"], [1, 0]], [0, 2, 0], ZERO, True), - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], ["1/2", "1/2"]], [2, 2, "1/2"], ZERO, True), - # Nash eq: - (games.create_2x2x2_nfg(), [[1, 0], [0, 1], [1, 0]], [0]*3, ZERO, True), # 101 - (games.create_2x2x2_nfg(), [[0, 1], [1, 0], [0, 1]], [0]*3, ZERO, True), # 010 - (games.create_2x2x2_nfg(), None, [0]*3, ZERO, True), # uniform is Nash - ] + ############################################################################## + # Zero matrix nfg, all liap_values are zero + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [["3/4", "1/4"], ["2/5", "3/5"]], + [0] * 2, + ZERO, + True, + ), + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [["1/2", "1/2"], ["1/2", "1/2"]], + [0] * 2, + ZERO, + True, + ), + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [[1, 0], [1, 0]], + [0] * 2, + ZERO, + True, + ), + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [[1 / 4, 3 / 4], [2 / 5, 3 / 5]], + [0] * 2, + TOL, + False, + ), + ############################################################################## + # 4x4 coordination nfg + (games.read_from_file("coordination_4x4_payoff.nfg"), None, [0] * 2, ZERO, True), + (games.read_from_file("coordination_4x4_payoff.nfg"), None, [0] * 2, TOL, False), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1, 0, 0, 0], [1, 0, 0, 0]], + [0] * 2, + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1, 0, 0, 0], [1, 0, 0, 0]], + [0] * 2, + TOL, + False, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/3", "1/2", "1/12", "1/12"], ["3/8", "1/8", "1/4", "1/4"]], + ["7/48", "13/48"], + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1 / 3, 1 / 2, 1 / 12, 1 / 12], [3 / 8, 1 / 8, 1 / 4, 1 / 4]], + [7 / 48, 13 / 48], + TOL, + False, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/3", 0, 0, "2/3"], [1, 0, 0, 0]], + ["2/3", "1/3"], + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1 / 3, 0, 0, 2 / 3], [1, 0, 0, 0]], + [2 / 3, 1 / 3], + TOL, + False, + ), + ############################################################################## + # El Farol bar game efg + ( + games.read_from_file("el_farol_bar.efg"), + [["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"]], + [0] * 5, + ZERO, + True, + ), + ( + games.read_from_file("el_farol_bar.efg"), + [[1, 0], [1, 0], [0, 1], [0, 1], [0, 1]], + [0] * 5, + ZERO, + True, + ), + ############################################################################## + # 2x2x2 nfg with 2 pure and 1 mixed eq + # Pure non-Nash + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], [1, 0]], + [1, 4, 1], + ZERO, + True, + ), # 111 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[0, 1], [0, 1], [0, 1]], + [1, 4, 1], + ZERO, + True, + ), # 000 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [0, 1], [0, 1]], + [0, 0, 3], + ZERO, + True, + ), # 100 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[0, 1], [1, 0], [1, 0]], + [0, 0, 3], + ZERO, + True, + ), # 011 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[0, 1], [0, 1], [1, 0]], + [3, 0, 0], + ZERO, + True, + ), # 001 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], [0, 1]], + [3, 0, 0], + ZERO, + True, + ), # 110 + # Mixed non-Nash + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [["1/2", "1/2"], [1, 0], [1, 0]], + ["1/2", 2, 2], + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], ["1/2", "1/2"], [1, 0]], + [0, 2, 0], + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], ["1/2", "1/2"]], + [2, 2, "1/2"], + ZERO, + True, + ), + # Nash eq: + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [0, 1], [1, 0]], + [0] * 3, + ZERO, + True, + ), # 101 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[0, 1], [1, 0], [0, 1]], + [0] * 3, + ZERO, + True, + ), # 010 + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + None, + [0] * 3, + ZERO, + True, + ), # uniform is Nash + ], ) -def test_player_regret_max_regret_reference(game: gbt.Game, profile_data: list, - player_regrets_exp: list, - tol: float | gbt.Rational | int, - rational_flag: bool): +def test_player_regret_max_regret_reference( + game: gbt.Game, + profile_data: list, + player_regrets_exp: list, + tol: float | gbt.Rational | int, + rational_flag: bool, +): profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) if rational_flag: player_regrets_exp = [gbt.Rational(r) for r in player_regrets_exp] @@ -622,119 +1094,216 @@ def test_player_regret_max_regret_reference(game: gbt.Game, profile_data: list, @pytest.mark.parametrize( "game,rational_flag", [ - ################################################################################# - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), False), - (games.create_coord_4x4_nfg(), True), - ################################################################################# - # Zero matrix nfg - (games.create_2x2_zero_nfg(), False), - (games.create_2x2_zero_nfg(), True), - ################################################################################# - # El Farol bar game efg - (games.create_el_farol_bar_game_efg(), False), - (games.create_el_farol_bar_game_efg(), True), - ################################################################################# - # Centipede with chance efg - (games.create_centipede_game_with_chance_efg(), False), - (games.create_centipede_game_with_chance_efg(), True), - ################################################################################# - # 2x2x2 nfg - (games.create_2x2x2_nfg(), False), - (games.create_2x2x2_nfg(), True), - ] + ################################################################################# + # 4x4 coordination nfg + (games.read_from_file("coordination_4x4_payoff.nfg"), False), + (games.read_from_file("coordination_4x4_payoff.nfg"), True), + ################################################################################# + # Zero matrix nfg + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), False), + (games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), True), + ################################################################################# + # El Farol bar game efg + (games.read_from_file("el_farol_bar.efg"), False), + (games.read_from_file("el_farol_bar.efg"), True), + ################################################################################# + # Centipede with chance efg + (games.read_from_file("cent3.efg"), False), + (games.read_from_file("cent3.efg"), True), + ################################################################################# + # 2x2x2 nfg + (games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), False), + (games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), True), + ], ) def test_strategy_regret_consistency(game: gbt.Game, rational_flag: bool): profile = game.mixed_strategy_profile(rational=False) for player in game.players: for strategy in player.strategies: - assert ( - profile.strategy_regret(strategy) == - ( - max(profile.strategy_value(s) for s in player.strategies) - - profile.strategy_value(strategy) - ) + assert profile.strategy_regret(strategy) == ( + max(profile.strategy_value(s) for s in player.strategies) + - profile.strategy_value(strategy) ) @pytest.mark.parametrize( "game,profile_data,tol,rational_flag", [ - ################################################################################# - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), - [["1/5", "2/5", "0/5", "2/5"], ["3/8", "1/4", "3/8", "0/4"]], ZERO, True), - (games.create_coord_4x4_nfg(), - [[1/3, 1/3, 0/3, 1/3], [1/4, 1/4, 3/8, 1/8]], TOL, False), - ################################################################################# - # Centipede with chance efg - (games.create_centipede_game_with_chance_efg(), - [["1/3", "1/3", "1/3", "0/1"], ["1/10", "3/5", "3/10", 0]], ZERO, True), - (games.create_centipede_game_with_chance_efg(), - [[1/3, 1/3, 1/3, 0], [.10, 3/5, .3, 0]], TOL, False), - ################################################################################# - # El Farol bar game efg - (games.create_el_farol_bar_game_efg(), - [[1, 0], ["1/2", "1/2"], ["1/3", "2/3"], ["1/5", "4/5"], ["1/8", "7/8"]], ZERO, True), - (games.create_el_farol_bar_game_efg(), - [[1, 0], [1/2, 1/2], [1/3, 2/3], [1/5, 4/5], [1/8, 7/8]], TOL, False), - ################################################################################# - # 2x2x2 nfg with 2 pure and 1 mixed eq - (games.create_2x2x2_nfg(), None, ZERO, True), - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], ZERO, True), - (games.create_2x2x2_nfg(), None, TOL, False), - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], TOL, False), - ] + ################################################################################# + # 4x4 coordination nfg + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/5", "2/5", "0/5", "2/5"], ["3/8", "1/4", "3/8", "0/4"]], + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1 / 3, 1 / 3, 0 / 3, 1 / 3], [1 / 4, 1 / 4, 3 / 8, 1 / 8]], + TOL, + False, + ), + ################################################################################# + # Centipede with chance efg + ( + games.read_from_file("cent3.efg"), + [["1/3", "1/3", "1/3", "0/1"], ["1/10", "3/5", "3/10", 0]], + ZERO, + True, + ), + ( + games.read_from_file("cent3.efg"), + [[1 / 3, 1 / 3, 1 / 3, 0], [0.10, 3 / 5, 0.3, 0]], + TOL, + False, + ), + ################################################################################# + # El Farol bar game efg + ( + games.read_from_file("el_farol_bar.efg"), + [[1, 0], ["1/2", "1/2"], ["1/3", "2/3"], ["1/5", "4/5"], ["1/8", "7/8"]], + ZERO, + True, + ), + ( + games.read_from_file("el_farol_bar.efg"), + [[1, 0], [1 / 2, 1 / 2], [1 / 3, 2 / 3], [1 / 5, 4 / 5], [1 / 8, 7 / 8]], + TOL, + False, + ), + ################################################################################# + # 2x2x2 nfg with 2 pure and 1 mixed eq + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + None, + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], [1, 0]], + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + None, + TOL, + False, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], [1, 0]], + TOL, + False, + ), + ], ) -def test_liap_value_consistency(game: gbt.Game, profile_data: list, - tol: float | gbt.Rational, - rational_flag: bool): +def test_liap_value_consistency( + game: gbt.Game, profile_data: list, tol: float | gbt.Rational, rational_flag: bool +): profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) assert ( - abs(profile.liap_value() - - sum([max(profile.strategy_value(strategy) - profile.payoff(player), 0)**2 - for player in game.players for strategy in player.strategies])) <= tol + abs( + profile.liap_value() + - sum( + [ + max(profile.strategy_value(strategy) - profile.payoff(player), 0) ** 2 + for player in game.players + for strategy in player.strategies + ] + ) + ) + <= tol ) @pytest.mark.parametrize( "game,profile_data,tol,rational_flag", [ - ################################################################################# - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), - [["1/5", "2/5", "0/5", "2/5"], ["3/8", "1/4", "3/8", "0/4"]], ZERO, True), - (games.create_coord_4x4_nfg(), - [[1/3, 1/3, 0/3, 1/3], [1/4, 1/4, 3/8, 1/8]], TOL, False), - ################################################################################# - # Centipede with chance efg - (games.create_centipede_game_with_chance_efg(), - [["1/3", "1/3", "1/3", "0/1"], ["1/10", "3/5", "3/10", 0]], ZERO, True), - (games.create_centipede_game_with_chance_efg(), - [[1/3, 1/3, 1/3, 0], [.10, 3/5, .3, 0]], TOL, False), - ################################################################################# - # El Farol bar game efg - (games.create_el_farol_bar_game_efg(), - [[1, 0], ["1/2", "1/2"], ["1/3", "2/3"], ["1/5", "4/5"], ["1/8", "7/8"]], ZERO, True), - (games.create_el_farol_bar_game_efg(), - [[1, 0], [1/2, 1/2], [1/3, 2/3], [1/5, 4/5], [1/8, 7/8]], TOL, False), - ################################################################################# - # 2x2x2 nfg with 2 pure and 1 mixed eq - (games.create_2x2x2_nfg(), None, ZERO, True), - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], ZERO, True), - (games.create_2x2x2_nfg(), None, TOL, False), - (games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], TOL, False), - ] + ################################################################################# + # 4x4 coordination nfg + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/5", "2/5", "0/5", "2/5"], ["3/8", "1/4", "3/8", "0/4"]], + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1 / 3, 1 / 3, 0 / 3, 1 / 3], [1 / 4, 1 / 4, 3 / 8, 1 / 8]], + TOL, + False, + ), + ################################################################################# + # Centipede with chance efg + ( + games.read_from_file("cent3.efg"), + [["1/3", "1/3", "1/3", "0/1"], ["1/10", "3/5", "3/10", 0]], + ZERO, + True, + ), + ( + games.read_from_file("cent3.efg"), + [[1 / 3, 1 / 3, 1 / 3, 0], [0.10, 3 / 5, 0.3, 0]], + TOL, + False, + ), + ################################################################################# + # El Farol bar game efg + ( + games.read_from_file("el_farol_bar.efg"), + [[1, 0], ["1/2", "1/2"], ["1/3", "2/3"], ["1/5", "4/5"], ["1/8", "7/8"]], + ZERO, + True, + ), + ( + games.read_from_file("el_farol_bar.efg"), + [[1, 0], [1 / 2, 1 / 2], [1 / 3, 2 / 3], [1 / 5, 4 / 5], [1 / 8, 7 / 8]], + TOL, + False, + ), + ################################################################################# + # 2x2x2 nfg with 2 pure and 1 mixed eq + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + None, + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], [1, 0]], + ZERO, + True, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + None, + TOL, + False, + ), + ( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + [[1, 0], [1, 0], [1, 0]], + TOL, + False, + ), + ], ) -def test_player_regret_max_regret_consistency(game: gbt.Game, profile_data: list, - tol: float | gbt.Rational, - rational_flag: bool): +def test_player_regret_max_regret_consistency( + game: gbt.Game, profile_data: list, tol: float | gbt.Rational, rational_flag: bool +): profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) player_regrets = [] for p in game.players: - p_regret = max([max(profile.strategy_value(strategy) - profile.payoff(p), 0) - for strategy in p.strategies]) + p_regret = max( + [ + max(profile.strategy_value(strategy) - profile.payoff(p), 0) + for strategy in p.strategies + ] + ) player_regrets.append(p_regret) assert abs(profile.player_regret(p) - p_regret) <= tol assert abs(profile.max_regret() - max(player_regrets)) <= tol @@ -743,147 +1312,272 @@ def test_player_regret_max_regret_consistency(game: gbt.Game, profile_data: list @pytest.mark.parametrize( "game,profile1,profile2,alpha,tol,rational_flag", [ - ################################################################################# - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), - [["1/5", "2/5", "0/5", "2/5"], ["3/8", "1/4", "3/8", "0/4"]], - [["1/5", "2/5", "0/5", "2/5"], ["1/4", "3/8", "0/4", "3/8"]], - gbt.Rational("3/5"), ZERO, True), - (games.create_coord_4x4_nfg(), - [[1/5, 2/5, 0/5, 2/5], [3/8, 1/4, 3/8, 0/4]], [[1/5, 2/5, 0/5, 2/5], [1/4, 3/8, 0/4, 3/8]], - 3/5, TOL, False), - ################################################################################# - # Zero matrix nfg - (games.create_2x2_zero_nfg(), - [["1/4", "3/4"], ["3/5", "2/5"]], [["1/2", "1/2"], ["3/5", "2/5"]], - gbt.Rational("5/6"), ZERO, True), - ################################################################################# - # Centipede game with chance - (games.create_centipede_game_with_chance_efg(), - [["1/3", "1/3", "1/3", "0/1"], ["1/10", "3/5", "3/10", "0/1"]], - [["1/3", "1/3", "1/3", "0/1"], ["1/5", "2/5", "1/5", "1/5"]], - gbt.Rational("1/12"), ZERO, True), - (games.create_centipede_game_with_chance_efg(), - [[1/3, 1/3, 1/3, 0/1], [1/10, 3/5, 3/10, 0/1]], [[1/3, 1/3, 1/3, 0/1], [1/5, 2/5, 1/5, 1/5]], - 1/12, TOL, False), - ################################################################################# - # Selten's horse game - (games.create_selten_horse_game_efg(), - [["4/9", "5/9"], ["1/11", "10/11"], ["8/9", "1/9"]], - [["4/9", "5/9"], ["10/11", "1/11"], ["8/9", "1/9"]], - gbt.Rational("4/9"), ZERO, True), - ################################################################################# - # El Farol bar game - (games.create_el_farol_bar_game_efg(), - [["4/9", "5/9"], ["1/3", "2/3"], ["1/2", "1/2"], ["11/12", "1/12"], ["1/2", "1/2"]], - [["4/9", "5/9"], ["1/3", "2/3"], ["1/2", "1/2"], ["1/12", "11/12"], ["1/2", "1/2"]], - gbt.Rational("1/2"), ZERO, True), - ] + ################################################################################# + # 4x4 coordination nfg + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/5", "2/5", "0/5", "2/5"], ["3/8", "1/4", "3/8", "0/4"]], + [["1/5", "2/5", "0/5", "2/5"], ["1/4", "3/8", "0/4", "3/8"]], + gbt.Rational("3/5"), + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1 / 5, 2 / 5, 0 / 5, 2 / 5], [3 / 8, 1 / 4, 3 / 8, 0 / 4]], + [[1 / 5, 2 / 5, 0 / 5, 2 / 5], [1 / 4, 3 / 8, 0 / 4, 3 / 8]], + 3 / 5, + TOL, + False, + ), + ################################################################################# + # Zero matrix nfg + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [["1/4", "3/4"], ["3/5", "2/5"]], + [["1/2", "1/2"], ["3/5", "2/5"]], + gbt.Rational("5/6"), + ZERO, + True, + ), + ################################################################################# + # Centipede game with chance + ( + games.read_from_file("cent3.efg"), + [["1/3", "1/3", "1/3", "0/1"], ["1/10", "3/5", "3/10", "0/1"]], + [["1/3", "1/3", "1/3", "0/1"], ["1/5", "2/5", "1/5", "1/5"]], + gbt.Rational("1/12"), + ZERO, + True, + ), + ( + games.read_from_file("cent3.efg"), + [[1 / 3, 1 / 3, 1 / 3, 0 / 1], [1 / 10, 3 / 5, 3 / 10, 0 / 1]], + [[1 / 3, 1 / 3, 1 / 3, 0 / 1], [1 / 5, 2 / 5, 1 / 5, 1 / 5]], + 1 / 12, + TOL, + False, + ), + ################################################################################# + # Selten's horse + ( + games.read_from_file("e01.efg"), + [["4/9", "5/9"], ["1/11", "10/11"], ["8/9", "1/9"]], + [["4/9", "5/9"], ["10/11", "1/11"], ["8/9", "1/9"]], + gbt.Rational("4/9"), + ZERO, + True, + ), + ################################################################################# + # El Farol bar game + ( + games.read_from_file("el_farol_bar.efg"), + [["4/9", "5/9"], ["1/3", "2/3"], ["1/2", "1/2"], ["11/12", "1/12"], ["1/2", "1/2"]], + [["4/9", "5/9"], ["1/3", "2/3"], ["1/2", "1/2"], ["1/12", "11/12"], ["1/2", "1/2"]], + gbt.Rational("1/2"), + ZERO, + True, + ), + ], ) -def test_linearity_payoff_property(game: gbt.Game, profile1: list, profile2: list, - alpha: float | gbt.Rational, - tol: float | gbt.Rational, rational_flag: bool): +def test_linearity_payoff_property( + game: gbt.Game, + profile1: list, + profile2: list, + alpha: float | gbt.Rational, + tol: float | gbt.Rational, + rational_flag: bool, +): profile1 = game.mixed_strategy_profile(rational=rational_flag, data=profile1) profile2 = game.mixed_strategy_profile(rational=rational_flag, data=profile2) - profile_data = [[alpha*profile1[strategy] + (1-alpha)*profile2[strategy] - for strategy in player.strategies] for player in game.players] + profile_data = [ + [ + alpha * profile1[strategy] + (1 - alpha) * profile2[strategy] + for strategy in player.strategies + ] + for player in game.players + ] profile3 = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) for player in game.players: assert ( - abs(alpha*profile1.payoff(player) + (1 - alpha)*profile2.payoff(player) - - profile3.payoff(player)) <= tol + abs( + alpha * profile1.payoff(player) + + (1 - alpha) * profile2.payoff(player) + - profile3.payoff(player) + ) + <= tol ) @pytest.mark.parametrize( "game,profile_data,tol,rational_flag", [ - ################################################################################# - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), - [["1/5", "2/5", "0/5", "2/5"], ["1/4", "3/8", "0/4", "3/8"]], ZERO, True), - (games.create_coord_4x4_nfg(), [[0.2, 0.4, 0, 0.4], [1/4, 3/8, 0, 3/8]], TOL, False), - (gbt.Game.from_arrays([[1, 2], [-3, 4]], [[-4, 3], [2, 1]]), [[1/2, 1/2], [3/5, 2/5]], - TOL, False), - ################################################################################# - # Zero matrix nfg - (games.create_2x2_zero_nfg(), [["4/5", "1/5"], ["4/7", "3/7"]], ZERO, True), - ################################################################################# - # Centipede game with chance - (games.create_centipede_game_with_chance_efg(), - [["1/5", "2/5", "1/5", "1/5"], ["1/10", "3/5", "3/10", "0/1"]], ZERO, True), - (games.create_centipede_game_with_chance_efg(), - [[1/3, 1/3, 1/3, 0/1], [1/10, 3/5, 3/10, 0/1]], TOL, False), - ################################################################################# - # Selten's horse - (games.create_selten_horse_game_efg(), [["4/9", "5/9"], ["6/11", "5/11"], ["4/7", "3/7"]], - ZERO, True), - (games.create_selten_horse_game_efg(), [[4/9, 5/9], [6/11, 5/11], [4/7, 3/7]], TOL, False), - ################################################################################# - # El Farol bar game - (games.create_el_farol_bar_game_efg(), - [["4/9", "5/9"], ["1/3", "2/3"], ["0/1", "1/1"], ["11/12", "1/12"], ["1/3", "2/3"]], - ZERO, True), - ] + ################################################################################# + # 4x4 coordination nfg + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/5", "2/5", "0/5", "2/5"], ["1/4", "3/8", "0/4", "3/8"]], + ZERO, + True, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[0.2, 0.4, 0, 0.4], [1 / 4, 3 / 8, 0, 3 / 8]], + TOL, + False, + ), + ( + gbt.Game.from_arrays([[1, 2], [-3, 4]], [[-4, 3], [2, 1]]), + [[1 / 2, 1 / 2], [3 / 5, 2 / 5]], + TOL, + False, + ), + ################################################################################# + # Zero matrix nfg + ( + games.read_from_file("2x2_bimatrix_all_zero_payoffs.nfg"), + [["4/5", "1/5"], ["4/7", "3/7"]], + ZERO, + True, + ), + ################################################################################# + # Centipede game with chance + ( + games.read_from_file("cent3.efg"), + [["1/5", "2/5", "1/5", "1/5"], ["1/10", "3/5", "3/10", "0/1"]], + ZERO, + True, + ), + ( + games.read_from_file("cent3.efg"), + [[1 / 3, 1 / 3, 1 / 3, 0 / 1], [1 / 10, 3 / 5, 3 / 10, 0 / 1]], + TOL, + False, + ), + ################################################################################# + # Selten's horse + ( + games.read_from_file("e01.efg"), + [["4/9", "5/9"], ["6/11", "5/11"], ["4/7", "3/7"]], + ZERO, + True, + ), + ( + games.read_from_file("e01.efg"), + [[4 / 9, 5 / 9], [6 / 11, 5 / 11], [4 / 7, 3 / 7]], + TOL, + False, + ), + ################################################################################# + # El Farol bar game + ( + games.read_from_file("el_farol_bar.efg"), + [["4/9", "5/9"], ["1/3", "2/3"], ["0/1", "1/1"], ["11/12", "1/12"], ["1/3", "2/3"]], + ZERO, + True, + ), + ], ) -def test_payoff_and_strategy_value_consistency(game: gbt.Game, profile_data: list, - tol: float | gbt.Rational, - rational_flag: bool): +def test_payoff_and_strategy_value_consistency( + game: gbt.Game, profile_data: list, tol: float | gbt.Rational, rational_flag: bool +): profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) for player in game.players: assert ( - abs(sum([profile[player][strategy] * profile.strategy_value(strategy) - for strategy in player.strategies]) - profile.payoff(player)) <= tol + abs( + sum( + [ + profile[player][strategy] * profile.strategy_value(strategy) + for strategy in player.strategies + ] + ) + - profile.payoff(player) + ) + <= tol ) @pytest.mark.parametrize( "game,profile1,profile2,alpha,rational_flag,tol", [ - ################################################################################# - # 4x4 coordination nfg - (games.create_coord_4x4_nfg(), - [["1/1111", "10/1111", "100/1111", "1000/1111"], ["1/4", "1/8", "3/8", "1/4"]], - [["1/1111", "10/1111", "99/1111", "1001/1111"], ["1/4", "1/8", "3/8", "1/4"]], "1/2", True, - ZERO), - (games.create_coord_4x4_nfg(), - [[1/1111, 10/1111, 100/1111, 1000/1111], [1/4, 1/8, 3/8, 1/4]], - [[1/1111, 10/1111, 99/1111, 1001/1111], [1/4, 1/8, 3/8, 1/4]], 1/2, False, TOL), - ################################################################################# - # centipede game with chance - (games.create_centipede_game_with_chance_efg(), - [["1/3", "1/3", "1/3", "0"], ["1/10", "3/5", "3/10", "0"]], - [["1/3", "1/3", "1/3", "0"], ["1/10", "3/5", "3/10", "0"]], "82943/62500", True, ZERO), - (games.create_centipede_game_with_chance_efg(), - [[1/3, 1/3, 1/3, 0], [1/10, 3/5, 3/10, 0]], - [[1/3, 1/3, 1/3, 0], [1/10, 3/5, 3/10, 0]], 82943/62500, False, TOL), - ] + ################################################################################# + # 4x4 coordination nfg + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [["1/1111", "10/1111", "100/1111", "1000/1111"], ["1/4", "1/8", "3/8", "1/4"]], + [["1/1111", "10/1111", "99/1111", "1001/1111"], ["1/4", "1/8", "3/8", "1/4"]], + "1/2", + True, + ZERO, + ), + ( + games.read_from_file("coordination_4x4_payoff.nfg"), + [[1 / 1111, 10 / 1111, 100 / 1111, 1000 / 1111], [1 / 4, 1 / 8, 3 / 8, 1 / 4]], + [[1 / 1111, 10 / 1111, 99 / 1111, 1001 / 1111], [1 / 4, 1 / 8, 3 / 8, 1 / 4]], + 1 / 2, + False, + TOL, + ), + ################################################################################# + # centipede game with chance + ( + games.read_from_file("cent3.efg"), + [["1/3", "1/3", "1/3", "0"], ["1/10", "3/5", "3/10", "0"]], + [["1/3", "1/3", "1/3", "0"], ["1/10", "3/5", "3/10", "0"]], + "82943/62500", + True, + ZERO, + ), + ( + games.read_from_file("cent3.efg"), + [[1 / 3, 1 / 3, 1 / 3, 0], [1 / 10, 3 / 5, 3 / 10, 0]], + [[1 / 3, 1 / 3, 1 / 3, 0], [1 / 10, 3 / 5, 3 / 10, 0]], + 82943 / 62500, + False, + TOL, + ), + ], ) -def test_property_linearity_strategy_value(game: gbt.Game, profile1: list, profile2: list, - alpha: float | str, rational_flag: bool, - tol: float | gbt.Rational): - +def test_property_linearity_strategy_value( + game: gbt.Game, + profile1: list, + profile2: list, + alpha: float | str, + rational_flag: bool, + tol: float | gbt.Rational, +): alpha = gbt.Rational(alpha) if rational_flag else alpha profile1 = game.mixed_strategy_profile(rational=rational_flag, data=profile1) profile2 = game.mixed_strategy_profile(rational=rational_flag, data=profile2) - profile_data = [[alpha*profile1[strategy] + (1-alpha)*profile2[strategy] - for strategy in player.strategies] for player in game.players] + profile_data = [ + [ + alpha * profile1[strategy] + (1 - alpha) * profile2[strategy] + for strategy in player.strategies + ] + for player in game.players + ] profile3 = game.mixed_strategy_profile(rational=rational_flag, data=profile_data) for player in game.players: for strategy in player.strategies: - convex_comb = alpha * profile1.strategy_value(strategy) +\ - (1-alpha) * profile2.strategy_value(strategy) + convex_comb = alpha * profile1.strategy_value(strategy) + ( + 1 - alpha + ) * profile2.strategy_value(strategy) assert abs(profile3.strategy_value(strategy) - convex_comb) <= tol -def _get_answers_one_order(game: gbt.Game, action_probs_1st: tuple, action_probs_2nd: tuple, - rational_flag: bool, func_to_test: typing.Callable, - object_to_test_on: typing.Any): +def _get_answers_one_order( + game: gbt.Game, + action_probs_1st: tuple, + action_probs_2nd: tuple, + rational_flag: bool, + func_to_test: typing.Callable, + object_to_test_on: typing.Any, +): """helper function for the 'profile_order' caching tests""" ret = dict() profile = game.mixed_strategy_profile(rational=rational_flag) @@ -894,14 +1588,27 @@ def _get_answers_one_order(game: gbt.Game, action_probs_1st: tuple, action_probs return ret -def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: tuple, - rational_flag: bool, func_to_test: typing.Callable, - objects_to_test_on: typing.Collection): +def _get_and_check_answers( + game: gbt.Game, + action_probs1: tuple, + action_probs2: tuple, + rational_flag: bool, + func_to_test: typing.Callable, + objects_to_test_on: typing.Collection, +): """helper function for the 'profile_order' caching tests""" - order1_answers = {o: _get_answers_one_order(game, action_probs1, action_probs2, rational_flag, - func_to_test, o) for o in objects_to_test_on} - order2_answers = {o: _get_answers_one_order(game, action_probs2, action_probs1, rational_flag, - func_to_test, o) for o in objects_to_test_on} + order1_answers = { + o: _get_answers_one_order( + game, action_probs1, action_probs2, rational_flag, func_to_test, o + ) + for o in objects_to_test_on + } + order2_answers = { + o: _get_answers_one_order( + game, action_probs2, action_probs1, rational_flag, func_to_test, o + ) + for o in objects_to_test_on + } assert order1_answers == order2_answers @@ -920,163 +1627,383 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2: @pytest.mark.parametrize( "game,action_probs1,action_probs2,rational_flag,func_to_test,objects_to_test", [ - ################################################################################# - # payoffs (for players) - ####################### - # 4x4 coordination nfg - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda profile, player: profile.payoff(player), lambda game: game.players, - id="payoffs_coord_doub"), - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda profile, player: profile.payoff(player), lambda game: game.players, - id="payoffs_coord_rat"), - # 2x2x2 nfg - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, player: profile.payoff(player), lambda game: game.players, - id="payoffs_2x2x2_doub"), - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, player: profile.payoff(player), lambda game: game.players, - id="payoffs_2x2x2_rat"), - # stripped-down poker - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, player: profile.payoff(player), lambda game: game.players, - id="payoffs_poker_doub"), - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, player: profile.payoff(player), lambda game: game.players, - id="payoffs_poker_rat"), - ################################################################################# - # regret (for strategies) - # 4x4 coordination nfg - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda profile, strategy: profile.strategy_regret(strategy), - lambda game: game.strategies, id="regret_coord_doub"), - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda profile, strategy: profile.strategy_regret(strategy), - lambda game: game.strategies, id="regret_coord_rat"), - # 2x2x2 nfg - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, strategy: profile.strategy_regret(strategy), - lambda game: game.strategies, id="regret_2x2x2_doub"), - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, strategy: profile.strategy_regret(strategy), - lambda game: game.strategies, id="regret_2x2x2_rat"), - # stripped-down poker - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, strategy: profile.strategy_regret(strategy), - lambda game: game.strategies, id="regret_poker_doub"), - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, strategy: profile.strategy_regret(strategy), - lambda game: game.strategies, id="regret_poker_rat"), - ################################################################################# - # strategy_value (for strategies) - # 4x4 coordination nfg - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda profile, strategy: profile.strategy_value(strategy), - lambda game: game.strategies, id="strat_value_coord_doub"), - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda profile, strategy: profile.strategy_value(strategy), - lambda game: game.strategies, id="strat_value_coord_rat"), - # 2x2x2 nfg - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, strategy: profile.strategy_value(strategy), - lambda game: game.strategies, id="strat_value_2x2x2_doub"), - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, strategy: profile.strategy_value(strategy), - lambda game: game.strategies, id="strat_value_2x2x2_rat"), - # stripped-down poker - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, strategy: profile.strategy_value(strategy), - lambda game: game.strategies, id="strat_value_poker_doub"), - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, strategy: profile.strategy_value(strategy), - lambda game: game.strategies, id="strat_value_poker_rat"), - ################################################################################# - # strategy_value_deriv (for strategies * strategies) - # 4x4 coordination nfg - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda profile, strat_pair: profile.strategy_value_deriv(strategy=strat_pair[0], - other=strat_pair[1]), - lambda game: list(product(game.strategies, game.strategies)), - id="strat_value_deriv_coord_doub"), - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda profile, strat_pair: profile.strategy_value_deriv(strategy=strat_pair[0], - other=strat_pair[1]), - lambda game: list(product(game.strategies, game.strategies)), - id="strat_value_deriv_coord_rat"), - # 2x2x2 nfg - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, strat_pair: profile.strategy_value_deriv(strategy=strat_pair[0], - other=strat_pair[1]), - lambda game: list(product(game.strategies, game.strategies)), - id="strat_value_deriv_2x2x2_doub"), - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, strat_pair: profile.strategy_value_deriv(strategy=strat_pair[0], - other=strat_pair[1]), - lambda game: list(product(game.strategies, game.strategies)), - id="strat_value_deriv_2x2x2_rat"), - # stripped-down poker - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, strat_pair: profile.strategy_value_deriv(strategy=strat_pair[0], - other=strat_pair[1]), - lambda game: list(product(game.strategies, game.strategies)), - id="strat_value_deriv_poker_doub"), - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, strat_pair: profile.strategy_value_deriv(strategy=strat_pair[0], - other=strat_pair[1]), - lambda game: list(product(game.strategies, game.strategies)), - id="strat_value_deriv_poker_rat"), - ################################################################################# - # liap_value (of profile, hence [1] for objects_to_test, any singleton collection would do) - # 4x4 coordination nfg - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda profile, y: profile.liap_value(), lambda x: [1], - id="liap_value_coord_doub"), - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda profile, y: profile.liap_value(), lambda x: [1], - id="liap_value_coord_rat"), - # 2x2x2 nfg - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, y: profile.liap_value(), lambda x: [1], - id="liap_value_2x2x2_doub"), - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, y: profile.liap_value(), lambda x: [1], - id="liap_value_2x2x2_rat"), - # stripped-down poker - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, y: profile.liap_value(), lambda x: [1], - id="liap_value_poker_doub"), - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, y: profile.liap_value(), lambda x: [1], - id="liap_value_poker_rat"), - ################################################################################# - # max_regret (of profile, hence [1] for objects_to_test, any singleton collection would do) - # 4x4 coordination nfg - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_doub, PROBS_2A_doub, False, - lambda profile, y: profile.max_regret(), lambda x: [1], - id="max_regret_coord_doub"), - pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_rat, PROBS_2A_rat, True, - lambda profile, y: profile.max_regret(), lambda x: [1], - id="max_regret_coord_rat"), - # 2x2x2 nfg - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, y: profile.max_regret(), lambda x: [1], - id="max_regret_2x2x2_doub"), - pytest.param(games.create_2x2x2_nfg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, y: profile.max_regret(), lambda x: [1], - id="max_regret_2x2x2_rat"), - # stripped-down poker - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False, - lambda profile, y: profile.max_regret(), lambda x: [1], - id="max_regret_poker_doub"), - pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True, - lambda profile, y: profile.max_regret(), lambda x: [1], - id="max_regret_poker_rat"), - ] + ################################################################################# + # payoffs (for players) + ####################### + # 4x4 coordination nfg + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda profile, player: profile.payoff(player), + lambda game: game.players, + id="payoffs_coord_doub", + ), + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda profile, player: profile.payoff(player), + lambda game: game.players, + id="payoffs_coord_rat", + ), + # 2x2x2 nfg + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, player: profile.payoff(player), + lambda game: game.players, + id="payoffs_2x2x2_doub", + ), + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, player: profile.payoff(player), + lambda game: game.players, + id="payoffs_2x2x2_rat", + ), + # stripped-down poker + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, player: profile.payoff(player), + lambda game: game.players, + id="payoffs_poker_doub", + ), + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, player: profile.payoff(player), + lambda game: game.players, + id="payoffs_poker_rat", + ), + ################################################################################# + # regret (for strategies) + # 4x4 coordination nfg + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda profile, strategy: profile.strategy_regret(strategy), + lambda game: game.strategies, + id="regret_coord_doub", + ), + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda profile, strategy: profile.strategy_regret(strategy), + lambda game: game.strategies, + id="regret_coord_rat", + ), + # 2x2x2 nfg + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, strategy: profile.strategy_regret(strategy), + lambda game: game.strategies, + id="regret_2x2x2_doub", + ), + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, strategy: profile.strategy_regret(strategy), + lambda game: game.strategies, + id="regret_2x2x2_rat", + ), + # stripped-down poker + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, strategy: profile.strategy_regret(strategy), + lambda game: game.strategies, + id="regret_poker_doub", + ), + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, strategy: profile.strategy_regret(strategy), + lambda game: game.strategies, + id="regret_poker_rat", + ), + ################################################################################# + # strategy_value (for strategies) + # 4x4 coordination nfg + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda profile, strategy: profile.strategy_value(strategy), + lambda game: game.strategies, + id="strat_value_coord_doub", + ), + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda profile, strategy: profile.strategy_value(strategy), + lambda game: game.strategies, + id="strat_value_coord_rat", + ), + # 2x2x2 nfg + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, strategy: profile.strategy_value(strategy), + lambda game: game.strategies, + id="strat_value_2x2x2_doub", + ), + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, strategy: profile.strategy_value(strategy), + lambda game: game.strategies, + id="strat_value_2x2x2_rat", + ), + # stripped-down poker + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, strategy: profile.strategy_value(strategy), + lambda game: game.strategies, + id="strat_value_poker_doub", + ), + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, strategy: profile.strategy_value(strategy), + lambda game: game.strategies, + id="strat_value_poker_rat", + ), + ################################################################################# + # strategy_value_deriv (for strategies * strategies) + # 4x4 coordination nfg + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda profile, strat_pair: profile.strategy_value_deriv( + strategy=strat_pair[0], other=strat_pair[1] + ), + lambda game: list(product(game.strategies, game.strategies)), + id="strat_value_deriv_coord_doub", + ), + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda profile, strat_pair: profile.strategy_value_deriv( + strategy=strat_pair[0], other=strat_pair[1] + ), + lambda game: list(product(game.strategies, game.strategies)), + id="strat_value_deriv_coord_rat", + ), + # 2x2x2 nfg + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, strat_pair: profile.strategy_value_deriv( + strategy=strat_pair[0], other=strat_pair[1] + ), + lambda game: list(product(game.strategies, game.strategies)), + id="strat_value_deriv_2x2x2_doub", + ), + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, strat_pair: profile.strategy_value_deriv( + strategy=strat_pair[0], other=strat_pair[1] + ), + lambda game: list(product(game.strategies, game.strategies)), + id="strat_value_deriv_2x2x2_rat", + ), + # stripped-down poker + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, strat_pair: profile.strategy_value_deriv( + strategy=strat_pair[0], other=strat_pair[1] + ), + lambda game: list(product(game.strategies, game.strategies)), + id="strat_value_deriv_poker_doub", + ), + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, strat_pair: profile.strategy_value_deriv( + strategy=strat_pair[0], other=strat_pair[1] + ), + lambda game: list(product(game.strategies, game.strategies)), + id="strat_value_deriv_poker_rat", + ), + ################################################################################# + # liap_value (of profile, hence [1] for objects_to_test, any singleton collection would do) + # 4x4 coordination nfg + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda profile, y: profile.liap_value(), + lambda x: [1], + id="liap_value_coord_doub", + ), + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda profile, y: profile.liap_value(), + lambda x: [1], + id="liap_value_coord_rat", + ), + # 2x2x2 nfg + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, y: profile.liap_value(), + lambda x: [1], + id="liap_value_2x2x2_doub", + ), + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, y: profile.liap_value(), + lambda x: [1], + id="liap_value_2x2x2_rat", + ), + # stripped-down poker + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, y: profile.liap_value(), + lambda x: [1], + id="liap_value_poker_doub", + ), + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, y: profile.liap_value(), + lambda x: [1], + id="liap_value_poker_rat", + ), + ################################################################################# + # max_regret (of profile, hence [1] for objects_to_test, any singleton collection would do) + # 4x4 coordination nfg + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_doub, + PROBS_2A_doub, + False, + lambda profile, y: profile.max_regret(), + lambda x: [1], + id="max_regret_coord_doub", + ), + pytest.param( + games.read_from_file("coordination_4x4_payoff.nfg"), + PROBS_1A_rat, + PROBS_2A_rat, + True, + lambda profile, y: profile.max_regret(), + lambda x: [1], + id="max_regret_coord_rat", + ), + # 2x2x2 nfg + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, y: profile.max_regret(), + lambda x: [1], + id="max_regret_2x2x2_doub", + ), + pytest.param( + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, y: profile.max_regret(), + lambda x: [1], + id="max_regret_2x2x2_rat", + ), + # stripped-down poker + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_doub, + PROBS_2B_doub, + False, + lambda profile, y: profile.max_regret(), + lambda x: [1], + id="max_regret_poker_doub", + ), + pytest.param( + games.create_stripped_down_poker_efg(), + PROBS_1B_rat, + PROBS_2B_rat, + True, + lambda profile, y: profile.max_regret(), + lambda x: [1], + id="max_regret_poker_rat", + ), + ], ) -def test_profile_order_consistency(game: gbt.Game, - action_probs1: tuple, - action_probs2: tuple, rational_flag: bool, - func_to_test: typing.Callable, - objects_to_test: typing.Callable): - _get_and_check_answers(game, action_probs1, action_probs2, rational_flag, func_to_test, - objects_to_test(game)) +def test_profile_order_consistency( + game: gbt.Game, + action_probs1: tuple, + action_probs2: tuple, + rational_flag: bool, + func_to_test: typing.Callable, + objects_to_test: typing.Callable, +): + _get_and_check_answers( + game, action_probs1, action_probs2, rational_flag, func_to_test, objects_to_test(game) + ) diff --git a/tests/test_nash.py b/tests/test_nash.py index f70ac4f2d..f47801d61 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -23,7 +23,7 @@ [ # Zero-sum games ( - games.create_two_player_perfect_info_win_lose_efg(), + games.read_from_file("two_player_perfect_info_win_lose.efg"), [ [[0, 0, 1, 0], [1, 0, 0]], [[0, 0, 1, 0], [0, 1, 0]], @@ -44,7 +44,7 @@ (games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), []), # 3-player game ( - games.create_mixed_behav_game_efg(), + games.read_from_file("mixed_behavior_game.efg"), [ [[1, 0], [1, 0], [1, 0]], [[0, 1], [0, 1], [1, 0]], @@ -78,7 +78,7 @@ def test_enumpure_strategy(game: gbt.Game, pure_strategy_prof_data: list): ############################################################# # Zero-sum games ( - games.create_two_player_perfect_info_win_lose_efg(), + games.read_from_file("two_player_perfect_info_win_lose.efg"), [ [[[1, 0], [1, 0]], [[0, 1], [1, 0]]], [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], @@ -101,7 +101,7 @@ def test_enumpure_strategy(game: gbt.Game, pure_strategy_prof_data: list): (games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), []), # 3-player game ( - games.create_mixed_behav_game_efg(), + games.read_from_file("mixed_behavior_game.efg"), [ [[[1, 0]], [[1, 0]], [[1, 0]]], [[[1, 0]], [[0, 1]], [[0, 1]]], @@ -239,7 +239,7 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): ), # 3-player game # ( - # games.create_mixed_behav_game_efg(), + # games.read_from_file("mixed_behavior_game.efg"), # [ # [[["1/2", "1/2"]], [["2/5", "3/5"]], [["1/4", "3/4"]]], # [[["2/5", "3/5"]], [["1/2", "1/2"]], [["1/3", "2/3"]]], @@ -249,7 +249,7 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): ############################################################################## ############################################################################## ( - games.create_3_player_with_internal_outcomes_efg(), + games.read_from_file("3_player.efg"), [ [[[1, 0], [1, 0]], [[1, 0], ["1/2", "1/2"]], [[1, 0], [0, 1]]], [[[1, 0], [1, 0]], [[1, 0], [0, 1]], @@ -257,7 +257,7 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): 2, ), ( - games.create_3_player_with_internal_outcomes_efg(nonterm_outcomes=True), + games.read_from_file("3_player_with_nonterm_outcomes.efg"), [ [[[1, 0], [1, 0]], [[1, 0], ["1/2", "1/2"]], [[1, 0], [0, 1]]], [[[1, 0], [1, 0]], [[1, 0], [0, 1]], @@ -267,26 +267,26 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): ############################################################################## ############################################################################## ( - games.create_non_zero_sum_lacking_outcome_efg(), + games.read_from_file("2_player_non_zero_sum.efg"), [[[["1/3", "2/3"]], [["1/2", "1/2"]]]], 1, ), ( - games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True), + games.read_from_file("2_player_non_zero_sum_missing_term_outcome.efg"), [[[["1/3", "2/3"]], [["1/2", "1/2"]]]], 1, ), ############################################################################## ############################################################################## ( - games.create_chance_in_middle_efg(), + games.read_from_file("chance_in_middle.efg"), [[[["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]]], ], # [[[1, 0], [1, 0], [1, 0], [0, 0], [0, 0]], [[0, 1], [1, 0]]], # [[[0, 1], [0, 0], [0, 0], [1, 0], [1, 0]], [[1, 0], [0, 1]]], 1, # subsequent eqs have undefined infosets; include after #issue 660 ), ( - games.create_chance_in_middle_efg(nonterm_outcomes=True), + games.read_from_file("chance_in_middle_with_nonterm_outcomes.efg"), [[[["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]]], ], # [[[1, 0], [1, 0], [1, 0], [0, 0], [0, 0]], [[0, 1], [1, 0]]], # [[[0, 1], [0, 0], [0, 0], [1, 0], [1, 0]], [[1, 0], [0, 1]]], @@ -335,7 +335,7 @@ def test_enumpoly_ordered_behavior( [ # 3-player game ( - games.create_mixed_behav_game_efg(), + games.read_from_file("mixed_behavior_game.efg"), [ [[["2/5", "3/5"]], [["1/2", "1/2"]], [["1/3", "2/3"]]], [[["1/2", "1/2"]], [["2/5", "3/5"]], [["1/4", "3/4"]]], @@ -553,33 +553,33 @@ def test_lcp_behavior_double(): # In the next test case: # 1/2-1/2 for l/r is determined by MixedBehaviorProfile.UndefinedToCentroid() ( - games.create_perfect_info_with_chance_efg(), + games.read_from_file("perfect_info_with_chance.efg"), [[[0, 1]], [[0, 1], [0, 1]]], ), ( - games.create_two_player_perfect_info_win_lose_efg(), + games.read_from_file("two_player_perfect_info_win_lose.efg"), [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], ), ( - games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True), + games.read_from_file("two_player_perfect_info_win_lose_with_nonterm_outcomes.efg"), [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], ), ( - games.create_three_action_internal_outcomes_efg(), + games.read_from_file("2_player_chance.efg"), [ [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], ] ), ( - games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), + games.read_from_file("2_player_chance_nonterm_outcomes_and_missing_term_outcomes.efg"), [ [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], ], ), ( - games.create_large_payoff_game_efg(), + games.read_from_file("large_payoff_game.efg"), [ [[1, 0], [1, 0]], [[0, 1], ["9999999999999999999/10000000000000000000", @@ -587,14 +587,14 @@ def test_lcp_behavior_double(): ], ), ( - games.create_chance_in_middle_efg(), + games.read_from_file("chance_in_middle.efg"), [ [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]] ] ), ( - games.create_chance_in_middle_efg(nonterm_outcomes=True), + games.read_from_file("chance_in_middle_with_nonterm_outcomes.efg"), [ [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]] @@ -602,7 +602,7 @@ def test_lcp_behavior_double(): ), # Non-zero-sum games ( - games.create_reduction_both_players_payoff_ties_efg(), + games.read_from_file("reduction_both_players_payoff_ties_GTE_survey.efg"), [[[0, 0, 1, 0], [1, 0]], [[0, 1], [0, 1], [0, 1], [0, 1]]], ), ( @@ -618,19 +618,19 @@ def test_lcp_behavior_double(): [[[0, 0, 0, 1]], [[0, 0, 0, 1]]], ), ( - games.create_entry_accomodation_efg(), + games.read_from_file("entry_accommodation.efg"), [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]] ), ( - games.create_entry_accomodation_efg(nonterm_outcomes=True), + games.read_from_file("entry_accommodation_with_nonterm_outcomes.efg"), [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]], ), ( - games.create_non_zero_sum_lacking_outcome_efg(), + games.read_from_file("2_player_non_zero_sum.efg"), [[["1/3", "2/3"]], [["1/2", "1/2"]]] ), ( - games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True), + games.read_from_file("2_player_non_zero_sum_missing_term_outcome.efg"), [[["1/3", "2/3"]], [["1/2", "1/2"]]], ), ], @@ -707,30 +707,31 @@ def test_lp_behavior_double(): "game,mixed_behav_prof_data", [ ( - games.create_two_player_perfect_info_win_lose_efg(), - [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], + games.read_from_file("two_player_perfect_info_win_lose.efg"), + [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], ), ( - games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True), - [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], + games.read_from_file("two_player_perfect_info_win_lose_with_nonterm_outcomes.efg"), + [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], ), ( - games.create_2x2_zero_sum_efg(missing_term_outcome=False), - [[["1/2", "1/2"]], [["1/2", "1/2"]]] + games.create_2x2_zero_sum_efg(missing_term_outcome=False), + [[["1/2", "1/2"]], [["1/2", "1/2"]]] ), ( games.create_2x2_zero_sum_efg(missing_term_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], ), - (games.create_matching_pennies_efg(with_neutral_outcome=False), - [[["1/2", "1/2"]], [["1/2", "1/2"]]]), + ( + games.create_matching_pennies_efg(with_neutral_outcome=False), + [[["1/2", "1/2"]], [["1/2", "1/2"]]]), ( games.create_matching_pennies_efg(with_neutral_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], ), ( - games.create_stripped_down_poker_efg(), - [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], + games.create_stripped_down_poker_efg(), + [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], ), ( games.create_stripped_down_poker_efg(nonterm_outcomes=True), @@ -758,32 +759,32 @@ def test_lp_behavior_double(): ], ), ( - games.create_seq_form_STOC_paper_zero_sum_2_player_efg(), + games.read_from_file("zerosum_efg_from_sequence_form_STOC94_paper.efg"), [ [[0, 1], ["2/3", "1/3"], ["1/3", "2/3"]], [["5/6", "1/6"], ["5/9", "4/9"]], ], ), ( - games.create_perfect_info_with_chance_efg(), + games.read_from_file("perfect_info_with_chance.efg"), [[[0, 1]], [[1, 0], [1, 0]]], ), ( - games.create_three_action_internal_outcomes_efg(), + games.read_from_file("2_player_chance.efg"), [ [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], ] ), ( - games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), + games.read_from_file("2_player_chance_nonterm_outcomes_and_missing_term_outcomes.efg"), [ [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], ], ), ( - games.create_large_payoff_game_efg(), + games.read_from_file("large_payoff_game.efg"), [ [[1, 0], [1, 0]], [[0, 1], ["9999999999999999999/10000000000000000000", @@ -791,14 +792,14 @@ def test_lp_behavior_double(): ], ), ( - games.create_chance_in_middle_efg(), + games.read_from_file("chance_in_middle.efg"), [ [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]] ], ), ( - games.create_chance_in_middle_efg(nonterm_outcomes=True), + games.read_from_file("chance_in_middle_with_nonterm_outcomes.efg"), [ [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]] diff --git a/tests/test_players.py b/tests/test_players.py index dd0257f0a..339ae1de2 100644 --- a/tests/test_players.py +++ b/tests/test_players.py @@ -152,7 +152,7 @@ def test_player_strategy_bad_type(): [ # NFGs ( - games.read_from_file("2x2x2_nfg_with_two_pure_one_mixed_eq.nfg"), + games.read_from_file("2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg"), [-1, 0, -1], [2, 4, 2] ),