@@ -230,6 +230,110 @@ def bet(player, payoffs, pot):
230230 g .sort_infosets ()
231231 return g
232232
233+ def create_kuhn_poker_efg_internal_outcomes () -> gbt .Game :
234+ """
235+ Returns
236+ -------
237+ Game
238+ Kuhn poker with 3 cards and 2 players
239+ """
240+ g = gbt .Game .new_tree (
241+ players = ["Alice" , "Bob" ], title = "Three-card poker (J, Q, K), two-player"
242+ )
243+ cards = ["J" , "Q" , "K" ]
244+ deals = ["JQ" , "JK" , "QJ" , "QK" , "KJ" , "KQ" ]
245+
246+ def deals_by_infoset (player , card ):
247+ player_idx = 0 if player == "Alice" else 1
248+ return [d for d in deals if d [player_idx ] == card ]
249+
250+ g .append_move (g .root , g .players .chance , deals )
251+ g .set_chance_probs (g .root .infoset , [gbt .Rational (1 , 6 )]* 6 )
252+ for alice_card in cards :
253+ # Alice's first move
254+ term_nodes = [g .root .children [d ] for d in deals_by_infoset ("Alice" , alice_card )]
255+ g .append_move (term_nodes , "Alice" , ["Check" , "Bet" ])
256+ for bob_card in cards :
257+ # Bob's move after Alice checks
258+ term_nodes = [g .root .children [d ].children ["Check" ]
259+ for d in deals_by_infoset ("Bob" , bob_card )]
260+ g .append_move (term_nodes , "Bob" , ["Check" , "Bet" ])
261+ for alice_card in cards :
262+ # Alice's move if Bob's second action is bet
263+ term_nodes = [g .root .children [d ].children ["Check" ].children ["Bet" ]
264+ for d in deals_by_infoset ("Alice" , alice_card )]
265+ g .append_move (term_nodes , "Alice" , ["Fold" , "Call" ])
266+ for bob_card in cards :
267+ # Bob's move after Alice bets initially
268+ term_nodes = [g .root .children [d ].children ["Bet" ]
269+ for d in deals_by_infoset ("Bob" , bob_card )]
270+ g .append_move (term_nodes , "Bob" , ["Fold" , "Call" ])
271+
272+ g .set_outcome (g .root , g .add_outcome ([- 1 , - 1 ], label = "Ante" ))
273+
274+ def calculate_payoffs (term_node ):
275+
276+ def get_path (node ):
277+ path = []
278+ while node .parent :
279+ path .append (node .prior_action .label )
280+ node = node .parent
281+ return path
282+
283+ def showdown_winner (deal ):
284+ # deal is an element of deals = ["JQ", "JK", "QJ", "QK", "KJ", "KQ"]
285+ card_values = dict (J = 0 , Q = 1 , K = 2 )
286+ a , b = deal
287+ return "Alice" if card_values [a ] > card_values [b ] else "Bob"
288+
289+ def showdown (deal , payoffs , pot ):
290+ payoffs [showdown_winner (deal )] += pot
291+ return payoffs
292+
293+ def bet (player , payoffs , pot ):
294+ payoffs [player ] += - 1
295+ pot += 1
296+ return payoffs , pot
297+
298+ path = get_path (term_node )
299+ deal = path .pop () # needed if there is a showdown
300+ payoffs = dict (Alice = 0 , Bob = 0 ) # ante of 1 for both players now ignored
301+ # outcome at root -- both pay -1
302+ pot = 2
303+ if path .pop () == "Check" : # Alice checks
304+ if path .pop () == "Check" : # Bob checks
305+ payoffs = showdown (deal , payoffs , pot )
306+ else : # Bob bets
307+ payoffs , pot = bet ("Bob" , payoffs , pot )
308+ if path .pop () == "Fold" : # Alice folds
309+ payoffs ["Bob" ] += pot
310+ else : # Alice calls
311+ payoffs , pot = bet ("Alice" , payoffs , pot )
312+ payoffs = showdown (deal , payoffs , pot )
313+ else : # Alice bets
314+ payoffs , pot = bet ("Alice" , payoffs , pot )
315+ if path .pop () == "Fold" : # Bob
316+ payoffs ["Alice" ] += pot
317+ else : # Bob calls
318+ payoffs , pot = bet ("Bob" , payoffs , pot )
319+ payoffs = showdown (deal , payoffs , pot )
320+
321+ return tuple (payoffs .values ())
322+
323+ # create 4 possible outcomes just once
324+ payoffs_to_outcomes = {(2 , 0 ): g .add_outcome ([2 , 0 ], label = "Alice wins 1" ),
325+ (3 , - 1 ): g .add_outcome ([3 , - 1 ], label = "Alice wins 2" ),
326+ (0 , 2 ): g .add_outcome ([0 , 2 ], label = "Bob wins 1" ),
327+ (- 1 , 3 ): g .add_outcome ([- 1 , 3 ], label = "Bob wins 2" )}
328+
329+ for term_node in [n for n in g .nodes if n .is_terminal ]:
330+ outcome = payoffs_to_outcomes [calculate_payoffs (term_node )]
331+ g .set_outcome (term_node , outcome )
332+
333+ # Ensure infosets are in the same order as if game was written to efg and read back in
334+ g .sort_infosets ()
335+ return g
336+
233337
234338def kuhn_poker_lcp_first_mixed_strategy_prof ():
235339 """
0 commit comments