Skip to content

Commit 1735cb5

Browse files
committed
LP_STRATEGY_RATIONAL_CASES for test_nash_strategy_solver
1 parent e1dfe80 commit 1735cb5

3 files changed

Lines changed: 346 additions & 225 deletions

File tree

tests/games.py

Lines changed: 54 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def read_from_file(fn: str) -> gbt.Game:
1919

2020

2121
def create_efg_corresponding_to_bimatrix_game(
22-
A: np.ndarray, B: np.ndarray, title: str
22+
A: np.ndarray, B: np.ndarray, title: str
2323
) -> gbt.Game:
2424
"""
2525
There is no direct pygambit method to create an EFG from a stategic-form game.
@@ -42,19 +42,29 @@ def create_efg_corresponding_to_bimatrix_game(
4242
# Extensive-form games (efg)
4343

4444

45-
def create_2x2_zero_sum_efg(missing_term_outcome: bool = False) -> gbt.Game:
45+
def create_2x2_zero_sum_efg(variant: None | str = None) -> gbt.Game:
4646
"""
4747
EFG corresponding to 2x2 zero-sum game (I,-I).
48-
If missing_term_outcome, the terminal node after action 0 then 1 does not have an outcome.
48+
49+
If variant is:
50+
- "missing term outcome", terminal node after action 0 then 1 does not have an outcome.
51+
- "with neutral outcome", there is a (0,0) payoff outcomes at a non-terminal node.
4952
"""
5053
title = "EFG for 2x2 zero-sum game (I,-I)"
51-
if missing_term_outcome:
52-
title += " with missing terminal outcome"
54+
55+
if variant:
56+
title += " " + variant
57+
5358
A = np.eye(2)
5459
B = -A
5560
g = create_efg_corresponding_to_bimatrix_game(A, B, title)
56-
if missing_term_outcome:
61+
62+
if variant == "missing term outcome":
5763
g.delete_outcome(g.root.children[0].children[1].outcome)
64+
elif variant == "with neutral outcome":
65+
neutral = g.add_outcome([0, 0], label="neutral")
66+
g.set_outcome(g.root.children[0], neutral)
67+
5868
return g
5969

6070

@@ -91,8 +101,9 @@ def create_stripped_down_poker_efg(nonterm_outcomes: bool = False) -> gbt.Game:
91101
return read_from_file("stripped_down_poker.efg")
92102

93103
g = gbt.Game.new_tree(
94-
players=["Alice", "Bob"], title="Stripped-Down Poker: a simple game of one-card\
95-
poker from Reiley et al (2008)."
104+
players=["Alice", "Bob"],
105+
title="Stripped-Down Poker: a simple game of one-card\
106+
poker from Reiley et al (2008).",
96107
)
97108
deals = ["King", "Queen"]
98109
g.append_move(g.root, g.players.chance, deals)
@@ -107,21 +118,15 @@ def create_stripped_down_poker_efg(nonterm_outcomes: bool = False) -> gbt.Game:
107118
bob_calls_and_loses_outcome = g.add_outcome([4, -1], label="Bob Calls and Loses")
108119

109120
for node in g.root.children:
110-
g.append_move(
111-
node,
112-
player="Alice",
113-
actions=["Bet", "Fold"]
114-
)
121+
g.append_move(node, player="Alice", actions=["Bet", "Fold"])
115122
g.set_outcome(node.children["Fold"], alice_folds_outcome)
116123
g.set_outcome(node.children["Bet"], alice_bets_outcome)
117124

118-
alice_bets_nodes = [g.root.children["King"].children["Bet"],
119-
g.root.children["Queen"].children["Bet"]]
120-
g.append_move(
121-
alice_bets_nodes,
122-
player="Bob",
123-
actions=["Call", "Fold"]
124-
)
125+
alice_bets_nodes = [
126+
g.root.children["King"].children["Bet"],
127+
g.root.children["Queen"].children["Bet"],
128+
]
129+
g.append_move(alice_bets_nodes, player="Bob", actions=["Call", "Fold"])
125130
for node in alice_bets_nodes:
126131
g.set_outcome(node.children["Fold"], bob_folds_outcome)
127132

@@ -136,9 +141,7 @@ def _create_kuhn_poker_efg_without_outcomes():
136141
"""
137142
Used in create_kuhn_poker_efg()
138143
"""
139-
g = gbt.Game.new_tree(
140-
players=["Alice", "Bob"], title="Three-card poker (J, Q, K), two-player"
141-
)
144+
g = gbt.Game.new_tree(players=["Alice", "Bob"], title="Three-card poker (J, Q, K), two-player")
142145
cards = ["J", "Q", "K"]
143146
deals = ["JQ", "JK", "QJ", "QK", "KJ", "KQ"]
144147

@@ -147,25 +150,29 @@ def deals_by_infoset(player, card):
147150
return [d for d in deals if d[player_idx] == card]
148151

149152
g.append_move(g.root, g.players.chance, deals)
150-
g.set_chance_probs(g.root.infoset, [gbt.Rational(1, 6)]*6)
153+
g.set_chance_probs(g.root.infoset, [gbt.Rational(1, 6)] * 6)
151154
for alice_card in cards:
152155
# Alice's first move
153156
term_nodes = [g.root.children[d] for d in deals_by_infoset("Alice", alice_card)]
154157
g.append_move(term_nodes, "Alice", ["Check", "Bet"])
155158
for bob_card in cards:
156159
# Bob's move after Alice checks
157-
term_nodes = [g.root.children[d].children["Check"]
158-
for d in deals_by_infoset("Bob", bob_card)]
160+
term_nodes = [
161+
g.root.children[d].children["Check"] for d in deals_by_infoset("Bob", bob_card)
162+
]
159163
g.append_move(term_nodes, "Bob", ["Check", "Bet"])
160164
for alice_card in cards:
161165
# Alice's move if Bob's second action is bet
162-
term_nodes = [g.root.children[d].children["Check"].children["Bet"]
163-
for d in deals_by_infoset("Alice", alice_card)]
166+
term_nodes = [
167+
g.root.children[d].children["Check"].children["Bet"]
168+
for d in deals_by_infoset("Alice", alice_card)
169+
]
164170
g.append_move(term_nodes, "Alice", ["Fold", "Call"])
165171
for bob_card in cards:
166172
# Bob's move after Alice bets initially
167-
term_nodes = [g.root.children[d].children["Bet"]
168-
for d in deals_by_infoset("Bob", bob_card)]
173+
term_nodes = [
174+
g.root.children[d].children["Bet"] for d in deals_by_infoset("Bob", bob_card)
175+
]
169176
g.append_move(term_nodes, "Bob", ["Fold", "Call"])
170177
return g
171178

@@ -189,7 +196,6 @@ def _create_kuhn_poker_efg_only_term_outcomes() -> gbt.Game:
189196
g = _create_kuhn_poker_efg_without_outcomes()
190197

191198
def calculate_payoffs(term_node):
192-
193199
def get_path(node):
194200
path = []
195201
while node.parent:
@@ -231,10 +237,12 @@ def bet(player, payoffs, pot):
231237
return tuple(payoffs.values())
232238

233239
# create 4 possible outcomes just once
234-
payoffs_to_outcomes = {(1, -1): g.add_outcome([1, -1], label="Alice wins 1"),
235-
(2, -2): g.add_outcome([2, -2], label="Alice wins 2"),
236-
(-1, 1): g.add_outcome([-1, 1], label="Bob wins 1"),
237-
(-2, 2): g.add_outcome([-2, 2], label="Bob wins 2")}
240+
payoffs_to_outcomes = {
241+
(1, -1): g.add_outcome([1, -1], label="Alice wins 1"),
242+
(2, -2): g.add_outcome([2, -2], label="Alice wins 2"),
243+
(-1, 1): g.add_outcome([-1, 1], label="Bob wins 1"),
244+
(-2, 2): g.add_outcome([-2, 2], label="Bob wins 2"),
245+
}
238246

239247
for term_node in [n for n in g.nodes if n.is_terminal]:
240248
outcome = payoffs_to_outcomes[calculate_payoffs(term_node)]
@@ -280,7 +288,6 @@ def _create_kuhn_poker_efg_nonterm_outcomes() -> gbt.Game:
280288
outcomes_dict[tmp] = g.add_outcome(payoffs, label=tmp)
281289

282290
def add_outcomes(term_node):
283-
284291
def get_path(node):
285292
path = []
286293
while node.parent:
@@ -388,9 +395,7 @@ def create_one_shot_trust_efg(unique_NE_variant: bool = False) -> gbt.Game:
388395
)
389396
g.append_move(g.root, "Buyer", ["Trust", "Not trust"])
390397
g.append_move(g.root.children[0], "Seller", ["Honor", "Abuse"])
391-
g.set_outcome(
392-
g.root.children[0].children[0], g.add_outcome([1, 1], label="Trustworthy")
393-
)
398+
g.set_outcome(g.root.children[0].children[0], g.add_outcome([1, 1], label="Trustworthy"))
394399
if unique_NE_variant:
395400
g.set_outcome(
396401
g.root.children[0].children[1], g.add_outcome(["1/2", 2], label="Untrustworthy")
@@ -489,9 +494,7 @@ def __init__(self, params):
489494
self.m1 = params["m1"]
490495

491496
def gbt_game(self):
492-
g = gbt.Game.new_tree(
493-
players=["1", "2"], title=f"Centipede Game with {self.N} rounds"
494-
)
497+
g = gbt.Game.new_tree(players=["1", "2"], title=f"Centipede Game with {self.N} rounds")
495498
current_node = g.root
496499
current_player = "1"
497500
for t in range(self.N):
@@ -510,7 +513,6 @@ def gbt_game(self):
510513
return g
511514

512515
def reduced_strategies(self):
513-
514516
if self.N % 2 == 0:
515517
n_moves = [int(self.N / 2)] * 2
516518
else:
@@ -594,7 +596,6 @@ def __init__(self, n_players, params):
594596
self.n_players = n_players
595597

596598
def get_n_infosets(self, level):
597-
598599
if self.n_players == 1:
599600
return {1: 2 ** (level - 1)}
600601

@@ -649,16 +650,13 @@ def gbt_game(self):
649650

650651
def reduced_strategic_form(self):
651652
# special case for 1 player
652-
dims = (
653-
(self.size_of_rsf[0], 1) if len(self.size_of_rsf) == 1 else self.size_of_rsf
654-
)
653+
dims = (self.size_of_rsf[0], 1) if len(self.size_of_rsf) == 1 else self.size_of_rsf
655654

656655
zeros = np.zeros(dims, dtype=int)
657656
return [zeros] * len(self.players)
658657

659658

660659
class BinEfgOnePlayerIR(BinaryTreeGames):
661-
662660
def __init__(self, params):
663661
super().__init__(n_players=1, params=params)
664662

@@ -667,26 +665,21 @@ def _redu_strats(self, player, level):
667665
return self._redu_strategies_level_1(player)
668666
else:
669667
tmp = self._redu_strats(1, level - 1)
670-
tmp = [
671-
t[1:] for t in tmp
672-
] # remove first action (1 from 1st half; 2 from 2nd half)
668+
tmp = [t[1:] for t in tmp] # remove first action (1 from 1st half; 2 from 2nd half)
673669
n_half = int(len(tmp) / 2)
674670
first_half = tmp[:n_half]
675671
second_half = tmp[n_half:]
676-
n_stars = (
677-
self.get_n_infosets(level)[1] - self.get_n_infosets(level - 1)[1] - 1
678-
)
672+
n_stars = self.get_n_infosets(level)[1] - self.get_n_infosets(level - 1)[1] - 1
679673
stars = "*" * n_stars
680674
return (
681-
["11" + t + stars for t in first_half]
682-
+ ["12" + t + stars for t in second_half]
683-
+ ["21" + stars + t for t in first_half]
684-
+ ["22" + stars + t for t in second_half]
675+
["11" + t + stars for t in first_half]
676+
+ ["12" + t + stars for t in second_half]
677+
+ ["21" + stars + t for t in first_half]
678+
+ ["22" + stars + t for t in second_half]
685679
)
686680

687681

688682
class BinEfgTwoOrThreePlayers(BinaryTreeGames):
689-
690683
def _redu_strats(self, player, level):
691684
if level == 1:
692685
return self._redu_strategies_level_1(player)
@@ -698,11 +691,9 @@ def _redu_strats(self, player, level):
698691
n_stars = tmp1[player] - tmp2[last_player] - 1
699692
stars = "*" * n_stars
700693
return [
701-
"1" + t + stars
702-
for t in self._redu_strats(player=last_player, level=level - 1)
694+
"1" + t + stars for t in self._redu_strats(player=last_player, level=level - 1)
703695
] + [
704-
"2" + stars + t
705-
for t in self._redu_strats(player=last_player, level=level - 1)
696+
"2" + stars + t for t in self._redu_strats(player=last_player, level=level - 1)
706697
]
707698
elif player == 2:
708699
tmp = self._redu_strats(player=1, level=level - 1)

tests/test_extensive.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def test_outcome_index_exception_label():
162162
[np.array([[1, 0], [0, 1]]), np.array([[-1, 0], [0, -1]])]
163163
),
164164
(
165-
games.create_2x2_zero_sum_efg(missing_term_outcome=True),
165+
games.create_2x2_zero_sum_efg(variant="missing term outcome"),
166166
[["1", "2"], ["1", "2"]],
167167
[np.array([[1, 0], [0, 1]]), np.array([[-1, 0], [0, -1]])]
168168
),
@@ -434,7 +434,7 @@ def test_reduced_strategic_form(
434434
),
435435
(
436436
games.create_2x2_zero_sum_efg(),
437-
games.create_2x2_zero_sum_efg(missing_term_outcome=True)
437+
games.create_2x2_zero_sum_efg(variant="missing term outcome"),
438438
),
439439
(
440440
games.create_matching_pennies_efg(),

0 commit comments

Comments
 (0)