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