Poker Simulation

PokerKit is a very powerful tool you can use to simulate games. It allows you to play any poker games and modify the state at any level. It also gives you a handy programmatic API to query if you can do something or get information such as maximum completion/betting/raising to amount, or checking/calling amount.

All poker games progresses as follows:

  1. Ante posting

  2. Bet collection (if anyone anted)

  3. Blind/straddle posting

  4. Dealing (if hand is still on) (burning card, hole, board, stand pat or discard)

  5. Betting (if hand is still on and people are not all in) (fold, check, call, bring-in, complete, bet, raise to)

  6. Bet collection (if anyone bet)

  7. Go back to Step 4 if any street remains and the hand is on

  8. Showdown (if there are still people in the pot)

  9. Hand killing (if anyone has shown hands that cannot win anything)

  10. Chips pushing (move chips from center to the winner(s))

  11. Chips pulling (winner(s) pull the chips they won into the pot)

Depending on the use cases, many of these phases can be automated without any user input, as user can specify which operations they want to be manual and automatic.

Initialization

PokerKit offers virtually unlimited poker variants to be played. However, defining poker variants can be quite an overwhelming task for a new user. We offer pre-defined poker variants where user can just supply arguments such as antes, blinds, starting stacks, et cetera.

Pre-defined poker variants:

Not finding what you are looking for? Define your own or create an issue!

They can be created as shown below:

from pokerkit import (
     Automation,
     FixedLimitDeuceToSevenLowballTripleDraw,
     NoLimitTexasHoldem,
)

state = FixedLimitDeuceToSevenLowballTripleDraw.create_state(
    # automations
    (
        Automation.ANTE_POSTING,
        Automation.BET_COLLECTION,
        Automation.BLIND_OR_STRADDLE_POSTING,
        Automation.CARD_BURNING,
        Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
        Automation.HAND_KILLING,
        Automation.CHIPS_PUSHING,
        Automation.CHIPS_PULLING,
    ),
    True,  # False for big blind ante, True otherwise
    None,  # ante
    (75000, 150000),  # blinds or straddles
    150000,  # small bet
    300000,  # big bet
    (1180000, 4340000, 5910000, 10765000),  # starting stacks
    4,  # number of players
)

state = NoLimitTexasHoldem.create_state(
    # automations
    (
        Automation.ANTE_POSTING,
        Automation.BET_COLLECTION,
        Automation.BLIND_OR_STRADDLE_POSTING,
        Automation.CARD_BURNING,
        Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
        Automation.HAND_KILLING,
        Automation.CHIPS_PUSHING,
        Automation.CHIPS_PULLING,
    ),
    True,  # False for big blind ante, True otherwise
    500,  # ante
    (1000, 2000),  # blinds or straddles
    2000,  # min bet
    (1125600, 2000000, 553500),  # starting stacks
    3,  # number of players
)

You can define your own variant as shown below:

from pokerkit import (
     Automation,
     BettingStructure,
     Deck,
     KuhnPokerHand,
     Opening,
     State,
     Street,
)

state = State(
    # deck
    Deck.KUHN_POKER,
    # hand types (high/low-split will have two types)
    (KuhnPokerHand,),
    # streets
    (
        Street(
            False,  # card burning
            (False,),  # hole card dealing statuses (False for face-down)
            0,  # board dealing card
            False,  # standing pat or discarding
            Opening.POSITION,  # who opens the betting?
            1,  # min bet
            None,  # maximum number of completions/bettings/raisings
        ),
    ),
    # betting structure
    BettingStructure.FIXED_LIMIT,
    # automations
    (
        Automation.ANTE_POSTING,
        Automation.BET_COLLECTION,
        Automation.BLIND_OR_STRADDLE_POSTING,
        Automation.CARD_BURNING,
        Automation.HOLE_DEALING,
        Automation.BOARD_DEALING,
        Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
        Automation.HAND_KILLING,
        Automation.CHIPS_PUSHING,
        Automation.CHIPS_PULLING,
    ),
    # False for big blind ante, otherwise True
    True,
    # ante
    (1,) * 2,
    # blind or straddles
    (0,) * 2,
    # bring-in
    0,
    # starting stacks
    (2,) * 2,
)

