@@ -18,6 +18,32 @@ def read_from_file(fn: str) -> gbt.Game:
1818 raise ValueError (f"Unknown file extension in { fn } " )
1919
2020
21+ def create_efg_corresponding_to_bimatrix_game (
22+ A : np .ndarray , B : np .ndarray , title : str
23+ ) -> gbt .Game :
24+ """
25+ There is not direct pygambit method to create an EFG from a stategic-form game.
26+ Here we create an EFG corresponding to a bimatrix game, which is given by two numpy arrays.
27+ Player 1 moves first.
28+ """
29+ assert A .shape == B .shape
30+ m , n = A .shape
31+ g = gbt .Game .new_tree (players = ["1" , "2" ], title = title )
32+ actions1 = [str (i ) for i in range (m )]
33+ actions2 = [str (i ) for i in range (n )]
34+ g .append_move (g .root , "1" , actions1 )
35+ for node in g .root .children :
36+ g .append_move (node , "2" , actions2 )
37+ iset = g .root .children [0 ].infoset
38+ for c in g .root .children :
39+ g .set_infoset (c , iset )
40+ from itertools import product
41+
42+ for i , j in product (range (m ), range (n )):
43+ g .set_outcome (g .root .children [i ].children [j ], g .add_outcome ([A [i , j ], B [i , j ]]))
44+ return g
45+
46+
2147################################################################################################
2248# Normal-form (aka strategic-form) games (nfg)
2349
@@ -87,10 +113,22 @@ def create_mixed_behav_game_efg() -> gbt.Game:
87113 Game
88114 Three-player extensive form game: binary tree with 3 infomation sets, one per player,
89115 with 1, 2, and 4 nodes respectively
116+
117+ Since no information is revealed this is directly equivalent to a simultaneous move game
90118 """
91119 return read_from_file ("mixed_behavior_game.efg" )
92120
93121
122+ def create_1_card_poker_efg () -> gbt .Game :
123+ """
124+ Returns
125+ -------
126+ Game
127+ One-card two-player poker game, as used in the user guide
128+ """
129+ return read_from_file ("poker.efg" )
130+
131+
94132def create_myerson_2_card_poker_efg () -> gbt .Game :
95133 """
96134 Returns
@@ -104,6 +142,128 @@ def create_myerson_2_card_poker_efg() -> gbt.Game:
104142 return read_from_file ("myerson_2_card_poker.efg" )
105143
106144
145+ def create_kuhn_poker_efg () -> gbt .Game :
146+ """
147+ Returns
148+ -------
149+ Game
150+ Kuhn poker with 3 cards and 2 players
151+ """
152+ g = gbt .Game .new_tree (
153+ players = ["Alice" , "Bob" ], title = "Three-card poker (J, Q, K), two-player"
154+ )
155+ g .append_move (g .root , g .players .chance , ["JQ" , "JK" , "QJ" , "QK" , "KJ" , "KQ" ])
156+ g .set_chance_probs (g .root .infoset , [gbt .Rational (1 , 6 )]* 6 )
157+ # For each chance outcome node, append Alice's first decision: Check or Bet.
158+ for i in range (6 ):
159+ g .append_move (g .root .children [i ], "Alice" , ["Check" , "Bet" ])
160+ # After Alice checks, Bob moves: Check or Bet.
161+ for i in range (6 ):
162+ g .append_move (g .root .children [i ].children [0 ], "Bob" , ["Check" , "Bet" ])
163+ # If Bob bets after Alice checked (Bob's second action), then Alice can Fold or Call.
164+ # Append Alice's response nodes for the check-then-bet branch
165+ for i in range (6 ):
166+ g .append_move (g .root .children [i ].children [0 ].children [1 ], "Alice" , ["Fold" , "Call" ])
167+ # If Alice bets initially, Bob can Fold or Call.
168+ for i in range (6 ):
169+ g .append_move (g .root .children [i ].children [1 ], "Bob" , ["Fold" , "Call" ])
170+ # Set up information sets to represent imperfect information.
171+ # Alice dealt J
172+ g .set_infoset (g .root .children [0 ], g .root .children [1 ].infoset )
173+ # Alice dealt Q
174+ g .set_infoset (g .root .children [2 ], g .root .children [3 ].infoset )
175+ # Alice dealt K
176+ g .set_infoset (g .root .children [4 ], g .root .children [5 ].infoset )
177+ # Bob's decision after Alice checks: Bob knows his own card but not Alice's
178+ # Bob dealt Q
179+ g .set_infoset (g .root .children [0 ].children [0 ], g .root .children [5 ].children [0 ].infoset )
180+ # Bob dealt K
181+ g .set_infoset (g .root .children [1 ].children [0 ], g .root .children [3 ].children [0 ].infoset )
182+ # Bob dealt J
183+ g .set_infoset (g .root .children [2 ].children [0 ], g .root .children [4 ].children [0 ].infoset )
184+ # Bob's decision after Alice bets:
185+ # Bob dealt Q
186+ g .set_infoset (g .root .children [0 ].children [1 ], g .root .children [5 ].children [1 ].infoset )
187+ # Bob dealt K
188+ g .set_infoset (g .root .children [1 ].children [1 ], g .root .children [3 ].children [1 ].infoset )
189+ # Bob dealt J
190+ g .set_infoset (g .root .children [2 ].children [1 ], g .root .children [4 ].children [1 ].infoset )
191+ # Alice's decision after she checked and Bob then bet:
192+ # Alice dealt J
193+ g .set_infoset (g .root .children [0 ].children [0 ].children [1 ],
194+ g .root .children [1 ].children [0 ].children [1 ].infoset )
195+ # Alice dealt Q
196+ g .set_infoset (g .root .children [2 ].children [0 ].children [1 ],
197+ g .root .children [3 ].children [0 ].children [1 ].infoset )
198+ # Alice dealt K
199+ g .set_infoset (g .root .children [4 ].children [0 ].children [1 ],
200+ g .root .children [5 ].children [0 ].children [1 ].infoset )
201+ # Add outcomes at terminal nodes (net payoffs); first define four outcomes:
202+ alice_wins1 = g .add_outcome ([1 , - 1 ], label = "Alice wins 1" )
203+ alice_wins2 = g .add_outcome ([2 , - 2 ], label = "Alice wins 2" )
204+ bob_wins1 = g .add_outcome ([- 1 , 1 ], label = "Bob wins 1" )
205+ bob_wins2 = g .add_outcome ([- 2 , 2 ], label = "Bob wins 2" )
206+ # Check-Check -> Bob wins 1
207+ g .set_outcome (g .root .children [0 ].children [0 ].children [0 ], bob_wins1 )
208+ # Check-Bet-Fold -> Bob wins 1
209+ g .set_outcome (g .root .children [0 ].children [0 ].children [1 ].children [0 ], bob_wins1 )
210+ # Check-Bet-Call -> showdown -> Bob wins 2
211+ g .set_outcome (g .root .children [0 ].children [0 ].children [1 ].children [1 ], bob_wins2 )
212+ # Bet-Fold -> Alice bet, Bob folds -> Alice wins pot -> Alice wins 1
213+ g .set_outcome (g .root .children [0 ].children [1 ].children [0 ], alice_wins1 )
214+ # Bet-Call -> showdown -> Bob wins 2
215+ g .set_outcome (g .root .children [0 ].children [1 ].children [1 ], bob_wins2 )
216+ # JK -> Bob wins showdown
217+ g .set_outcome (g .root .children [1 ].children [0 ].children [0 ], bob_wins1 )
218+ g .set_outcome (g .root .children [1 ].children [0 ].children [1 ].children [0 ], bob_wins1 )
219+ g .set_outcome (g .root .children [1 ].children [0 ].children [1 ].children [1 ], bob_wins2 )
220+ g .set_outcome (g .root .children [1 ].children [1 ].children [0 ], alice_wins1 )
221+ g .set_outcome (g .root .children [1 ].children [1 ].children [1 ], bob_wins2 )
222+ # QJ -> Alice wins showdown
223+ g .set_outcome (g .root .children [2 ].children [0 ].children [0 ], alice_wins1 )
224+ g .set_outcome (g .root .children [2 ].children [0 ].children [1 ].children [0 ], bob_wins1 )
225+ g .set_outcome (g .root .children [2 ].children [0 ].children [1 ].children [1 ], alice_wins2 )
226+ g .set_outcome (g .root .children [2 ].children [1 ].children [0 ], alice_wins1 )
227+ g .set_outcome (g .root .children [2 ].children [1 ].children [1 ], alice_wins2 )
228+ # QK -> Bob wins showdown
229+ g .set_outcome (g .root .children [3 ].children [0 ].children [0 ], bob_wins1 )
230+ g .set_outcome (g .root .children [3 ].children [0 ].children [1 ].children [0 ], bob_wins1 )
231+ g .set_outcome (g .root .children [3 ].children [0 ].children [1 ].children [1 ], bob_wins2 )
232+ g .set_outcome (g .root .children [3 ].children [1 ].children [0 ], alice_wins1 )
233+ g .set_outcome (g .root .children [3 ].children [1 ].children [1 ], bob_wins2 )
234+ # KJ -> Alice wins showdown
235+ g .set_outcome (g .root .children [4 ].children [0 ].children [0 ], alice_wins1 )
236+ g .set_outcome (g .root .children [4 ].children [0 ].children [1 ].children [0 ], bob_wins1 )
237+ g .set_outcome (g .root .children [4 ].children [0 ].children [1 ].children [1 ], alice_wins2 )
238+ g .set_outcome (g .root .children [4 ].children [1 ].children [0 ], alice_wins1 )
239+ g .set_outcome (g .root .children [4 ].children [1 ].children [1 ], alice_wins2 )
240+ # KQ -> Alice wins showdown
241+ g .set_outcome (g .root .children [5 ].children [0 ].children [0 ], alice_wins1 )
242+ g .set_outcome (g .root .children [5 ].children [0 ].children [1 ].children [0 ], bob_wins1 )
243+ g .set_outcome (g .root .children [5 ].children [0 ].children [1 ].children [1 ], alice_wins2 )
244+ g .set_outcome (g .root .children [5 ].children [1 ].children [0 ], alice_wins1 )
245+ g .set_outcome (g .root .children [5 ].children [1 ].children [1 ], alice_wins2 )
246+ # Ensure infosets are in the same order as if game was written to efg and read back in
247+ g .sort_infosets ()
248+ return g
249+
250+
251+ def create_one_shot_trust_efg () -> gbt .Game :
252+ g = gbt .Game .new_tree (
253+ players = ["Buyer" , "Seller" ], title = "One-shot trust game, after Kreps (1990)"
254+ )
255+ g .append_move (g .root , "Buyer" , ["Trust" , "Not trust" ])
256+ g .append_move (g .root .children [0 ], "Seller" , ["Honor" , "Abuse" ])
257+ g .set_outcome (
258+ g .root .children [0 ].children [0 ], g .add_outcome ([1 , 1 ], label = "Trustworthy" )
259+ )
260+ g .set_outcome (
261+ g .root .children [0 ].children [1 ], g .add_outcome ([- 1 , 2 ], label = "Untrustworthy" )
262+ )
263+ g .set_outcome (g .root .children [1 ], g .add_outcome ([0 , 0 ], label = "Opt-out" ))
264+ return g
265+
266+
107267def create_centipede_game_with_chance_efg () -> gbt .Game :
108268 """
109269 Returns
@@ -198,7 +358,6 @@ def create_reduction_generic_payoffs_efg() -> gbt.Game:
198358 )
199359
200360 g .set_outcome (g .root .children [3 ], g .add_outcome ([12 , - 12 ], label = "d" ))
201-
202361 return g
203362
204363
@@ -236,6 +395,138 @@ def create_reduction_both_players_payoff_ties_efg() -> gbt.Game:
236395 return g
237396
238397
398+ def create_seq_form_STOC_paper_zero_sum_2_player_efg () -> gbt .Game :
399+ """
400+ Example from
401+
402+ Fast Algorithms for Finding Randomized Strategies in Game Trees (1994)
403+ Koller, Megiddo, von Stengel
404+ """
405+ g = gbt .Game .new_tree (players = ["1" , "2" ], title = "From STOC'94 paper" )
406+ g .append_move (g .root , g .players .chance , actions = ["1" , "2" , "3" , "4" ])
407+ g .set_chance_probs (g .root .infoset , [0.2 , 0.2 , 0.2 , 0.4 ])
408+ g .append_move (g .root .children [0 ], player = "1" , actions = ["l" , "r" ])
409+ g .append_move (g .root .children [1 ], player = "1" , actions = ["c" , "d" ])
410+ g .append_infoset (g .root .children [2 ], g .root .children [1 ].infoset )
411+ g .append_move (g .root .children [0 ].children [1 ], player = "2" , actions = ["p" , "q" ])
412+ g .append_move (
413+ g .root .children [0 ].children [1 ].children [0 ], player = "1" , actions = ["L" , "R" ]
414+ )
415+ g .append_infoset (
416+ g .root .children [0 ].children [1 ].children [1 ],
417+ g .root .children [0 ].children [1 ].children [0 ].infoset ,
418+ )
419+ g .append_move (g .root .children [2 ].children [0 ], player = "2" , actions = ["s" , "t" ])
420+ g .append_infoset (
421+ g .root .children [2 ].children [1 ], g .root .children [2 ].children [0 ].infoset
422+ )
423+
424+ g .set_outcome (
425+ g .root .children [0 ].children [0 ],
426+ outcome = g .add_outcome (payoffs = [5 , - 5 ], label = "l" ),
427+ )
428+ g .set_outcome (
429+ g .root .children [0 ].children [1 ].children [0 ].children [0 ],
430+ outcome = g .add_outcome (payoffs = [10 , - 10 ], label = "rpL" ),
431+ )
432+ g .set_outcome (
433+ g .root .children [0 ].children [1 ].children [0 ].children [1 ],
434+ outcome = g .add_outcome (payoffs = [15 , - 15 ], label = "rpR" ),
435+ )
436+ g .set_outcome (
437+ g .root .children [0 ].children [1 ].children [1 ].children [0 ],
438+ outcome = g .add_outcome (payoffs = [20 , - 20 ], label = "rqL" ),
439+ )
440+ g .set_outcome (
441+ g .root .children [0 ].children [1 ].children [1 ].children [1 ],
442+ outcome = g .add_outcome (payoffs = [- 5 , 5 ], label = "rqR" ),
443+ )
444+ g .set_outcome (
445+ g .root .children [1 ].children [0 ],
446+ outcome = g .add_outcome (payoffs = [10 , - 10 ], label = "c" ),
447+ )
448+ g .set_outcome (
449+ g .root .children [1 ].children [1 ],
450+ outcome = g .add_outcome (payoffs = [20 , - 20 ], label = "d" ),
451+ )
452+ g .set_outcome (
453+ g .root .children [2 ].children [0 ].children [0 ],
454+ outcome = g .add_outcome (payoffs = [20 , - 20 ], label = "cs" ),
455+ )
456+ g .set_outcome (
457+ g .root .children [2 ].children [0 ].children [1 ],
458+ outcome = g .add_outcome (payoffs = [50 , - 50 ], label = "ct" ),
459+ )
460+ g .set_outcome (
461+ g .root .children [2 ].children [1 ].children [0 ],
462+ outcome = g .add_outcome (payoffs = [30 , - 30 ], label = "ds" ),
463+ )
464+ g .set_outcome (
465+ g .root .children [2 ].children [1 ].children [1 ],
466+ outcome = g .add_outcome (payoffs = [15 , - 15 ], label = "dt" ),
467+ )
468+ g .set_outcome (
469+ g .root .children [3 ], outcome = g .add_outcome (payoffs = [5 , - 5 ], label = "nothing" )
470+ )
471+ g .root .children [0 ].infoset .label = "0"
472+ g .root .children [1 ].infoset .label = "1"
473+ g .root .children [0 ].children [1 ].infoset .label = "01"
474+ g .root .children [2 ].children [0 ].infoset .label = "20"
475+ g .root .children [0 ].children [1 ].children [0 ].infoset .label = "010"
476+
477+ return g
478+
479+
480+ def create_two_player_perfect_info_win_lose_efg () -> gbt .Game :
481+ g = gbt .Game .new_tree (players = ["1" , "2" ], title = "2 player perfect info win lose" )
482+ g .append_move (g .root , "2" , ["a" , "b" ])
483+ g .append_move (g .root .children [0 ], "1" , ["L" , "R" ])
484+ g .append_move (g .root .children [1 ], "1" , ["L" , "R" ])
485+ g .append_move (g .root .children [0 ].children [0 ], "2" , ["l" , "r" ])
486+ g .set_outcome (
487+ g .root .children [0 ].children [0 ].children [0 ], g .add_outcome ([1 , - 1 ], label = "aLl" )
488+ )
489+ g .set_outcome (
490+ g .root .children [0 ].children [0 ].children [1 ], g .add_outcome ([- 1 , 1 ], label = "aLr" )
491+ )
492+ g .set_outcome (g .root .children [0 ].children [1 ], g .add_outcome ([1 , - 1 ], label = "aR" ))
493+ g .set_outcome (g .root .children [1 ].children [0 ], g .add_outcome ([1 , - 1 ], label = "bL" ))
494+ g .set_outcome (g .root .children [1 ].children [1 ], g .add_outcome ([- 1 , 1 ], label = "bR" ))
495+ return g
496+
497+
498+ def create_EFG_for_nxn_bimatrix_coordination_game (n : int ) -> gbt .Game :
499+ A = np .eye (n , dtype = int )
500+ B = A
501+ title = f"{ n } x{ n } coordination game, { 2 ** n - 1 } equilibria"
502+ return create_efg_corresponding_to_bimatrix_game (A , B , title )
503+
504+
505+ def create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq () -> gbt .Game :
506+ # 6 x 6 Payoff matrix A:
507+ A = [
508+ [- 180 , 72 , - 333 , 297 , - 153 , 270 ],
509+ [- 30 , 17 , - 33 , 42 , - 3 , 20 ],
510+ [- 81 , 36 , - 126 , 126 , - 36 , 90 ],
511+ [90 , - 36 , 126 , - 126 , 36 , - 81 ],
512+ [20 , - 3 , 42 , - 33 , 17 , - 30 ],
513+ [270 , - 153 , 297 , - 333 , 72 , - 180 ],
514+ ]
515+ # 6 x 6 Payoff matrix B:
516+ B = [
517+ [72 , 36 , 17 , - 3 , - 36 , - 153 ],
518+ [- 180 , - 81 , - 30 , 20 , 90 , 270 ],
519+ [297 , 126 , 42 , - 33 , - 126 , - 333 ],
520+ [- 333 , - 126 , - 33 , 42 , 126 , 297 ],
521+ [270 , 90 , 20 , - 30 , - 81 , - 180 ],
522+ [- 153 , - 36 , - 3 , 17 , 36 , 72 ],
523+ ]
524+ A = np .array (A )
525+ B = np .array (B )
526+ title = "6x6 Long Lemke-Howson Paths, unique eq"
527+ return create_efg_corresponding_to_bimatrix_game (A , B , title )
528+
529+
239530class EfgFamilyForReducedStrategicFormTests (ABC ):
240531 """ """
241532
0 commit comments