Skip to content

Commit 4a0a824

Browse files
committed
ante implemented as an outcome at the root; simply test to compare RSF of old and new versions
1 parent 67cb1f5 commit 4a0a824

2 files changed

Lines changed: 113 additions & 0 deletions

File tree

tests/games.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

234338
def kuhn_poker_lcp_first_mixed_strategy_prof():
235339
"""

tests/test_nash.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,3 +542,12 @@ def test_logit_solve_lambda():
542542
game = games.read_from_file("const_sum_game.nfg")
543543
assert len(gbt.qre.logit_solve_lambda(
544544
game=game, lam=[1, 2, 3], first_step=0.2, max_accel=1)) > 0
545+
546+
def test_kuhn():
547+
old = games.create_kuhn_poker_efg()
548+
new = games.create_kuhn_poker_efg_internal_outcomes()
549+
550+
for i in [0,1]:
551+
assert (old.to_arrays()[i] == new.to_arrays()[i]).all()
552+
553+

0 commit comments

Comments
 (0)