There is a lot to specify and you will have to experiment to get it right.

These are two different ways to create a state. Now that we have a state, we can play around with it!

Attributes

You can access various things about the state by accessing the following attributes and methods.

There are more, such as the initial game parameters and attributes that keep track of who is acting, et cetera. You can look at pokerkit.state.State for a complete list.

Operations

The wide selection you have is not the only thing that will overwhelm you. We also offer fine-grained poker state modifications. Depending on your use case, many of our operations will not be of concern.

All operations:

For example, if you are trying to create a poker AI, you are not worried about mucking the best hand or showing the worse hand, burning a card, pushing the chips to the winners, collecting chips a player won, collecting bets after each street, et cetera. But, you want to handle user actions like fold, check, call, bring-in, complete, bet, and raise. Also, you might want to control what cards are dealt to each player and to the board.

However, if you are trying to create an online poker room, you need all these fine changes to create smooth user experience. Although, you might not be concerned about exactly what cards are dealt. You would be happy with cards being dealt at random (hopefully).

PokerKit allow you to specify what you are worried about, and what you are not worried about. pokerkit.state.Automation describes operations that can be automated.

Sample automations:

from pokerkit import Automation

# automate everything except actions
automations = (
    Automation.ANTE_POSTING,
    Automation.BET_COLLECTION,
    Automation.BLIND_OR_STRADDLE_POSTING,
    Automation.CARD_BURNING,
    Automation.HOLE_DEALING,
    Automation.BOARD_DEALING,
    Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
    Automation.HAND_KILLING,
    Automation.CHIPS_PUSHING,
    Automation.CHIPS_PULLING,
)

# Automate everything except actions and dealings
automations = (
    Automation.ANTE_POSTING,
    Automation.BET_COLLECTION,
    Automation.BLIND_OR_STRADDLE_POSTING,
    Automation.CARD_BURNING,
    Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
    Automation.HAND_KILLING,
    Automation.CHIPS_PUSHING,
    Automation.CHIPS_PULLING,
)

# Automate nothing
automations = ()

Now, let’s say you know what operations you should worry about. How do you know when to invoke them? PokerKit has handy methods to query whether you can perform an operation:

These methods return True if you can perform such an operation (with specified arguments, if any) or False if otherwise.

Most of the operations can optionally accept arguments. Some are more important than others. Let’s see what we can specify for each action.

  • Ante posting: player_index, defaults to first player who did not post ante

  • Bet collection: N/A

  • Blind/straddle posting: player_index, defaults to first player who did not post the blind or straddle

  • Card burning: card, defaults to randomly drawing from the deck

  • Hole dealing: cards, defaults to randomly drawing a single card from the deck

  • Board dealing: cards, defaults to randomly drawing required cards from the deck

  • Standing pat/discarding: cards, defaults to standing pat

  • Folding: N/A

  • Checking/calling: N/A

  • Bring-in posting: N/A

  • Completion/betting/raising to: amount, defaults to completion, min-bet, or min-raise

  • Hole cards showing/mucking: status, defaults to showing only when no-one else has shown a better hand

  • Hand killing: player_index, defaults to the first player who cannot win any portion of the pot

  • Chips pushing: N/A

  • Chips pulling: player_index, defaults to the first player who won a portion of the pot

