diff --git a/tests/games.py b/tests/games.py index e34591d4d..3325a4940 100644 --- a/tests/games.py +++ b/tests/games.py @@ -19,7 +19,7 @@ def read_from_file(fn: str) -> gbt.Game: def create_efg_corresponding_to_bimatrix_game( - A: np.ndarray, B: np.ndarray, title: str + A: np.ndarray, B: np.ndarray, title: str ) -> gbt.Game: """ There is no direct pygambit method to create an EFG from a stategic-form game. @@ -123,6 +123,238 @@ def create_2x2_zero_sum_efg(missing_term_outcome: bool = False) -> gbt.Game: 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) + return g + + 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. @@ -211,8 +443,6 @@ def create_stripped_down_poker_efg(nonterm_outcomes: bool = False) -> gbt.Game: g.set_outcome(bob_calls_and_loses_node, bob_calls_and_loses_outcome) bob_calls_and_wins_node = g.root.children["Queen"].children["Bet"].children["Call"] g.set_outcome(bob_calls_and_wins_node, bob_calls_and_wins_outcome) - # g.to_efg("stripped_down_poker_nonterminal_outcomes.efg") - return g @@ -701,21 +931,33 @@ def create_seq_form_STOC_paper_zero_sum_2_player_efg() -> gbt.Game: return g -def create_two_player_perfect_info_win_lose_efg() -> gbt.Game: +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"]) - 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")) + 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 @@ -990,14 +1232,14 @@ def _redu_strats(self, player, level): first_half = tmp[:n_half] second_half = tmp[n_half:] n_stars = ( - self.get_n_infosets(level)[1] - self.get_n_infosets(level - 1)[1] - 1 + self.get_n_infosets(level)[1] - self.get_n_infosets(level - 1)[1] - 1 ) stars = "*" * n_stars return ( - ["11" + t + stars for t in first_half] - + ["12" + t + stars for t in second_half] - + ["21" + stars + t for t in first_half] - + ["22" + stars + t for t in second_half] + ["11" + t + stars for t in first_half] + + ["12" + t + stars for t in second_half] + + ["21" + stars + t for t in first_half] + + ["22" + stars + t for t in second_half] ) diff --git a/tests/test_extensive.py b/tests/test_extensive.py index 7b8470285..75ad60993 100644 --- a/tests/test_extensive.py +++ b/tests/test_extensive.py @@ -409,3 +409,63 @@ def test_reduced_strategic_form( # convert strings to rationals exp_array = games.vectorized_make_rational(np_arrays_of_rsf[i]) assert (arrays[i] == exp_array).all() + + +@pytest.mark.parametrize( + "standard,modified", + [ + ( + games.create_two_player_perfect_info_win_lose_efg(), + games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True) + ), + ( + games.create_3_player_with_internal_outcomes_efg(), + games.create_3_player_with_internal_outcomes_efg(nonterm_outcomes=True) + ), + ( + games.create_chance_in_middle_efg(), + games.create_chance_in_middle_efg(nonterm_outcomes=True) + ), + ( + games.create_non_zero_sum_lacking_outcome_efg(), + games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True) + ), + ( + games.create_entry_accomodation_efg(), + games.create_entry_accomodation_efg(nonterm_outcomes=True) + ), + ( + games.create_three_action_internal_outcomes_efg(), + games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True) + ), + ( + games.create_kuhn_poker_efg(), + games.create_kuhn_poker_efg(nonterm_outcomes=True) + ), + ( + games.create_stripped_down_poker_efg(), + games.create_stripped_down_poker_efg(nonterm_outcomes=True) + ), + ( + games.create_2x2_zero_sum_efg(), + games.create_2x2_zero_sum_efg(missing_term_outcome=True) + ), + ( + games.create_matching_pennies_efg(), + games.create_matching_pennies_efg(with_neutral_outcome=True) + ), + ], +) +def test_reduced_strategy_form_nonterminal_outcomes_consistency(standard: gbt.Game, + modified: gbt.Game): + """ + standard: game uses only non-terminal outcomes, with all non-terminal nodes having outcomes + modified: is payoff equivalent, but with non-terminal outcomes or missing terminal outcomes + + The test checks that the corresponding reduced strategic forms match. + """ + arrays_s = standard.to_arrays() + arrays_m = modified.to_arrays() + assert (len(arrays_s) == len(arrays_m)) + for array_s, array_m in zip(arrays_s, arrays_m, strict=True): + assert (array_s == array_m).all() diff --git a/tests/test_nash.py b/tests/test_nash.py index b436b79a9..1f3e07281 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -48,23 +48,23 @@ def test_enummixed_double(): (games.create_one_shot_trust_efg(), [[[0, 1], ["1/2", "1/2"]], [[0, 1], [0, 1]]]), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(3), - [ - [[1, 0, 0], [1, 0, 0]], - [["1/2", "1/2", 0], ["1/2", "1/2", 0]], - [["1/3", "1/3", "1/3"], ["1/3", "1/3", "1/3"]], - [["1/2", 0, "1/2"], ["1/2", 0, "1/2"]], - [[0, 1, 0], [0, 1, 0]], - [[0, "1/2", "1/2"], [0, "1/2", "1/2"]], - [[0, 0, 1], [0, 0, 1]], - ], + games.create_EFG_for_nxn_bimatrix_coordination_game(3), + [ + [[1, 0, 0], [1, 0, 0]], + [["1/2", "1/2", 0], ["1/2", "1/2", 0]], + [["1/3", "1/3", "1/3"], ["1/3", "1/3", "1/3"]], + [["1/2", 0, "1/2"], ["1/2", 0, "1/2"]], + [[0, 1, 0], [0, 1, 0]], + [[0, "1/2", "1/2"], [0, "1/2", "1/2"]], + [[0, 0, 1], [0, 0, 1]], + ], ), ( - games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), - [ - [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"], - ["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], - ], + games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), + [ + [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"], + ["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], + ], ), ] ) @@ -89,9 +89,9 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): [ # 2-player zero-sum games ( - games.create_stripped_down_poker_efg(), - [[[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]]], - None, + games.create_stripped_down_poker_efg(), + [[[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]]], + None, ), # 2-player non-zero-sum games pytest.param( @@ -109,22 +109,22 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): marks=pytest.mark.xfail(reason="Problem with enumpoly, as per issue #660") ), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(3), - [ - [[["1/3", "1/3", "1/3"]], [["1/3", "1/3", "1/3"]]], - [[["1/2", "1/2", 0]], [["1/2", "1/2", 0]]], - [[["1/2", 0, "1/2"]], [["1/2", 0, "1/2"]]], - [[[1, 0, 0]], [[1, 0, 0]]], - [[[0, "1/2", "1/2"]], [[0, "1/2", "1/2"]]], - [[[0, 1, 0]], [[0, 1, 0]]], - [[[0, 0, 1]], [[0, 0, 1]]], - ], - None, + games.create_EFG_for_nxn_bimatrix_coordination_game(3), + [ + [[["1/3", "1/3", "1/3"]], [["1/3", "1/3", "1/3"]]], + [[["1/2", "1/2", 0]], [["1/2", "1/2", 0]]], + [[["1/2", 0, "1/2"]], [["1/2", 0, "1/2"]]], + [[[1, 0, 0]], [[1, 0, 0]]], + [[[0, "1/2", "1/2"]], [[0, "1/2", "1/2"]]], + [[[0, 1, 0]], [[0, 1, 0]]], + [[[0, 0, 1]], [[0, 0, 1]]], + ], + None, ), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(4), - [[[["1/4", "1/4", "1/4", "1/4"]], [["1/4", "1/4", "1/4", "1/4"]]]], - 1, + games.create_EFG_for_nxn_bimatrix_coordination_game(4), + [[[["1/4", "1/4", "1/4", "1/4"]], [["1/4", "1/4", "1/4", "1/4"]]]], + 1, ), # 3-player game # ( @@ -135,10 +135,80 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): # ], # 2, # 9 in total found by enumpoly (see unordered test) # ), + ( + games.create_3_player_with_internal_outcomes_efg(), + [ + [[[1, 0], [1, 0]], [[1, 0], ["1/2", "1/2"]], [[0, 1], [1, 0]]], + [[[1, 0], [1, 0]], [[1, 0], [0, 1]], + [["1/3", "2/3"], [1, 0]]]], + 2, + ), + ( + games.create_3_player_with_internal_outcomes_efg(nonterm_outcomes=True), + [ + [[[1, 0], [1, 0]], [[1, 0], ["1/2", "1/2"]], [[0, 1], [1, 0]]], + [[[1, 0], [1, 0]], [[1, 0], [0, 1]], + [["1/3", "2/3"], [1, 0]]]], + 2, + ), + ( + games.create_entry_accomodation_efg(), + [ + [[["2/3", "1/3"], [1, 0], [1, 0]], + [["2/3", "1/3"]]], + [[[0, 1], [0, 0], ["1/3", "2/3"]], + [[0, 1]]], + [[[0, 1], [0, 0], [1, 0]], [[1, 0]]], + [[[0, 1], [0, 0], [0, 0]], [[0, 1]]]], + 4, + ), + ( + games.create_entry_accomodation_efg(nonterm_outcomes=True), + [ + [[["2/3", "1/3"], [1, 0], [1, 0]], + [["2/3", "1/3"]]], + [[[0, 1], [0, 0], ["1/3", "2/3"]], + [[0, 1]]], + [[[0, 1], [0, 0], [1, 0]], [[1, 0]]], + [[[0, 1], [0, 0], [0, 0]], [[0, 1]]]], + 4, + ), + ( + games.create_non_zero_sum_lacking_outcome_efg(), + [[[["1/3", "2/3"]], [["1/2", "1/2"]]]], + 1, + ), + ( + games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True), + [[[["1/3", "2/3"]], [["1/2", "1/2"]]]], + 1, + ), + ( + games.create_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]]]], + 3, + ), + ( + games.create_chance_in_middle_efg(nonterm_outcomes=True), + [[[["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]]]], + 3, + ), ], ) def test_enumpoly_ordered_behavior( - game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int + game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int ): """Test calls of enumpoly for mixed behavior equilibria, using max_regret (internal consistency); and comparison to a set of previously @@ -177,24 +247,24 @@ def test_enumpoly_ordered_behavior( [ # 3-player game ( - games.create_mixed_behav_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"]]], - [[["1/2", "1/2"]], [["1/2", "1/2"]], [[1, 0]]], - [[["1/3", "2/3"]], [[1, 0]], [["1/4", "3/4"]]], - [[[1, 0]], [[1, 0]], [[1, 0]]], - [[[1, 0]], [[0, 1]], [[0, 1]]], - [[[0, 1]], [["1/4", "3/4"]], [["1/3", "2/3"]]], - [[[0, 1]], [[1, 0]], [[0, 1]]], - [[[0, 1]], [[0, 1]], [[1, 0]]], - ], - 9, + games.create_mixed_behav_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"]]], + [[["1/2", "1/2"]], [["1/2", "1/2"]], [[1, 0]]], + [[["1/3", "2/3"]], [[1, 0]], [["1/4", "3/4"]]], + [[[1, 0]], [[1, 0]], [[1, 0]]], + [[[1, 0]], [[0, 1]], [[0, 1]]], + [[[0, 1]], [["1/4", "3/4"]], [["1/3", "2/3"]]], + [[[0, 1]], [[1, 0]], [[0, 1]]], + [[[0, 1]], [[0, 1]], [[1, 0]]], + ], + 9, ), ], ) def test_enumpoly_unordered_behavior( - game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int + game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int ): """Test calls of enumpoly for mixed behavior equilibria, using max_regret (internal consistency); and comparison to a set of previously @@ -255,54 +325,54 @@ def test_lcp_strategy_double(): [ # Zero-sum games ( - games.create_2x2_zero_sum_efg(), - [[["1/2", "1/2"], ["1/2", "1/2"]]], - None + games.create_2x2_zero_sum_efg(), + [[["1/2", "1/2"], ["1/2", "1/2"]]], + None ), ( - games.create_2x2_zero_sum_efg(missing_term_outcome=True), - [[["1/2", "1/2"], ["1/2", "1/2"]]], - None + games.create_2x2_zero_sum_efg(missing_term_outcome=True), + [[["1/2", "1/2"], ["1/2", "1/2"]]], + None ), (games.create_stripped_down_poker_efg(), [[["1/3", "2/3", 0, 0], ["2/3", "1/3"]]], None), ( - games.create_stripped_down_poker_efg(nonterm_outcomes=True), - [[["1/3", "2/3", 0, 0], ["2/3", "1/3"]]], - None + games.create_stripped_down_poker_efg(nonterm_outcomes=True), + [[["1/3", "2/3", 0, 0], ["2/3", "1/3"]]], + None ), (games.create_kuhn_poker_efg(), [games.kuhn_poker_lcp_first_mixed_strategy_prof()], 1), ( - games.create_kuhn_poker_efg(nonterm_outcomes=True), - [games.kuhn_poker_lcp_first_mixed_strategy_prof()], - 1 + games.create_kuhn_poker_efg(nonterm_outcomes=True), + [games.kuhn_poker_lcp_first_mixed_strategy_prof()], + 1 ), # Non-zero-sum games (games.create_one_shot_trust_efg(), [[[0, 1], ["1/2", "1/2"]]], None), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(3), - [ - [[1, 0, 0], [1, 0, 0]], - [["1/2", "1/2", 0], ["1/2", "1/2", 0]], - [[0, 1, 0], [0, 1, 0]], - [[0, "1/2", "1/2"], [0, "1/2", "1/2"]], - [["1/3", "1/3", "1/3"], ["1/3", "1/3", "1/3"]], - [["1/2", 0, "1/2"], ["1/2", 0, "1/2"]], - [[0, 0, 1], [0, 0, 1]], - ], - None, + games.create_EFG_for_nxn_bimatrix_coordination_game(3), + [ + [[1, 0, 0], [1, 0, 0]], + [["1/2", "1/2", 0], ["1/2", "1/2", 0]], + [[0, 1, 0], [0, 1, 0]], + [[0, "1/2", "1/2"], [0, "1/2", "1/2"]], + [["1/3", "1/3", "1/3"], ["1/3", "1/3", "1/3"]], + [["1/2", 0, "1/2"], ["1/2", 0, "1/2"]], + [[0, 0, 1], [0, 0, 1]], + ], + None, ), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(4), - [[[1, 0, 0, 0], [1, 0, 0, 0]]], - 1, + games.create_EFG_for_nxn_bimatrix_coordination_game(4), + [[[1, 0, 0, 0], [1, 0, 0, 0]]], + 1, ), ( - games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), - [ - [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"], - ["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], - ], - None + games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), + [ + [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"], + ["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], + ], + None ), ] ) @@ -345,40 +415,40 @@ def test_lcp_behavior_double(): [ # Zero-sum games (also tested with lp solve) ( - games.create_2x2_zero_sum_efg(), - [[["1/2", "1/2"]], [["1/2", "1/2"]]] + games.create_2x2_zero_sum_efg(), + [[["1/2", "1/2"]], [["1/2", "1/2"]]] ), pytest.param( games.create_2x2_zero_sum_efg(missing_term_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), (games.create_matching_pennies_efg(), - [[["1/2", "1/2"]], [["1/2", "1/2"]]]), + [[["1/2", "1/2"]], [["1/2", "1/2"]]]), pytest.param( games.create_matching_pennies_efg(with_neutral_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with nonterminal nodes in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), (games.create_stripped_down_poker_efg(), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]]), pytest.param( games.create_stripped_down_poker_efg(nonterm_outcomes=True), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( - games.create_kuhn_poker_efg(), - [ + games.create_kuhn_poker_efg(), [ - ["2/3", "1/3"], - [1, 0], - [1, 0], - ["1/3", "2/3"], - [0, 1], - ["1/2", "1/2"], + [ + ["2/3", "1/3"], + [1, 0], + [1, 0], + ["1/3", "2/3"], + [0, 1], + ["1/2", "1/2"], + ], + [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], - [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], - ], ), pytest.param( games.create_kuhn_poker_efg(nonterm_outcomes=True), @@ -393,30 +463,94 @@ def test_lcp_behavior_double(): ], [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), # In the next test case: # 1/2-1/2 for l/r is determined by MixedBehaviorProfile.UndefinedToCentroid() ( - games.create_two_player_perfect_info_win_lose_efg(), + games.create_perfect_info_with_chance_efg(), + [[[0, 1]], [[0, 1], [0, 1]]], + ), + ( + games.create_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), [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], ), - # Non-zero-sum games ( - games.create_reduction_both_players_payoff_ties_efg(), - [[[0, 0, 1, 0], [1, 0]], [[0, 1], [0, 1], [0, 1], [0, 1]]], + games.create_three_action_internal_outcomes_efg(), + [ + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], + ] + ), + pytest.param( + games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), + [ + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], + ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") + ), + ( + games.create_large_payoff_game_efg(), + [ + [[1, 0], [1, 0]], + [[0, 1], ["9999999999999999999/10000000000000000000", + "1/10000000000000000000"]], + ], ), ( - games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), + games.create_chance_in_middle_efg(), [ - [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"]], - [["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] + ] + ), + pytest.param( + games.create_chance_in_middle_efg(nonterm_outcomes=True), + [ + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") + ), + # Non-zero-sum games + ( + games.create_reduction_both_players_payoff_ties_efg(), + [[[0, 0, 1, 0], [1, 0]], [[0, 1], [0, 1], [0, 1], [0, 1]]], + ), + ( + games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), + [ + [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"]], + [["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], + ], ), (games.create_EFG_for_nxn_bimatrix_coordination_game(3), [[[0, 0, 1]], [[0, 0, 1]]]), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(4), - [[[0, 0, 0, 1]], [[0, 0, 0, 1]]], + games.create_EFG_for_nxn_bimatrix_coordination_game(4), + [[[0, 0, 0, 1]], [[0, 0, 0, 1]]], + ), + ( + games.create_entry_accomodation_efg(), + [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]] + ), + pytest.param( + games.create_entry_accomodation_efg(nonterm_outcomes=True), + [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") + ), + ( + games.create_non_zero_sum_lacking_outcome_efg(), + [[["1/3", "2/3"]], [["1/2", "1/2"]]] + ), + pytest.param( + games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True), + [[["1/3", "2/3"]], [["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ], ) @@ -448,22 +582,22 @@ def test_lp_strategy_double(): "game,mixed_strategy_prof_data", [ ( - games.create_2x2_zero_sum_efg(), - [["1/2", "1/2"], ["1/2", "1/2"]], + games.create_2x2_zero_sum_efg(), + [["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_2x2_zero_sum_efg(missing_term_outcome=True), + [["1/2", "1/2"], ["1/2", "1/2"]], ), (games.create_stripped_down_poker_efg(), [["1/3", "2/3", 0, 0], ["2/3", "1/3"]]), ( - games.create_stripped_down_poker_efg(nonterm_outcomes=True), - [["1/3", "2/3", 0, 0], ["2/3", "1/3"]] + games.create_stripped_down_poker_efg(nonterm_outcomes=True), + [["1/3", "2/3", 0, 0], ["2/3", "1/3"]] ), (games.create_kuhn_poker_efg(), games.kuhn_poker_lp_mixed_strategy_prof()), ( - games.create_kuhn_poker_efg(nonterm_outcomes=True), - games.kuhn_poker_lp_mixed_strategy_prof() + games.create_kuhn_poker_efg(nonterm_outcomes=True), + games.kuhn_poker_lp_mixed_strategy_prof() ), ], ) @@ -491,62 +625,109 @@ 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.create_two_player_perfect_info_win_lose_efg(), + [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], + ), + pytest.param( + games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True), + [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( - 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"]]] ), pytest.param( games.create_2x2_zero_sum_efg(missing_term_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), (games.create_matching_pennies_efg(with_neutral_outcome=False), - [[["1/2", "1/2"]], [["1/2", "1/2"]]]), + [[["1/2", "1/2"]], [["1/2", "1/2"]]]), pytest.param( games.create_matching_pennies_efg(with_neutral_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with nonterminal nodes in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( - 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"]]], ), pytest.param( games.create_stripped_down_poker_efg(nonterm_outcomes=True), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], - marks=pytest.mark.xfail(reason="Problem with nonterminal nodes in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( - games.create_kuhn_poker_efg(), - [ - [[1, 0], [1, 0], [1, 0], ["2/3", "1/3"], [1, 0], [0, 1]], - [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], - ], + games.create_kuhn_poker_efg(), + [ + [[1, 0], [1, 0], [1, 0], ["2/3", "1/3"], [1, 0], [0, 1]], + [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], + ], ), pytest.param( games.create_kuhn_poker_efg(nonterm_outcomes=True), [ [ - ["2/3", "1/3"], [1, 0], [1, 0], - ["1/3", "2/3"], + [1, 0], + ["2/3", "1/3"], + [1, 0], [0, 1], - ["1/2", "1/2"], ], [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], - marks=pytest.mark.xfail(reason="Problem with nonterminal nodes in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") + ), + ( + games.create_seq_form_STOC_paper_zero_sum_2_player_efg(), + [ + [[0, 1], ["1/3", "2/3"], ["2/3", "1/3"]], + [["5/6", "1/6"], ["5/9", "4/9"]], + ], + ), + ( + games.create_perfect_info_with_chance_efg(), + [[[0, 1]], [[1, 0], [1, 0]]], ), ( - games.create_seq_form_STOC_paper_zero_sum_2_player_efg(), + games.create_three_action_internal_outcomes_efg(), [ - [[0, 1], ["1/3", "2/3"], ["2/3", "1/3"]], - [["5/6", "1/6"], ["5/9", "4/9"]], + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], + ] + ), + pytest.param( + games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), + [ + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") + ), + ( + games.create_large_payoff_game_efg(), + [ + [[1, 0], [1, 0]], + [[0, 1], ["9999999999999999999/10000000000000000000", + "1/10000000000000000000"]], + ], + ), + ( + games.create_chance_in_middle_efg(), + [ + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] + ], + ), + pytest.param( + games.create_chance_in_middle_efg(nonterm_outcomes=True), + [ + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] + ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ], ) @@ -637,7 +818,7 @@ def test_logit_solve_branch_error_with_invalid_max_accel(): def test_logit_solve_branch(): game = games.read_from_file("const_sum_game.nfg") assert len(gbt.qre.logit_solve_branch( - game=game, maxregret=0.2, first_step=0.2, max_accel=1)) > 0 + game=game, maxregret=0.2, first_step=0.2, max_accel=1)) > 0 def test_logit_solve_lambda_error_with_invalid_first_step(): @@ -659,30 +840,4 @@ def test_logit_solve_lambda_error_with_invalid_max_accel(): def test_logit_solve_lambda(): game = games.read_from_file("const_sum_game.nfg") assert len(gbt.qre.logit_solve_lambda( - game=game, lam=[1, 2, 3], first_step=0.2, max_accel=1)) > 0 - - -def test_kuhn(): - """ - TEMPORARY - - Check that the reduced strategic forms match for the versions with and without - nonterminal nodes - """ - old = games.create_kuhn_poker_efg(nonterm_outcomes=False) - new = games.create_kuhn_poker_efg(nonterm_outcomes=True) - for i in [0, 1]: - assert (old.to_arrays()[i] == new.to_arrays()[i]).all() - - -def test_stripped(): - """ - TEMPORARY - - Check that the reduced strategic forms match for the versions with and without - nonterminal nodes - """ - old = games.create_stripped_down_poker_efg() - new = games.create_stripped_down_poker_efg(nonterm_outcomes=True) - for i in [0, 1]: - assert (old.to_arrays()[i] == new.to_arrays()[i]).all() + game=game, lam=[1, 2, 3], first_step=0.2, max_accel=1)) > 0