uoftcprg / pokerkit Goto Github PK
View Code? Open in Web Editor NEWAn open-source Python library for poker game simulations, hand evaluations, and statistical analysis
Home Page: https://pokerkit.readthedocs.io/
License: MIT License
An open-source Python library for poker game simulations, hand evaluations, and statistical analysis
Home Page: https://pokerkit.readthedocs.io/
License: MIT License
Thanks for your excellent open-source work!
I notice that there are wsop and pluribus hand history in the data folder. How can I browse or use these files?
Thank you for your reply.
The lookup and evaluations could highly benefit from mojo especially in terms of parallelizing.
I'm trying to use the library to build a game tree. However, I do not know how to apply all-in action to a state. Do I need to code this function my self?
For PokerKit Version 0.5
, I am planning on implementing run it twice (or thrice, etc.) and double (or triple, quadruple, etc.) board games.
This version release introduces a number of backward incompatible changes. Please read the below content carefully!
Summary of changes
Minor cleanup that may break older code.
Option to choose cash-game vs. tournament (default) mode (defaults to tournament mode).
Option to choose the number of runouts during all-in situations (disabled in tournament mode).
More degree of freedom in hole dealing/showdown order.
Changed
The parameters divmod
, and rake
for relevant poker game/state initialization methods are now keyword-only arguments. Before, one could supply them as positional arguments but this is no longer allowed!
pokerkit.state.State.board_cards
(previously list[Card]
) is now of type list[list[Card]]
.
state.board_cards == [[As], [Ks], [Qs], [Js, Jh], [Ts, Th]]
. Or, when double board omaha is played, something like state.board_cards == [[??, ??], [??, ??], [??, ??]]
will develop after the flop.pokerkit.state.State.get_hand
, pokerkit.state.State.get_up_hand
, and pokerkit.state.State.get_up_hands
now also requires the board_index
to be supplied.pokerkit.state.State.reserved_cards
, pokerkit.state.State.cards_in_play
, pokerkit.state.State.cards_not_in_play
, and pokerkit.state.State.get_dealable_cards(deal_count: int)
now return Iterator[Card]
instead of tuple[Card, ...]
.pokerkit.state.State.verify_hole_dealing()
, pokerkit.state.State.can_deal_hole()
, pokerkit.state.State.deal_hole()
, pokerkit.state.State.verify_hole_cards_showing_or_mucking()
, pokerkit.state.State.can_show_or_muck_hole_cards()
, and pokerkit.state.State.show_or_muck_hole_cards()
also accepts an optional positional argument player_index
to control the dealee, or the showdown performer. The verifiers also returns a player dealt if the dealee is not specified.Added
New enum class pokerkit.state.State.Mode
for setting tournament/cash-game mode while initializing poker states.
Tournament mode: pokerkit.state.Mode.TOURNAMENT
Cash-game mode: pokerkit.state.Mode.CASH_GAME
New parameter mode
in relevant poker game/state initialization methods. It defaults to tournament mode.
New automation pokerkit.state.State.Automation.RUNOUT_COUNT_SELECTION
which instructs PokerKit to carry out only one run-out.
New pokerkit.state.RUNOUT_COUNT_SELECTION
operation.
runout_count
and player_index
who gives out the selection.pokerkit.state.State.can_select_runout_count(player_index: int | None = None, runout_count: int | None = None)
.pokerkit.state.State.verify_runout_count_selection(player_index: int | None = None, runout_count: int | None = None)
.pokerkit.state.State.select_runout_count(player_index: int | None = None, runout_count: int | None = None, *, commentary: str | None = None)
.pokerkit.state.State.runout_count_selector_indices
.runout_count
are in disagreement among active players, only 1
runout is performed.New attribute pokerkit.state.State.street_return_indices
that internally keeps track of at the end of what street to return to ensure multiple runouts are performed.
New attribute pokerkit.state.State.runout_count
that shows the players' preferences on the number of runouts. It maybe None
in which case the runout selection was skipped due to the state being of tournament mode or all players showed no preference by passing in None
(or leaving empty) for the runout_count
argument during the corresponding method call of pokerkit.state.select_runout_count()
.
New attributes pokerkit.state.State.board_count
and pokerkit.state.State.board_indices
on the number of boards and the range of its indices. The number of boards is at least 1
but may be more due to multiple runouts or the variant being played.
New method pokerkit.state.State.get_board_cards(board_index: int)
on getting the board_index
'th board.
New attribute pokerkit.state.State.runout_count_selector_statuses
that keeps track of who can select the number of runouts.
New attribute pokerkit.state.State.runout_count_selection_flag
that keeps track of whether the runout count selection has been carried out.
Pending changes
from pokerface import *
from random import random
evaluator = StandardEvaluator()
deck = StandardDeck()
stakes = (0,(1,2))
stacks = [100,100]
def random_action(p):
if p.can_fold() and p.check_call_amount > 0 and random()<0.25:
p.fold()
return
r = random()
if r < 0.33:
p.check_call()
elif r<0.6:
if p.can_bet_raise():
p.bet_raise(max(p.bet_raise_min_amount, p.stack/5))
elif r<0.75:
if p.can_bet_raise():
p.bet_raise(max(p.bet_raise_min_amount, p.stack/3))
elif r<0.9:
if p.can_bet_raise():
p.bet_raise(max(p.bet_raise_min_amount, p.stack/5))
elif r<0.9:
if p.can_bet_raise():
p.bet_raise(p.stack)
def hand():
deck = StandardDeck()
game = NoLimitTexasHoldEm(Stakes(0, (1, 2)), stacks)
game.nature.deal_hole()
game.nature.deal_hole()
while game.is_terminal() == False and game.actor != game.nature:
random_action(game.actor)
if game.is_terminal() == False:
game.nature.deal_board()
if game.actor != game.nature:
game.actor.check_call()
game.actor.check_call()
game.nature.deal_board()
if game.actor != game.nature:
game.actor.check_call()
game.actor.check_call()
game.nature.deal_board()
if game.actor != game.nature and game.is_terminal() == False:
game.actor.check_call()
game.actor.check_call()
print(game.players, '____')
for p in game.players:
if game.is_terminal() == False:
print(game.players)
print(game.actor)
print(game.actor.can_showdown(), game.actor.is_showdown_necessary())
game.actor.showdown()
# print(dir(game))
# print(dir(game.nature))
print(game.players, 'end')
stacks.clear()
stacks.append(game.players[1].stack)
stacks.append(game.players[0].stack)
return
while 0 not in stacks:
hand()
results in
SequenceView([PokerPlayer(0, 101, ????), PokerPlayer(0, 99)]) end
SequenceView([PokerPlayer(0, 79.0, ????), PokerPlayer(0, 81.0, ????)]) ____
SequenceView([PokerPlayer(0, 79.0, ????), PokerPlayer(0, 81.0, ????)])
PokerPlayer(0, 79.0, ????)
True True
SequenceView([PokerPlayer(0, 79.0, 2cJs), PokerPlayer(0, 81.0, ????)])
PokerPlayer(0, 81.0, ????)
True True
SequenceView([PokerPlayer(0, 79.0, 2cJs), PokerPlayer(0, 121.0, QhAd)]) end
Traceback (most recent call last):
File "/home/tenoke/.local/lib/python3.8/site-packages/pokerface/game.py", line 270, in _update
while self.stages[index]._is_done():
IndexError: tuple index out of range
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "create_data.py", line 67, in <module>
hand()
File "create_data.py", line 35, in hand
random_action(game.actor)
File "create_data.py", line 11, in random_action
p.fold()
File "/home/tenoke/.local/lib/python3.8/site-packages/pokerface/game.py", line 708, in fold
self._get_fold_action().act()
File "/home/tenoke/.local/lib/python3.8/site-packages/pokerface/_actions.py", line 16, in act
self.game._update()
File "/home/tenoke/.local/lib/python3.8/site-packages/pokerface/game.py", line 275, in _update
self._distribute()
File "/home/tenoke/.local/lib/python3.8/site-packages/pokerface/game.py", line 283, in _distribute
for side_pot in self._side_pots:
File "/home/tenoke/.local/lib/python3.8/site-packages/pokerface/game.py", line 187, in _side_pots
cur = players[-1]._put
IndexError: list index out of range
despite needing to showdown to finish the hand
Hello, I'm working to create a poker solver. I'm using CFR. And I need to create a game tree. I've been trying to use your library to create a Pokerkit. However I'm still having some issues. Particularly when a node reaches the river. For instance a player bets and the other player calls, sometimes I don't have state.street is None
equals to True. I also have another problem. I didn't find a method to know when I have to call burn_card()
and deal_board
, so I did a Try Except.
Could you give a code to do a random poker tree ?
Here's the code I did that implements DeepCFR and creates a game tree :
class DeepCFR:
def __init__(
self,
starting_stacks: tuple[int],
blinds: tuple[int, int],
ranges: tuple[tuple[tuple[str, str]], tuple[tuple[str, str]]],
pot: int,
board: tuple[str],
bets: tuple[tuple[int], tuple[int]] = [(0.3, 0.7), (0.3, 0.7)],
iterations: int = 1,
K: int = 1,
n_players: int = 2,
mv_max: int = 100000,
mpi_max: int = 200000,
):
"""
starting_stacks: tuple of int
The starting stacks of the players
blinds: tuple of int
The small and big blinds
ranges: tuple of tuples of str
The ranges of the players. The first tuple is the range of the player ip and the second tuple is the range of the second player oop.
pot: int
The chips in the pot
board: tuple of str
The cards on the board. Each card is a string of the form "Xy" where X is the value and y is the suit
bets: tuple of tuples of int
The bets of the players. The first tuple is the bets of the player ip and the second tuple is the bets of the second player oop.
iterations: int
Number of cfr iterations to run
K: int
Number of traversals to run
n_players: int
Number of players
mv_max: int
Maximum size of the advantage memory
mpi_max: int
Maximum size of the strategy memory
"""
self.iterations = iterations
self.n_players = n_players
self.K = K
self.starting_stacks = starting_stacks
self.blinds = blinds
self.ranges = ranges
self.pot = pot
self.board = board
self.bets = bets
self.m_v = [[], []]
self.m_pi = []
self.mv_max = mv_max
self.mpi_max = mpi_max
self.val_net = []
self.n_game = 0
self.ip = 1
def get_opponent(self, player: int):
return 1 if player == 0 else 0
def run(self):
start = time.time()
print("Running DeepCFR...")
for t in range(self.iterations):
for player in range(self.n_players):
# Too many iterations. There needs to do a random picking of the cards
for k in range(self.K):
if k % 1000 == 0:
print(
f"Iteration {t} of {self.iterations} and k {k} of {self.K}"
)
card = random.choice(self.ranges[player])
opponent_card = random.choice(self.ranges[self.get_opponent(player)])
game = self.create_game(player, card, opponent_card)
print(card, opponent_card, self.board)
available_cards = [c for c in CARDS if c not in card + opponent_card + self.board]
turn_card = random.choice(available_cards)
river_card = random.choice([c for c in available_cards if c != turn_card])
turn_river_cards = [turn_card, river_card]
print("turn_river_cards : ", turn_river_cards)
print("card : ", card)
print("opponent_card : ", opponent_card)
self.Traverse(game, player, t, card, opponent_card, turn_river_cards, [])
# Update the advantage networks V using the advantage memories MV,1, MV,2
# Update the strategy network Π using the strategy memory MΠ
print("m_v: ", self.m_v)
print("m_pi: ", self.m_pi)
print("n_game: ", self.n_game)
print(f"DeepCFR finished in {time.time() - start:.2f} seconds")
def create_game(self, player: int, card: tuple[str, str], opponent_card: tuple[str, str]):
if len(self.board) == 3:
game = NoLimitTexasHoldem.create_state(
# Automations
(
Automation.ANTE_POSTING,
Automation.BET_COLLECTION,
Automation.BLIND_OR_STRADDLE_POSTING,
Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
Automation.HAND_KILLING,
Automation.CHIPS_PUSHING,
Automation.CHIPS_PULLING,
),
False, # Uniform antes?
0, # Antes
self.blinds, # Blinds or straddles
self.blinds[1], # Min-bet
(self.starting_stacks[0]+self.blinds[1], self.starting_stacks[1]+self.blinds[1]), # Starting stacks
2, # Number of players
)
if player == self.ip:
game.deal_hole("".join(opponent_card))
game.deal_hole("".join(card))
else:
game.deal_hole("".join(card))
game.deal_hole("".join(opponent_card))
game.check_or_call()
game.check_or_call()
game.burn_card()
game.deal_board("".join(self.board))
return game
def payoff(self, game, player: int):
return game.stacks[player] - self.starting_stacks[player]
def get_available_moves(self, game, player):
available_moves = []
#new_game = deepcopy(game)
if game.can_check_or_call():
available_moves.append(("check/call", 0))
#print("self bets : ", self.bets[player])
for bet in self.bets[player]:
new_game = deepcopy(game)
if int(bet*game.stacks[player]) <= game.stacks[player] and int(bet*game.stacks[player]) >= new_game.verify_completion_betting_or_raising_to():
available_moves.append(("raise", bet*game.stacks[player]))
return available_moves
def Traverse(
self,
game,
player: int,
traversal: int,
player_cards: tuple[str, str],
opponent_cards: tuple[str, str],
turn_river_cards: list[str],
bet_history : list,
):
if game.street is None:
return self.payoff(game, player)
try:
if len(turn_river_cards) == 0 and game.street is None:
return self.payoff(game, player)
elif len(turn_river_cards) > 0:
game.burn_card()
game.deal_board(turn_river_cards.pop(0))
print("card burnt and dealt")
print(game.burn_cards)
print(game.board_cards)
print(game.street)
return self.Traverse(game, player, traversal, player_cards, opponent_cards, turn_river_cards, bet_history)
#print("didn't go inside a if")
except ValueError:
pass
if game.street is None:
print("end of the hand : ", game.stacks)
return self.payoff(game, player)
new_game = deepcopy(game)
player_to_act = new_game._pop_actor_index()
if player_to_act == player:
#print("player to act : ", player_to_act)
new_game = deepcopy(game)
available_moves = self.get_available_moves(new_game, player)
values = np.array([0.0] * len(available_moves))
regrets = np.array([0.0] * len(available_moves))
for index, move in enumerate(available_moves):
new_game = deepcopy(game)
if move[0] == "check/call":
#print("check/call")
new_game.check_or_call()
else:
new_game.complete_bet_or_raise_to(move[1])
bet_history.append((player, 1, move[1]))
values[index] = self.Traverse(new_game, player, traversal, player_cards, opponent_cards, turn_river_cards, bet_history)
for index, move in enumerate(available_moves):
regrets[index] = values[index] - np.max(values)
cards = (player_cards, game.board_cards)
infoset = (cards, bet_history)
self.m_v[player].append((infoset, traversal, regrets))
else:
#print("opponent to act")
available_moves = self.get_available_moves(game, self.get_opponent(player))
pred = random.choice(available_moves)
if pred[0] == "check/call":
#print("check/call")
game.check_or_call()
else:
#print("raise")
game.complete_bet_or_raise_to(pred[1])
#bet_history.append(self.get_opponent(player), 1, pred[1])
return self.Traverse(game, player, traversal, player_cards, opponent_cards, turn_river_cards, bet_history)
# Example
DeepCFRAlgo = DeepCFR(
[100, 100],
(1, 2),
(
(("2c", "2d"), ("Kh", "Kd")),
(("3c", "3d"), ("Ah", "As"), ("Kc", "Kd"), ("Kh", "Kd")),
),
100,
("2h", "3h", "4h"),
)
DeepCFRAlgo.run()
The main problem I have is at the river :
return self.actor_indices.popleft()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
IndexError: pop from an empty deque
the issue is created from player_to_act = new_game._pop_actor_index()
.
Thanks for great library!
I want to deal card randomly but can't find the API.
No issue here - just a comment!
Randomly stumbled upon this repo! I am UofT alum! Also played at the poker club! Very cool to see these packages! You guys should make a GTO poker solver like GTOWizard.com since thats like 250$ per month and would be useful for students!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.