How do you know what the minimum bets are? How do you know to whom the hole card will be dealt next? How do you know the call amount? Whose action is it? You can access all these information through the following methods or properties

  • Effective ante: poker.state.State.get_effective_ante()

  • Ante poster indices: poker.state.State.ante_poster_indices

  • Effective blind/straddle: poker.state.State.get_effective_blind_or_straddle()

  • Blind/straddle poster indices: poker.state.State.blind_or_straddle_poster_indices

  • Available cards to be dealt: poker.state.State.available_cards

  • Next default hole dealee: poker.state.State.hole_dealee_index

  • Next stander pat or discarder: poker.state.State.stander_pat_or_discarder_index

  • Next actor (fold, check, …): poker.state.State.actor_index

  • Effective stack: poker.state.State.get_effective_stack

  • Checking/Calling amount: poker.state.State.checking_or_calling_amount

  • Effective bring-in amount: poker.state.State.effective_bring_in_amount

  • Min completion/bet/raise to amount: poker.state.State.min_completion_betting_or_raising_to_amount

  • Pot completion/bet/raise to amount: poker.state.State.pot_completion_betting_or_raising_to_amount

  • Max completion/bet/raise to amount: poker.state.State.max_completion_betting_or_raising_to_amount

  • Person who is in showdown: poker.state.State.showdown_index

  • Indices of players who cannot win and whose hand is about to be killed: poker.state.State.hand_killing_indices

  • Players who won but has not taken back the chips into their stack yet: poker.state.State.chips_pulling_indices`

After each action is performed, description of which player was involved, what was the amount, what card was burnt, what cards were dealt, how much bets were collected, et cetera are returned. The types of these are as shown:

Interactions

Now, let’s look at some sample interactions!

This is a simple interaction.

>>> from pokerkit import *
>>> state = FixedLimitTexasHoldem.create_state(
...     (
...         Automation.ANTE_POSTING,
...         Automation.BET_COLLECTION,
...         Automation.BLIND_OR_STRADDLE_POSTING,
...         Automation.CARD_BURNING,
...         Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
...         Automation.HAND_KILLING,
...         Automation.CHIPS_PUSHING,
...         Automation.CHIPS_PULLING,
...     ),
...     True,
...     None,
...     (1, 2),
...     2,
...     4,
...     200,
...     2,
... )
>>> # Below shows the pre-flop dealings and actions.
>>> state.deal_hole('AcAs')
State.HoleDealing(player_index=0, cards=(Ac, As), statuses=(False, False))
>>> state.deal_hole('7h6h')
State.HoleDealing(player_index=1, cards=(7h, 6h), statuses=(False, False))
>>> state.complete_bet_or_raise_to()
State.CompletionBettingOrRaisingTo(player_index=1, amount=4)
>>> state.complete_bet_or_raise_to()
State.CompletionBettingOrRaisingTo(player_index=0, amount=6)
>>> state.fold()
State.Folding(player_index=1)
>>> # Below show the final stacks.
>>> state.stacks
[204, 196]

Below shows the first televised million dollar pot between Tom Dwan and Phil Ivey.

Link: https://youtu.be/GnxFohpljqM

>>> state = NoLimitTexasHoldem.create_state(
...     (
...         Automation.ANTE_POSTING,
...         Automation.BET_COLLECTION,
...         Automation.BLIND_OR_STRADDLE_POSTING,
...         Automation.CARD_BURNING,
...         Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
...         Automation.HAND_KILLING,
...         Automation.CHIPS_PUSHING,
...         Automation.CHIPS_PULLING,
...     ),
...     True,
...     500,
...     (1000, 2000),
...     2000,
...     (1125600, 2000000, 553500),
...     3,
... )
>>> # Below shows the pre-flop dealings and actions.
>>> state.deal_hole('Ac2d')  # Ivey
State.HoleDealing(player_index=0, cards=(Ac, 2d), statuses=(False, False))
>>> state.deal_hole('5h7s')  # Antonius*
State.HoleDealing(player_index=1, cards=(5h, 7s), statuses=(False, False))
>>> state.deal_hole('7h6h')  # Dwan
State.HoleDealing(player_index=2, cards=(7h, 6h), statuses=(False, False))
>>> state.complete_bet_or_raise_to(7000)  # Dwan
State.CompletionBettingOrRaisingTo(player_index=2, amount=7000)
>>> state.complete_bet_or_raise_to(23000)  # Ivey
State.CompletionBettingOrRaisingTo(player_index=0, amount=23000)
>>> state.fold()  # Antonius
State.Folding(player_index=1)
>>> state.check_or_call()  # Dwan
State.CheckingOrCalling(player_index=2, amount=16000)
>>> # Below shows the flop dealing and actions.
>>> state.deal_board('Jc3d5c')
State.BoardDealing(cards=(Jc, 3d, 5c))
>>> state.complete_bet_or_raise_to(35000)  # Ivey
State.CompletionBettingOrRaisingTo(player_index=0, amount=35000)
>>> state.check_or_call()  # Dwan
State.CheckingOrCalling(player_index=2, amount=35000)
>>> # Below shows the turn dealing and actions.
>>> state.deal_board('4h')
State.BoardDealing(cards=(4h,))
>>> state.complete_bet_or_raise_to(90000)  # Ivey
State.CompletionBettingOrRaisingTo(player_index=0, amount=90000)
>>> state.complete_bet_or_raise_to(232600)  # Dwan
State.CompletionBettingOrRaisingTo(player_index=2, amount=232600)
>>> state.complete_bet_or_raise_to(1067100)  # Ivey
State.CompletionBettingOrRaisingTo(player_index=0, amount=1067100)
>>> state.check_or_call()  # Dwan
State.CheckingOrCalling(player_index=2, amount=262400)
>>> # Below shows the river dealing.
>>> state.deal_board('Jh')
State.BoardDealing(cards=(Jh,))
>>> # Below show the final stacks.
>>> state.stacks
[572100, 1997500, 1109500]

Below shows an all-in hand between Xuan and Phua.

Link: https://youtu.be/QlgCcphLjaQ

>>> state = NoLimitShortDeckHoldem.create_state(
...     (
...         Automation.ANTE_POSTING,
...         Automation.BET_COLLECTION,
...         Automation.BLIND_OR_STRADDLE_POSTING,
...         Automation.CARD_BURNING,
...         Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
...         Automation.HAND_KILLING,
...         Automation.CHIPS_PUSHING,
...         Automation.CHIPS_PULLING,
...     ),
...     True,
...     3000,
...     {-1: 3000},
...     3000,
...     (495000, 232000, 362000, 403000, 301000, 204000),
...     6,
... )
>>> # Below shows the pre-flop dealings and actions.
>>> state.deal_hole('Th8h')  # Badziakouski
State.HoleDealing(player_index=0, cards=(Th, 8h), statuses=(False, False))
>>> state.deal_hole('QsJd')  # Zhong
State.HoleDealing(player_index=1, cards=(Qs, Jd), statuses=(False, False))
>>> state.deal_hole('QhQd')  # Xuan
State.HoleDealing(player_index=2, cards=(Qh, Qd), statuses=(False, False))
>>> state.deal_hole('8d7c')  # Jun
State.HoleDealing(player_index=3, cards=(8d, 7c), statuses=(False, False))
>>> state.deal_hole('KhKs')  # Phua
State.HoleDealing(player_index=4, cards=(Kh, Ks), statuses=(False, False))
>>> state.deal_hole('8c7h')  # Koon
State.HoleDealing(player_index=5, cards=(8c, 7h), statuses=(False, False))
>>> state.check_or_call()  # Badziakouski
State.CheckingOrCalling(player_index=0, amount=3000)
>>> state.check_or_call()  # Zhong
State.CheckingOrCalling(player_index=1, amount=3000)
>>> state.complete_bet_or_raise_to(35000)  # Xuan
State.CompletionBettingOrRaisingTo(player_index=2, amount=35000)
>>> state.fold()  # Jun
State.Folding(player_index=3)
>>> state.complete_bet_or_raise_to(298000)  # Phua
State.CompletionBettingOrRaisingTo(player_index=4, amount=298000)
>>> state.fold()  # Koon
State.Folding(player_index=5)
>>> state.fold()  # Badziakouski
State.Folding(player_index=0)
>>> state.fold()  # Zhong
State.Folding(player_index=1)
>>> state.check_or_call()  # Xuan
State.CheckingOrCalling(player_index=2, amount=263000)
>>> # Below shows the flop dealing.
>>> state.deal_board('9h6cKc')
State.BoardDealing(cards=(9h, 6c, Kc))
>>> # Below shows the turn dealing.
>>> state.deal_board('Jh')
State.BoardDealing(cards=(Jh,))
>>> # Below shows the river dealing.
>>> state.deal_board('Ts')
State.BoardDealing(cards=(Ts,))
>>> # Below show the final stacks.
>>> state.stacks
[489000, 226000, 684000, 400000, 0, 198000]

Below shows the largest online poker pot every played between Patrik Antonius and Viktor Blom.

Link: https://youtu.be/UMBm66Id2AA

>>> state = PotLimitOmahaHoldem.create_state(
...     (
...         Automation.ANTE_POSTING,
...         Automation.BET_COLLECTION,
...         Automation.BLIND_OR_STRADDLE_POSTING,
...         Automation.CARD_BURNING,
...         Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
...         Automation.HAND_KILLING,
...         Automation.CHIPS_PUSHING,
...         Automation.CHIPS_PULLING,
...     ),
...     True,
...     None,
...     (50000, 100000),
...     2000,
...     (125945025, 67847350),
...     2,
... )
>>> # Below shows the pre-flop dealings and actions.
>>> state.deal_hole('Ah3sKsKh')  # Antonius
State.HoleDealing(player_index=0, cards=(Ah, 3s, Ks, Kh), statuses=(False, False, False, False))
>>> state.deal_hole('6d9s7d8h')  # Blom
State.HoleDealing(player_index=1, cards=(6d, 9s, 7d, 8h), statuses=(False, False, False, False))
>>> state.complete_bet_or_raise_to(300000)  # Blom
State.CompletionBettingOrRaisingTo(player_index=1, amount=300000)
>>> state.complete_bet_or_raise_to(900000)  # Antonius
State.CompletionBettingOrRaisingTo(player_index=0, amount=900000)
>>> state.complete_bet_or_raise_to(2700000)  # Blom
State.CompletionBettingOrRaisingTo(player_index=1, amount=2700000)
>>> state.complete_bet_or_raise_to(8100000)  # Antonius
State.CompletionBettingOrRaisingTo(player_index=0, amount=8100000)
>>> state.check_or_call()  # Blom
State.CheckingOrCalling(player_index=1, amount=5400000)
>>> # Below shows the flop dealing and actions.
>>> state.deal_board('4s5c2h')
State.BoardDealing(cards=(4s, 5c, 2h))
>>> state.complete_bet_or_raise_to(9100000)  # Antonius
State.CompletionBettingOrRaisingTo(player_index=0, amount=9100000)
>>> state.complete_bet_or_raise_to(43500000)  # Blom
State.CompletionBettingOrRaisingTo(player_index=1, amount=43500000)
>>> state.complete_bet_or_raise_to(77900000)  # Antonius
State.CompletionBettingOrRaisingTo(player_index=0, amount=77900000)
>>> state.check_or_call()  # Blom
State.CheckingOrCalling(player_index=1, amount=16247350)
>>> # Below shows the turn dealing.
>>> state.deal_board('5h')
State.BoardDealing(cards=(5h,))
>>> # Below shows the river dealing.
>>> state.deal_board('9c')
State.BoardDealing(cards=(9c,))
>>> # Below show the final stacks.
>>> state.stacks
[193792375, 0]

Below shows a bad beat between Yockey and Arieh.

Link: https://youtu.be/pChCqb2FNxY

>>> state = FixedLimitDeuceToSevenLowballTripleDraw.create_state(
...     (
...         Automation.ANTE_POSTING,
...         Automation.BET_COLLECTION,
...         Automation.BLIND_OR_STRADDLE_POSTING,
...         Automation.CARD_BURNING,
...         Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
...         Automation.HAND_KILLING,
...         Automation.CHIPS_PUSHING,
...         Automation.CHIPS_PULLING,
...     ),
...     True,
...     None,
...     (75000, 150000),
...     150000,
...     300000,
...     (1180000, 4340000, 5910000, 10765000),
...     4,
... )
>>> # Below shows the pre-flop dealings and actions.
>>> state.deal_hole('7h6c4c3d2c')  # Yockey
State.HoleDealing(player_index=0, cards=(7h, 6c, 4c, 3d, 2c), statuses=(False, False, False, False, False))
>>> state.deal_hole('JsJcJdJhTs')  # Hui*
State.HoleDealing(player_index=1, cards=(Js, Jc, Jd, Jh, Ts), statuses=(False, False, False, False, False))
>>> state.deal_hole('KsKcKdKhTh')  # Esposito*
State.HoleDealing(player_index=2, cards=(Ks, Kc, Kd, Kh, Th), statuses=(False, False, False, False, False))
>>> state.deal_hole('AsQs6s5c3c')  # Arieh
State.HoleDealing(player_index=3, cards=(As, Qs, 6s, 5c, 3c), statuses=(False, False, False, False, False))
>>> state.fold()  # Esposito
State.Folding(player_index=2)
>>> state.complete_bet_or_raise_to()  # Arieh
State.CompletionBettingOrRaisingTo(player_index=3, amount=300000)
>>> state.complete_bet_or_raise_to()  # Yockey
State.CompletionBettingOrRaisingTo(player_index=0, amount=450000)
>>> state.fold()  # Hui
State.Folding(player_index=1)
>>> state.check_or_call()  # Arieh
State.CheckingOrCalling(player_index=3, amount=150000)
>>> # Below shows the first draw and actions.
>>> state.stand_pat_or_discard()  # Yockey
State.StandingPatOrDiscarding(player_index=0, cards=())
>>> state.stand_pat_or_discard('AsQs')  # Arieh
State.StandingPatOrDiscarding(player_index=3, cards=(As, Qs))
>>> state.deal_hole('2hQh')  # Arieh
State.HoleDealing(player_index=3, cards=(2h, Qh), statuses=(False, False))
>>> state.complete_bet_or_raise_to()  # Yockey
State.CompletionBettingOrRaisingTo(player_index=0, amount=150000)
>>> state.check_or_call()  # Arieh
State.CheckingOrCalling(player_index=3, amount=150000)
>>> # Below shows the second draw and actions.
>>> state.stand_pat_or_discard()  # Yockey
State.StandingPatOrDiscarding(player_index=0, cards=())
>>> state.stand_pat_or_discard('Qh')  # Arieh
State.StandingPatOrDiscarding(player_index=3, cards=(Qh,))
>>> state.deal_hole('4d')  # Arieh
State.HoleDealing(player_index=3, cards=(4d,), statuses=(False,))
>>> state.complete_bet_or_raise_to()  # Yockey
State.CompletionBettingOrRaisingTo(player_index=0, amount=300000)
>>> state.check_or_call()  # Arieh
State.CheckingOrCalling(player_index=3, amount=300000)
>>> # Below shows the third draw and actions.
>>> state.stand_pat_or_discard()  # Yockey
State.StandingPatOrDiscarding(player_index=0, cards=())
>>> state.stand_pat_or_discard('6s')  # Arieh
State.StandingPatOrDiscarding(player_index=3, cards=(6s,))
>>> state.deal_hole('7c')  # Arieh
State.HoleDealing(player_index=3, cards=(7c,), statuses=(False,))
>>> state.complete_bet_or_raise_to()  # Yockey
State.CompletionBettingOrRaisingTo(player_index=0, amount=280000)
>>> state.check_or_call()  # Arieh
State.CheckingOrCalling(player_index=3, amount=280000)
>>> # Below show the final stacks.
>>> state.stacks
[0, 4190000, 5910000, 12095000]

Below shows an example badugi hand from Wikipedia.

Link: https://en.wikipedia.org/wiki/Badugi

>>> state = FixedLimitBadugi.create_state(
...     (
...         Automation.ANTE_POSTING,
...         Automation.BET_COLLECTION,
...         Automation.BLIND_OR_STRADDLE_POSTING,
...         Automation.CARD_BURNING,
...         Automation.HOLE_CARDS_SHOWING_OR_MUCKING,
...         Automation.HAND_KILLING,
...         Automation.CHIPS_PUSHING,
...         Automation.CHIPS_PULLING,
...     ),
...     True,
...     None,
...     (1, 2),
...     2,
...     4,
...     200,
...     4,
... )
>>> # Below shows the pre-flop dealings and actions.
>>> state.deal_hole('As4hJcKh')  # Bob*
State.HoleDealing(player_index=0, cards=(As, 4h, Jc, Kh), statuses=(False, False, False, False))
>>> state.deal_hole('3s5d7s8s')  # Carol*
State.HoleDealing(player_index=1, cards=(3s, 5d, 7s, 8s), statuses=(False, False, False, False))
>>> state.deal_hole('KsKdQsQd')  # Ted*
State.HoleDealing(player_index=2, cards=(Ks, Kd, Qs, Qd), statuses=(False, False, False, False))
>>> state.deal_hole('2s4c6dKc')  # Alice*
State.HoleDealing(player_index=3, cards=(2s, 4c, 6d, Kc), statuses=(False, False, False, False))
>>> state.fold()  # Ted
State.Folding(player_index=2)
>>> state.check_or_call()  # Alice
State.CheckingOrCalling(player_index=3, amount=2)
>>> state.check_or_call()  # Bob
State.CheckingOrCalling(player_index=0, amount=1)
>>> state.check_or_call()  # Carol
State.CheckingOrCalling(player_index=1, amount=0)
>>> # Below shows the first draw and actions.
>>> state.stand_pat_or_discard('JcKh')  # Bob*
State.StandingPatOrDiscarding(player_index=0, cards=(Jc, Kh))
>>> state.stand_pat_or_discard('7s8s')  # Carol*
State.StandingPatOrDiscarding(player_index=1, cards=(7s, 8s))
>>> state.stand_pat_or_discard('Kc')  # Alice*
State.StandingPatOrDiscarding(player_index=3, cards=(Kc,))
>>> state.deal_hole('TcJs')  # Bob*
State.HoleDealing(player_index=0, cards=(Tc, Js), statuses=(False, False))
>>> state.deal_hole('7cTh')  # Carol*
State.HoleDealing(player_index=1, cards=(7c, Th), statuses=(False, False))
>>> state.deal_hole('Qc')  # Alice*
State.HoleDealing(player_index=3, cards=(Qc,), statuses=(False,))
>>> state.check_or_call()  # Bob
State.CheckingOrCalling(player_index=0, amount=0)
>>> state.complete_bet_or_raise_to()  # Carol
State.CompletionBettingOrRaisingTo(player_index=1, amount=2)
>>> state.check_or_call()  # Alice
State.CheckingOrCalling(player_index=3, amount=2)
>>> state.check_or_call()  # Bob
State.CheckingOrCalling(player_index=0, amount=2)
>>> # Below shows the second draw and actions.
>>> state.stand_pat_or_discard('Js')  # Bob*
State.StandingPatOrDiscarding(player_index=0, cards=(Js,))
>>> state.stand_pat_or_discard()  # Carol*
State.StandingPatOrDiscarding(player_index=1, cards=())
>>> state.stand_pat_or_discard('Qc')  # Alice*
State.StandingPatOrDiscarding(player_index=3, cards=(Qc,))
>>> state.deal_hole('Ts')  # Bob*
State.HoleDealing(player_index=0, cards=(Ts,), statuses=(False,))
>>> state.deal_hole('9h')  # Alice*
State.HoleDealing(player_index=3, cards=(9h,), statuses=(False,))
>>> state.check_or_call()  # Bob
State.CheckingOrCalling(player_index=0, amount=0)
>>> state.complete_bet_or_raise_to()  # Carol
State.CompletionBettingOrRaisingTo(player_index=1, amount=4)
>>> state.complete_bet_or_raise_to()  # Alice
State.CompletionBettingOrRaisingTo(player_index=3, amount=8)
>>> state.fold()  # Bob
State.Folding(player_index=0)
>>> state.check_or_call()  # Carol
State.CheckingOrCalling(player_index=1, amount=4)
>>> # Below shows the third draw and actions.
>>> state.stand_pat_or_discard('Th')  # Carol*
State.StandingPatOrDiscarding(player_index=1, cards=(Th,))
>>> state.stand_pat_or_discard()  # Alice*
State.StandingPatOrDiscarding(player_index=3, cards=())
>>> state.deal_hole('8h')  # Carol*
State.HoleDealing(player_index=1, cards=(8h,), statuses=(False,))
>>> state.check_or_call()  # Carol
State.CheckingOrCalling(player_index=1, amount=0)
>>> state.complete_bet_or_raise_to()  # Alice
State.CompletionBettingOrRaisingTo(player_index=3, amount=4)
>>> state.check_or_call()  # Carol
State.CheckingOrCalling(player_index=1, amount=4)
>>> # Below show the final stacks.
>>> state.stacks
[196, 220, 200, 184]

There are more example hands in the unit tests. Please take a look at the repository to learn more.