Giter Club home page Giter Club logo

pokerkit's People

Contributors

aussieseaweed avatar benireydman avatar electronic-yoda avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

pokerkit's Issues

Dataset in this repository

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.

How to all-in?

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?

Run It Twice / Double Board Bomb Pots

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).

    • Unlike in tourneys, in cash-games, players can select the number of runouts during all-in situations or opt to not show their hand after an all-in.
  • Option to choose the number of runouts during all-in situations (disabled in tournament mode).

    • In theory, people choose number of runouts before they show their hands. But, this isn't always followed. It is also unclear who must select the number of runouts first. As such, after all-in, when showdown takes place,
  • 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]].

    • For example, if an all-in happens on the flop (AsKsQs) and is run twice (JsTs, JhTh), 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.
    • The function signatures for 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.
    • The properties/method 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, ...].
    • The method triplets for the hole dealing and showdown operation 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

      • In all-in situations, players have a chance to choose the number of runouts during showdown.
  • 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.

    • Arguments: runout_count and player_index who gives out the selection.
    • Querier: pokerkit.state.State.can_select_runout_count(player_index: int | None = None, runout_count: int | None = None).
    • Validator: pokerkit.state.State.verify_runout_count_selection(player_index: int | None = None, runout_count: int | None = None).
    • Operator: pokerkit.state.State.select_runout_count(player_index: int | None = None, runout_count: int | None = None, *, commentary: str | None = None).
    • People who can select run count: pokerkit.state.State.runout_count_selector_indices.
    • If runout_count are in disagreement among active players, only 1 runout is performed.
    • When multiple runs are selected, the state will be incompatible with the PHH file format, as it stands.
  • 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.

    • The maximum number of boards is either equal to the number of boards of the variant or (in case of multiple runouts) the product of it and the number of runouts.
  • 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

  • Double/triple/etc. board variants.

game.actor.showdown() throws IndexError for 2nd player at the end of a hand

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

Create a game tree with Pokerkit

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().

Awesome library!

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!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.