LLM as Player

LLM as Player#

Here we use an LLM to make decisions.

  • This example includes utilities for estimating the cost of LLM calls.

  • To set expectations: the prompt and the information provided to the LLM are intentionally simple and could be expanded. Still, it’s a solid starting point, and the cost summary at the end of the notebook provides a sense of what it can cost to play a game.

  • The notebook contains the full log history of a 10 hand long game played using gpt-5-nano against 3 simple bots. The model untimately won the game with a few good moves and a number of questionable decisions as well.

#pip install openai ollama python-dotenv
import logging, sys, os

from openai import OpenAI
from openai.types.responses.response_usage import ResponseUsage
from dotenv import load_dotenv

from maverick import (
    Game,
    Player,
    ActionType,
    PlayerLike,
    PlayerAction,
    PlayerState,
)
from maverick.players import FoldBot, CallBot, AggressiveBot
from maverick.utils import estimate_holding_strength, find_highest_scoring_hand

_ = load_dotenv(override=True)
from collections import defaultdict

KNOWN_LLM_PRICES = {
    "gpt-5.2":    {"input": 1.75, "cached": 0.175, "output": 14.00},
    "gpt-5.1":    {"input": 1.25, "cached": 0.125, "output": 10.00},
    "gpt-5":      {"input": 1.25, "cached": 0.125, "output": 10.00},
    "gpt-5-mini": {"input": 0.25, "cached": 0.025, "output": 2.00},
    "gpt-5-nano": {"input": 0.05, "cached": 0.005, "output": 0.40},
}

LLM_PRICES = defaultdict(lambda: {"input": 0.0, "cached": 0.0, "output": 0.0}, KNOWN_LLM_PRICES)


def llm_call_cost(
    *,
    input_tokens: int,
    output_tokens: int,
    model: str,
    cached_input_tokens: int = 0,
) -> float:
    p = LLM_PRICES[model]
    uncached_input = max(0, input_tokens - cached_input_tokens)
    total_cost =  (
        uncached_input / 1_000_000 * p["input"]
        + cached_input_tokens / 1_000_000 * p["cached"]
        + output_tokens / 1_000_000 * p["output"]
    )
    return total_cost
SYSTEM_MESSAGE = """
You are an expert Texas Hold'em poker agent.

You will be given the current game state and a list of valid actions.
Your task: choose exactly one valid action and (if required) a numeric amount.

Action semantics:
- FOLD: provide no `amount`.
- CHECK: provide no `amount`.
- CALL: provide no `amount` (the engine infers call cost).
- BET: provide `amount` = the amount you transfer from your stack to the pot.
  The amount MUST be >= min_bet_amount.
- RAISE: provide `amount` = the amount you transfer from your stack to the pot.
  The amount MUST be >= min_raise_amount.
  
Card notation:
- Cards use standard short codes: Ah=Ace of hearts, Td=Ten of diamonds, 2c=Two of clubs.
- Ranks: 2-9,T,J,Q,K,A. Suits: h,d,c,s.
  
Glossary of terms:
- equity_vs_field = probability that your hand wins against all remaining players,
  estimated via Monte-Carlo simulation.
- best_current_hand = the strongest 5-card hand you can make now with your hole cards
  and the community cards.
- best_hand_type = the type of your best current hand (e.g., "FLUSH", "STRAIGHT").

Output requirements:
- Output MUST conform to the PlayerAction schema.
- Do NOT output explanations or any extra text.
- Never choose an action not present in valid_actions.
"""


class LLMPlayer(Player):
    
    def __init__(self, *args, api_key:str, model:str, **kwargs):
        super().__init__(*args, **kwargs)
        self.model = model
        self._reset_usage()
        if "_usage" in kwargs and kwargs["_usage"] is not None:
            self._usage = kwargs["_usage"]
        self._set_up_llm_client(api_key=api_key)
            
    def _set_up_llm_client(self, api_key: str) -> None:
        """Set up the LLM client with the provided API key."""
        if api_key.lower() == "ollama":
            OLLAMA_BASE_URL = "http://localhost:11434/v1"
            self.llm = OpenAI(base_url=OLLAMA_BASE_URL, api_key='ollama')
        else:
            self.llm = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
        
    def to_dict(self) -> dict:
        base = super().to_dict()
        base["_usage"] = self._usage
        return base
        
    def _reset_usage(self) -> None:
        """Reset the usage statistics for this player."""
        self._usage = {
            "input_tokens": 0,
            "output_tokens": 0,
            "total_tokens": 0,
            "input_tokens_details": {
                "cached_tokens": 0,
            },
            "output_tokens_details": {
                "reasoning_tokens": 0
            }
        }
    
    def _accumulate_usage(self, usage: ResponseUsage) -> None:
        self._usage["input_tokens"] += usage.input_tokens
        self._usage["output_tokens"] += usage.output_tokens
        self._usage["total_tokens"] += usage.total_tokens
        if usage.input_tokens_details:
            self._usage["input_tokens_details"]["cached_tokens"] += usage.input_tokens_details.cached_tokens
        if usage.output_tokens_details:
            self._usage["output_tokens_details"]["reasoning_tokens"] += usage.output_tokens_details.reasoning_tokens
        
    def _total_call_cost(self) -> float:
        """Calculate the total cost of all OpenAI calls made by this player."""
        input_tokens = self._usage["input_tokens"]
        output_tokens = self._usage["output_tokens"]
        cached_input_tokens = self._usage["input_tokens_details"]["cached_tokens"]
        
        return llm_call_cost(
            input_tokens=input_tokens,
            output_tokens=output_tokens,
            model=self.model,
            cached_input_tokens=cached_input_tokens,
        )
    
    def decide_action(
        self,
        *,
        game: Game,
        valid_actions: list["ActionType"],
        min_raise_amount: int,
        call_amount: int,
        min_bet_amount: int,
    ) -> "PlayerAction":

        # ---------- Debug prints (optional) ----------
        print(f"\n{self.name} is requested to take action.")
        print("     Current street:", game.state.street.name)
        print("     Button position:", game.state.button_position)
        print("     Pot:", game.state.pot)
        print("     Current bet:", game.state.current_bet)
        print("     Small blind:", game.state.small_blind)
        print("     Big blind:", game.state.big_blind)
        print("     Community cards:", [str(c) for c in game.state.community_cards])
        print("     Player stack:", self.state.stack)
        print("     Player current bet:", self.state.current_bet)
        print("     Amount to call:", call_amount)
        print("     Private cards:", [str(c) for c in self.state.holding.cards])
        print("     Valid actions:", [v.name for v in valid_actions])
        print("     Min raise amount:", min_raise_amount)
        print("     Min bet amount:", min_bet_amount)

        private_cards = self.state.holding.cards
        community_cards = game.state.community_cards

        # ---------- Hand evaluation (your existing utilities) ----------
        n_private_required=game.rules.showdown.hole_cards_required
        (
            strongest_hand, 
            strongest_hand_type, 
            strongest_hand_score
        ) = find_highest_scoring_hand(
            private_cards, 
            community_cards, 
            n_private=n_private_required
        )

        hand_prob = estimate_holding_strength(
            private_cards,
            community_cards=community_cards,
            n_simulations=1000,
            n_private=n_private_required,
            n_players=len(game.state.get_players_in_hand()),
        )

        print("     Best hand type:", strongest_hand_type.name)
        print("     Best hand score:", strongest_hand_score)
        print("     Equity (MC):", hand_prob)

        # ---------- Prompt (structured, minimal, unambiguous) ----------
        player_message = f"""
STATE (chip amounts are integers):

player:
  id: {self.id}
  name: {self.name}
  stack: {int(self.state.stack)}
  current_bet: {int(self.state.current_bet)}
  amount_to_call: {call_amount}
  hole_cards: {', '.join(card.code() for card in private_cards)}

table:
  street: {game.state.street.name}
  community_cards: {', '.join(card.code() for card in community_cards)}
  pot: {int(game.state.pot)}
  current_bet: {int(game.state.current_bet)}
  blinds: [{int(game.state.small_blind)}, {int(game.state.big_blind)}]
  players_in_hand: {len(game.state.get_players_in_hand())}

constraints:
  valid_actions: {[v.name for v in valid_actions]}
  min_raise_amount: {min_raise_amount}
  min_bet_amount: {min_bet_amount}

hand_eval:
  best_current_hand: {strongest_hand}
  best_hand_type: {strongest_hand_type.name}
  equity_vs_field: {hand_prob:.3f}

Choose your next action now.
If action is BET or RAISE, include integer amount.
Remember: for RAISE, amount must be >= min_raise_amount.
Remember: for BET, amount must be >= min_bet_amount.
"""

        # ---------- Call the model ----------
        try:
            response = self.llm.responses.parse(
                model=self.model,
                input=[
                    {"role": "system", "content": SYSTEM_MESSAGE},
                    {"role": "user", "content": player_message},
                ],
                text_format=PlayerAction,
            )
            self._accumulate_usage(response.usage)
            action: PlayerAction = response.output_parsed
            print(f"     LLMPlayer raw action output: {action}")  
        except Exception as e:
            logging.error(f"LLMPlayer {self.id} failed to decide action: {e}")
            action = PlayerAction(player_id=self.id, action_type=ActionType.FOLD)

        # ---------- Post-parse validation & self-heal ----------
        valid_set = set(valid_actions)
        if action.action_type not in valid_set:
            # safest fallback: CHECK if possible else FOLD
            logging.error("LLMPlayer produced invalid action type, applying fallback.")
            fallback = ActionType.CHECK if ActionType.CHECK in valid_set else ActionType.FOLD
            action = PlayerAction(player_id=self.id, action_type=fallback)

        needs_amount = action.action_type in {ActionType.BET, ActionType.RAISE}
        if needs_amount:
            # Missing amount -> pick a conservative default
            if getattr(action, "amount", None) is None:
                logging.error("LLMPlayer missing amount for bet/raise, applying default.")
                if action.action_type == ActionType.RAISE:
                    action.amount = min_raise_amount
                else:
                    action.amount = min_bet_amount
            # Normalize to int and clamp
            action.amount = int(action.amount)

            # Enforce min raise amount
            if action.action_type == ActionType.RAISE and action.amount < min_raise_amount:
                action.amount = min_raise_amount
                
            # Enforce min bet amount
            if action.action_type == ActionType.BET and action.amount < min_bet_amount:
                action.amount = min_bet_amount

            # No negative or zero bet/raise
            if action.amount <= 0:
                logging.error("LLMPlayer produced non-positive amount for bet/raise, applying fallback.")
                fallback = ActionType.CHECK if ActionType.CHECK in valid_set else ActionType.FOLD
                action = PlayerAction(player_id=self.id, action_type=fallback)

            # Can't wager more than stack in this action (if your engine allows all-in as exact stack, keep this)
            if action.amount > int(self.state.stack):
                logging.error("LLMPlayer produced amount exceeding stack, clamping to stack.")
                action.amount = int(self.state.stack)

        else:
            # Strip stray amount for non-sizing actions
            if hasattr(action, "amount") and action.amount is not None:
                logging.error("LLMPlayer produced amount for non-sizing action, stripping.")
                action.amount = None

        print(f"     Player decides to {action.action_type.name}"
              f"{'' if getattr(action, 'amount', None) is None else f' ({action.amount})'}.\n")
        
        sys.stdout.flush()
        return action
llm_player = LLMPlayer(
    name="LLM", 
    state=PlayerState(stack=1000),
    api_key=os.getenv('OPENAI_API_KEY'),
    model="gpt-5-nano",
)

# llm_player = LLMPlayer(
#     name="LLM", 
#     state=PlayerState(stack=1000),
#     api_key="ollama",
#     model="deepseek-r1:8b",
# )

response = llm_player.llm.responses.parse(
    model=llm_player.model, 
    input=[{"role": "user", "content": "Tell me a poker joke."}]
)
response.output_text, response.usage.total_tokens
('Yes, I’m ready. Want to play Texas Hold’em? We can do a quick heads-up round with 1000 chips each, blinds 1/2. If that works, say “start” and I’ll deal the first hand. You’ll respond with actions like: check, bet <amount>, call, raise <amount>, or fold.',
 1691)
llm_call_cost(
    input_tokens=response.usage.input_tokens,
    output_tokens=response.usage.output_tokens,
    model=llm_player.model,
    cached_input_tokens=response.usage.input_tokens_details.cached_tokens if response.usage.input_tokens_details else 0,
)
0.0006718500000000001
# Configure logging such that we only get the log messages of the game
logging.basicConfig(level=logging.DEBUG, format="%(name)s: %(message)s")
logging.getLogger().setLevel(logging.WARNING)  # Set root logger to WARNING
logging.getLogger("maverick").setLevel(logging.DEBUG)  # Only maverick logs at DEBUG

# Create game with blinds
game = Game(small_blind=10, big_blind=20, max_hands=10)

# Create and add players with different strategies
players: list[PlayerLike] = [
    CallBot(name="CallBot", state=PlayerState(stack=1000)),
    AggressiveBot(name="AggroBot", state=PlayerState(stack=1000)),
    FoldBot(name="FoldBot", state=PlayerState(stack=1000)),
    llm_player,
]

for player in players:
    game.add_player(player)

game.start()
maverick: Player CallBot joined the game.
maverick: Player AggroBot joined the game.
maverick: Player FoldBot joined the game.
maverick: Player LLM joined the game.
maverick: Game started.

maverick: ============================== Hand 1 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: FoldBot
maverick: PRE_FLOP | Posting small blind of 10 by player LLM. Remaining stack: 990
maverick: PRE_FLOP | Posting big blind of 20 by player CallBot. Remaining stack: 980
maverick: PRE_FLOP | Player AggroBot raises by 40 chips to total bet 40. Remaining stack: 960.
maverick: PRE_FLOP | Current pot: 70 | Current bet: 40
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 70 | Current bet: 40
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 2
     Pot: 70
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 990
     Player current bet: 10
     Amount to call: 30
     Private cards: ['3♠', 'K♥']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 50
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.1303
     Equity (MC): 0.315
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: PRE_FLOP | Player LLM calls with amount 30. Remaining stack: 960.
maverick: PRE_FLOP | Current pot: 100 | Current bet: 40
maverick: PRE_FLOP | Player CallBot calls with amount 20. Remaining stack: 960.
maverick: PRE_FLOP | Current pot: 120 | Current bet: 40
maverick: PRE_FLOP | Betting round complete

maverick: FLOP | Dealt flop. Community cards: ['9♠', '5♣', '8♥']
LLM is requested to take action.
     Current street: FLOP
     Button position: 2
     Pot: 120
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['9♠', '5♣', '8♥']
     Player stack: 960
     Player current bet: 0
     Amount to call: 0
     Private cards: ['3♠', 'K♥']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.1309080503
     Equity (MC): 0.165
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.FOLD: 1> amount=None
     Player decides to FOLD.
maverick: FLOP | Player LLM folds.
maverick: FLOP | Current pot: 120 | Current bet: 0
maverick: FLOP | Player CallBot checks.
maverick: FLOP | Current pot: 120 | Current bet: 0
maverick: FLOP | Player AggroBot bets amount 40. Remaining stack: 920.
maverick: FLOP | Current pot: 160 | Current bet: 40
maverick: FLOP | Player CallBot calls with amount 40. Remaining stack: 920.
maverick: FLOP | Current pot: 200 | Current bet: 40
maverick: FLOP | Betting round complete

maverick: TURN | Dealt turn. Community cards: ['9♠', '5♣', '8♥', '3♣']
maverick: TURN | Player CallBot checks.
maverick: TURN | Current pot: 200 | Current bet: 0
maverick: TURN | Player AggroBot bets amount 40. Remaining stack: 880.
maverick: TURN | Current pot: 240 | Current bet: 40
maverick: TURN | Player CallBot calls with amount 40. Remaining stack: 880.
maverick: TURN | Current pot: 280 | Current bet: 40
maverick: TURN | Betting round complete

maverick: RIVER | Dealt river. Community cards: ['9♠', '5♣', '8♥', '3♣', 'Q♦']
maverick: RIVER | Player CallBot checks.
maverick: RIVER | Current pot: 280 | Current bet: 0
maverick: RIVER | Player AggroBot bets amount 40. Remaining stack: 840.
maverick: RIVER | Current pot: 320 | Current bet: 40
maverick: RIVER | Player CallBot calls with amount 40. Remaining stack: 840.
maverick: RIVER | Current pot: 360 | Current bet: 40
maverick: RIVER | Betting round complete

maverick: SHOWDOWN | Player CallBot has holding 7♦ 10♥ at showdown,
maverick: SHOWDOWN | Player CallBot has hand HIGH_CARD with cards ['7♦', '10♥', '9♠', '8♥', 'Q♦'] (score: 100.12101)
maverick: SHOWDOWN | Player AggroBot has holding 9♥ A♦ at showdown,
maverick: SHOWDOWN | Player AggroBot has hand PAIR with cards ['9♥', 'A♦', '9♠', '8♥', 'Q♦'] (score: 209.14121)
maverick: SHOWDOWN | Player AggroBot wins 360 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: ============================== Hand 2 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: LLM
maverick: PRE_FLOP | Posting small blind of 10 by player CallBot. Remaining stack: 830
maverick: PRE_FLOP | Posting big blind of 20 by player AggroBot. Remaining stack: 1180
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 30 | Current bet: 20
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 3
     Pot: 30
     Current bet: 20
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 960
     Player current bet: 0
     Amount to call: 20
     Private cards: ['9♥', 'J♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 40
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.1109
     Equity (MC): 0.4
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: PRE_FLOP | Player LLM calls with amount 20. Remaining stack: 940.
maverick: PRE_FLOP | Current pot: 50 | Current bet: 20
maverick: PRE_FLOP | Player CallBot calls with amount 10. Remaining stack: 820.
maverick: PRE_FLOP | Current pot: 60 | Current bet: 20
maverick: PRE_FLOP | Player AggroBot raises by 20 chips to total bet 40. Remaining stack: 1160.
maverick: PRE_FLOP | Current pot: 80 | Current bet: 40
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 3
     Pot: 80
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 940
     Player current bet: 20
     Amount to call: 20
     Private cards: ['9♥', 'J♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 40
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.1109
     Equity (MC): 0.371
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: PRE_FLOP | Player LLM calls with amount 20. Remaining stack: 920.
maverick: PRE_FLOP | Current pot: 100 | Current bet: 40
maverick: PRE_FLOP | Player CallBot calls with amount 20. Remaining stack: 800.
maverick: PRE_FLOP | Current pot: 120 | Current bet: 40
maverick: PRE_FLOP | Betting round complete

maverick: FLOP | Dealt flop. Community cards: ['10♥', 'Q♠', '6♦']
maverick: FLOP | Player CallBot checks.
maverick: FLOP | Current pot: 120 | Current bet: 0
maverick: FLOP | Player AggroBot bets amount 40. Remaining stack: 1120.
maverick: FLOP | Current pot: 160 | Current bet: 40
LLM is requested to take action.
     Current street: FLOP
     Button position: 3
     Pot: 160
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: ['10♥', 'Q♠', '6♦']
     Player stack: 920
     Player current bet: 0
     Amount to call: 40
     Private cards: ['9♥', 'J♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 80
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.1211100906
     Equity (MC): 0.431
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: FLOP | Player LLM calls with amount 40. Remaining stack: 880.
maverick: FLOP | Current pot: 200 | Current bet: 40
maverick: FLOP | Player CallBot calls with amount 40. Remaining stack: 760.
maverick: FLOP | Current pot: 240 | Current bet: 40
maverick: FLOP | Betting round complete

maverick: TURN | Dealt turn. Community cards: ['10♥', 'Q♠', '6♦', 'J♦']
maverick: TURN | Player CallBot checks.
maverick: TURN | Current pot: 240 | Current bet: 0
maverick: TURN | Player AggroBot bets amount 40. Remaining stack: 1080.
maverick: TURN | Current pot: 280 | Current bet: 40
LLM is requested to take action.
     Current street: TURN
     Button position: 3
     Pot: 280
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: ['10♥', 'Q♠', '6♦', 'J♦']
     Player stack: 880
     Player current bet: 0
     Amount to call: 40
     Private cards: ['9♥', 'J♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 80
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 211.121009
     Equity (MC): 0.554
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: TURN | Player LLM calls with amount 40. Remaining stack: 840.
maverick: TURN | Current pot: 320 | Current bet: 40
maverick: TURN | Player CallBot calls with amount 40. Remaining stack: 720.
maverick: TURN | Current pot: 360 | Current bet: 40
maverick: TURN | Betting round complete

maverick: RIVER | Dealt river. Community cards: ['10♥', 'Q♠', '6♦', 'J♦', '3♠']
maverick: RIVER | Player CallBot checks.
maverick: RIVER | Current pot: 360 | Current bet: 0
maverick: RIVER | Player AggroBot bets amount 40. Remaining stack: 1040.
maverick: RIVER | Current pot: 400 | Current bet: 40
LLM is requested to take action.
     Current street: RIVER
     Button position: 3
     Pot: 400
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: ['10♥', 'Q♠', '6♦', 'J♦', '3♠']
     Player stack: 840
     Player current bet: 0
     Amount to call: 40
     Private cards: ['9♥', 'J♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 80
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 211.121009
     Equity (MC): 0.581
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: RIVER | Player LLM calls with amount 40. Remaining stack: 800.
maverick: RIVER | Current pot: 440 | Current bet: 40
maverick: RIVER | Player CallBot calls with amount 40. Remaining stack: 680.
maverick: RIVER | Current pot: 480 | Current bet: 40
maverick: RIVER | Betting round complete

maverick: SHOWDOWN | Player CallBot has holding A♠ 5♦ at showdown,
maverick: SHOWDOWN | Player CallBot has hand HIGH_CARD with cards ['A♠', '10♥', 'Q♠', '6♦', 'J♦'] (score: 100.14121)
maverick: SHOWDOWN | Player AggroBot has holding K♦ 9♦ at showdown,
maverick: SHOWDOWN | Player AggroBot has hand STRAIGHT with cards ['K♦', '9♦', '10♥', 'Q♠', 'J♦'] (score: 500.13)
maverick: SHOWDOWN | Player LLM has holding 9♥ J♣ at showdown,
maverick: SHOWDOWN | Player LLM has hand PAIR with cards ['9♥', 'J♣', '10♥', 'Q♠', 'J♦'] (score: 211.12101)
maverick: SHOWDOWN | Player AggroBot wins 480 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: ============================== Hand 3 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: CallBot
maverick: PRE_FLOP | Posting small blind of 10 by player AggroBot. Remaining stack: 1510
maverick: PRE_FLOP | Posting big blind of 20 by player FoldBot. Remaining stack: 980
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 0
     Pot: 30
     Current bet: 20
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 800
     Player current bet: 0
     Amount to call: 20
     Private cards: ['Q♥', '6♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 40
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.1206
     Equity (MC): 0.251
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.FOLD: 1> amount=None
     Player decides to FOLD.
maverick: PRE_FLOP | Player LLM folds.
maverick: PRE_FLOP | Current pot: 30 | Current bet: 20
maverick: PRE_FLOP | Player CallBot calls with amount 20. Remaining stack: 660.
maverick: PRE_FLOP | Current pot: 50 | Current bet: 20
maverick: PRE_FLOP | Player AggroBot raises by 30 chips to total bet 40. Remaining stack: 1480.
maverick: PRE_FLOP | Current pot: 80 | Current bet: 40
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 80 | Current bet: 40
maverick: PRE_FLOP | Player CallBot calls with amount 20. Remaining stack: 640.
maverick: PRE_FLOP | Current pot: 100 | Current bet: 40
maverick: PRE_FLOP | Betting round complete

maverick: FLOP | Dealt flop. Community cards: ['9♣', '5♥', 'K♥']
maverick: FLOP | Player AggroBot bets amount 40. Remaining stack: 1440.
maverick: FLOP | Current pot: 140 | Current bet: 40
maverick: FLOP | Player CallBot calls with amount 40. Remaining stack: 600.
maverick: FLOP | Current pot: 180 | Current bet: 40
maverick: FLOP | Betting round complete

maverick: TURN | Dealt turn. Community cards: ['9♣', '5♥', 'K♥', 'K♣']
maverick: TURN | Player AggroBot bets amount 40. Remaining stack: 1400.
maverick: TURN | Current pot: 220 | Current bet: 40
maverick: TURN | Player CallBot calls with amount 40. Remaining stack: 560.
maverick: TURN | Current pot: 260 | Current bet: 40
maverick: TURN | Betting round complete

maverick: RIVER | Dealt river. Community cards: ['9♣', '5♥', 'K♥', 'K♣', '2♣']
maverick: RIVER | Player AggroBot bets amount 40. Remaining stack: 1360.
maverick: RIVER | Current pot: 300 | Current bet: 40
maverick: RIVER | Player CallBot calls with amount 40. Remaining stack: 520.
maverick: RIVER | Current pot: 340 | Current bet: 40
maverick: RIVER | Betting round complete

maverick: SHOWDOWN | Player CallBot has holding J♠ A♣ at showdown,
maverick: SHOWDOWN | Player CallBot has hand PAIR with cards ['J♠', 'A♣', '9♣', 'K♥', 'K♣'] (score: 213.14111)
maverick: SHOWDOWN | Player AggroBot has holding K♠ 6♦ at showdown,
maverick: SHOWDOWN | Player AggroBot has hand THREE_OF_A_KIND with cards ['K♠', '6♦', '9♣', 'K♥', 'K♣'] (score: 413.0906)
maverick: SHOWDOWN | Player AggroBot wins 340 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: ============================== Hand 4 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: AggroBot
maverick: PRE_FLOP | Posting small blind of 10 by player FoldBot. Remaining stack: 970
maverick: PRE_FLOP | Posting big blind of 20 by player LLM. Remaining stack: 780
maverick: PRE_FLOP | Player CallBot calls with amount 20. Remaining stack: 500.
maverick: PRE_FLOP | Current pot: 50 | Current bet: 20
maverick: PRE_FLOP | Player AggroBot raises by 40 chips to total bet 40. Remaining stack: 1660.
maverick: PRE_FLOP | Current pot: 90 | Current bet: 40
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 90 | Current bet: 40
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 1
     Pot: 90
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 780
     Player current bet: 20
     Amount to call: 20
     Private cards: ['7♥', '2♥']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 40
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.0702
     Equity (MC): 0.261
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: PRE_FLOP | Player LLM calls with amount 20. Remaining stack: 760.
maverick: PRE_FLOP | Current pot: 110 | Current bet: 40
maverick: PRE_FLOP | Player CallBot calls with amount 20. Remaining stack: 480.
maverick: PRE_FLOP | Current pot: 130 | Current bet: 40
maverick: PRE_FLOP | Betting round complete

maverick: FLOP | Dealt flop. Community cards: ['3♥', 'J♥', '8♥']
LLM is requested to take action.
     Current street: FLOP
     Button position: 1
     Pot: 130
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['3♥', 'J♥', '8♥']
     Player stack: 760
     Player current bet: 0
     Amount to call: 0
     Private cards: ['7♥', '2♥']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: FLUSH
     Best hand score: 600.1108070302
     Equity (MC): 0.829
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.BET: 4> amount=60
     Player decides to BET (60).
maverick: FLOP | Player LLM bets amount 60. Remaining stack: 700.
maverick: FLOP | Current pot: 190 | Current bet: 60
maverick: FLOP | Player CallBot calls with amount 60. Remaining stack: 420.
maverick: FLOP | Current pot: 250 | Current bet: 60
maverick: FLOP | Player AggroBot raises by 120 chips to total bet 120. Remaining stack: 1540.
maverick: FLOP | Current pot: 370 | Current bet: 120
LLM is requested to take action.
     Current street: FLOP
     Button position: 1
     Pot: 370
     Current bet: 120
     Small blind: 10
     Big blind: 20
     Community cards: ['3♥', 'J♥', '8♥']
     Player stack: 700
     Player current bet: 60
     Amount to call: 60
     Private cards: ['7♥', '2♥']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 120
     Min bet amount: 20
     Best hand type: FLUSH
     Best hand score: 600.1108070302
     Equity (MC): 0.841
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: FLOP | Player LLM calls with amount 60. Remaining stack: 640.
maverick: FLOP | Current pot: 430 | Current bet: 120
maverick: FLOP | Player CallBot calls with amount 60. Remaining stack: 360.
maverick: FLOP | Current pot: 490 | Current bet: 120
maverick: FLOP | Betting round complete

maverick: TURN | Dealt turn. Community cards: ['3♥', 'J♥', '8♥', 'A♥']
LLM is requested to take action.
     Current street: TURN
     Button position: 1
     Pot: 490
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['3♥', 'J♥', '8♥', 'A♥']
     Player stack: 640
     Player current bet: 0
     Amount to call: 0
     Private cards: ['7♥', '2♥']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: FLUSH
     Best hand score: 600.1411080703
     Equity (MC): 0.685
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.BET: 4> amount=250
     Player decides to BET (250).
maverick: TURN | Player LLM bets amount 250. Remaining stack: 390.
maverick: TURN | Current pot: 740 | Current bet: 250
maverick: TURN | Player CallBot calls with amount 250. Remaining stack: 110.
maverick: TURN | Current pot: 990 | Current bet: 250
maverick: TURN | Player AggroBot raises by 500 chips to total bet 500. Remaining stack: 1040.
maverick: TURN | Current pot: 1490 | Current bet: 500
LLM is requested to take action.
     Current street: TURN
     Button position: 1
     Pot: 1490
     Current bet: 500
     Small blind: 10
     Big blind: 20
     Community cards: ['3♥', 'J♥', '8♥', 'A♥']
     Player stack: 390
     Player current bet: 250
     Amount to call: 250
     Private cards: ['7♥', '2♥']
     Valid actions: ['FOLD', 'CALL', 'ALL_IN']
     Min raise amount: 500
     Min bet amount: 20
     Best hand type: FLUSH
     Best hand score: 600.1411080703
     Equity (MC): 0.678
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: TURN | Player LLM calls with amount 250. Remaining stack: 140.
maverick: TURN | Current pot: 1740 | Current bet: 500
maverick: TURN | Player CallBot calls with amount 110. Remaining stack: 0.
maverick: TURN | Current pot: 1850 | Current bet: 500
maverick: TURN | Betting round complete

maverick: RIVER | Dealt river. Community cards: ['3♥', 'J♥', '8♥', 'A♥', '3♣']
LLM is requested to take action.
     Current street: RIVER
     Button position: 1
     Pot: 1850
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['3♥', 'J♥', '8♥', 'A♥', '3♣']
     Player stack: 140
     Player current bet: 0
     Amount to call: 0
     Private cards: ['7♥', '2♥']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: FLUSH
     Best hand score: 600.1411080703
     Equity (MC): 0.641
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CHECK: 2> amount=None
     Player decides to CHECK.
maverick: RIVER | Player LLM checks.
maverick: RIVER | Current pot: 1850 | Current bet: 0
maverick: RIVER | Player AggroBot bets amount 40. Remaining stack: 1000.
maverick: RIVER | Current pot: 1890 | Current bet: 40
LLM is requested to take action.
     Current street: RIVER
     Button position: 1
     Pot: 1890
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: ['3♥', 'J♥', '8♥', 'A♥', '3♣']
     Player stack: 140
     Player current bet: 0
     Amount to call: 40
     Private cards: ['7♥', '2♥']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 80
     Min bet amount: 20
     Best hand type: FLUSH
     Best hand score: 600.1411080703
     Equity (MC): 0.626
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: RIVER | Player LLM calls with amount 40. Remaining stack: 100.
maverick: RIVER | Current pot: 1930 | Current bet: 40
maverick: RIVER | Betting round complete

maverick: SHOWDOWN | Player CallBot has holding 9♦ A♣ at showdown,
maverick: SHOWDOWN | Player CallBot has hand TWO_PAIR with cards ['A♣', '3♥', 'J♥', 'A♥', '3♣'] (score: 314.0311)
maverick: SHOWDOWN | Player AggroBot has holding 5♦ 8♦ at showdown,
maverick: SHOWDOWN | Player AggroBot has hand TWO_PAIR with cards ['8♦', '3♥', '8♥', 'A♥', '3♣'] (score: 308.0314)
maverick: SHOWDOWN | Player LLM has holding 7♥ 2♥ at showdown,
maverick: SHOWDOWN | Player LLM has hand FLUSH with cards ['7♥', '3♥', 'J♥', '8♥', 'A♥'] (score: 600.14111)
maverick: SHOWDOWN | Player LLM wins 1930 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: ============================== Hand 5 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: LLM
maverick: PRE_FLOP | Posting small blind of 10 by player AggroBot. Remaining stack: 990
maverick: PRE_FLOP | Posting big blind of 20 by player FoldBot. Remaining stack: 950
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 2
     Pot: 30
     Current bet: 20
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 2030
     Player current bet: 0
     Amount to call: 20
     Private cards: ['Q♣', '10♦']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 40
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.121
     Equity (MC): 0.397
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.FOLD: 1> amount=None
     Player decides to FOLD.
maverick: PRE_FLOP | Player LLM folds.
maverick: PRE_FLOP | Current pot: 30 | Current bet: 20
maverick: PRE_FLOP | Player AggroBot raises by 30 chips to total bet 40. Remaining stack: 960.
maverick: PRE_FLOP | Current pot: 60 | Current bet: 40
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 60 | Current bet: 40
maverick: PRE_FLOP | Betting round complete

maverick: SHOWDOWN | Player AggroBot wins 60 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: ============================== Hand 6 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: AggroBot
maverick: PRE_FLOP | Posting small blind of 10 by player FoldBot. Remaining stack: 940
maverick: PRE_FLOP | Posting big blind of 20 by player LLM. Remaining stack: 2010
maverick: PRE_FLOP | Player AggroBot raises by 40 chips to total bet 40. Remaining stack: 980.
maverick: PRE_FLOP | Current pot: 70 | Current bet: 40
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 70 | Current bet: 40
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 0
     Pot: 70
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 2010
     Player current bet: 20
     Amount to call: 20
     Private cards: ['3♦', '4♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 40
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.0403
     Equity (MC): 0.395
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: PRE_FLOP | Player LLM calls with amount 20. Remaining stack: 1990.
maverick: PRE_FLOP | Current pot: 90 | Current bet: 40
maverick: PRE_FLOP | Betting round complete

maverick: FLOP | Dealt flop. Community cards: ['K♦', '3♠', '2♣']
LLM is requested to take action.
     Current street: FLOP
     Button position: 0
     Pot: 90
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['K♦', '3♠', '2♣']
     Player stack: 1990
     Player current bet: 0
     Amount to call: 0
     Private cards: ['3♦', '4♣']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 203.130402
     Equity (MC): 0.652
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.BET: 4> amount=40
     Player decides to BET (40).
maverick: FLOP | Player LLM bets amount 40. Remaining stack: 1950.
maverick: FLOP | Current pot: 130 | Current bet: 40
maverick: FLOP | Player AggroBot raises by 80 chips to total bet 80. Remaining stack: 900.
maverick: FLOP | Current pot: 210 | Current bet: 80
LLM is requested to take action.
     Current street: FLOP
     Button position: 0
     Pot: 210
     Current bet: 80
     Small blind: 10
     Big blind: 20
     Community cards: ['K♦', '3♠', '2♣']
     Player stack: 1950
     Player current bet: 40
     Amount to call: 40
     Private cards: ['3♦', '4♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 80
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 203.130402
     Equity (MC): 0.617
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: FLOP | Player LLM calls with amount 40. Remaining stack: 1910.
maverick: FLOP | Current pot: 250 | Current bet: 80
maverick: FLOP | Betting round complete

maverick: TURN | Dealt turn. Community cards: ['K♦', '3♠', '2♣', '9♠']
LLM is requested to take action.
     Current street: TURN
     Button position: 0
     Pot: 250
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['K♦', '3♠', '2♣', '9♠']
     Player stack: 1910
     Player current bet: 0
     Amount to call: 0
     Private cards: ['3♦', '4♣']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 203.130904
     Equity (MC): 0.586
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CHECK: 2> amount=None
     Player decides to CHECK.
maverick: TURN | Player LLM checks.
maverick: TURN | Current pot: 250 | Current bet: 0
maverick: TURN | Player AggroBot bets amount 40. Remaining stack: 860.
maverick: TURN | Current pot: 290 | Current bet: 40
LLM is requested to take action.
     Current street: TURN
     Button position: 0
     Pot: 290
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: ['K♦', '3♠', '2♣', '9♠']
     Player stack: 1910
     Player current bet: 0
     Amount to call: 40
     Private cards: ['3♦', '4♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 80
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 203.130904
     Equity (MC): 0.57
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: TURN | Player LLM calls with amount 40. Remaining stack: 1870.
maverick: TURN | Current pot: 330 | Current bet: 40
maverick: TURN | Betting round complete

maverick: RIVER | Dealt river. Community cards: ['K♦', '3♠', '2♣', '9♠', '5♥']
LLM is requested to take action.
     Current street: RIVER
     Button position: 0
     Pot: 330
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['K♦', '3♠', '2♣', '9♠', '5♥']
     Player stack: 1870
     Player current bet: 0
     Amount to call: 0
     Private cards: ['3♦', '4♣']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 203.130905
     Equity (MC): 0.484
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CHECK: 2> amount=None
     Player decides to CHECK.
maverick: RIVER | Player LLM checks.
maverick: RIVER | Current pot: 330 | Current bet: 0
maverick: RIVER | Player AggroBot bets amount 40. Remaining stack: 820.
maverick: RIVER | Current pot: 370 | Current bet: 40
LLM is requested to take action.
     Current street: RIVER
     Button position: 0
     Pot: 370
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: ['K♦', '3♠', '2♣', '9♠', '5♥']
     Player stack: 1870
     Player current bet: 0
     Amount to call: 40
     Private cards: ['3♦', '4♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 80
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 203.130905
     Equity (MC): 0.515
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: RIVER | Player LLM calls with amount 40. Remaining stack: 1830.
maverick: RIVER | Current pot: 410 | Current bet: 40
maverick: RIVER | Betting round complete

maverick: SHOWDOWN | Player AggroBot has holding 4♠ 9♣ at showdown,
maverick: SHOWDOWN | Player AggroBot has hand PAIR with cards ['4♠', '9♣', 'K♦', '9♠', '5♥'] (score: 209.1305)
maverick: SHOWDOWN | Player LLM has holding 3♦ 4♣ at showdown,
maverick: SHOWDOWN | Player LLM has hand PAIR with cards ['3♦', 'K♦', '3♠', '9♠', '5♥'] (score: 203.13091)
maverick: SHOWDOWN | Player AggroBot wins 410 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: ============================== Hand 7 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: FoldBot
maverick: PRE_FLOP | Posting small blind of 10 by player LLM. Remaining stack: 1820
maverick: PRE_FLOP | Posting big blind of 20 by player AggroBot. Remaining stack: 1210
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 30 | Current bet: 20
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 1
     Pot: 30
     Current bet: 20
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 1820
     Player current bet: 10
     Amount to call: 10
     Private cards: ['4♥', 'A♥']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 30
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.1404
     Equity (MC): 0.608
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: PRE_FLOP | Player LLM calls with amount 10. Remaining stack: 1810.
maverick: PRE_FLOP | Current pot: 40 | Current bet: 20
maverick: PRE_FLOP | Player AggroBot raises by 20 chips to total bet 40. Remaining stack: 1190.
maverick: PRE_FLOP | Current pot: 60 | Current bet: 40
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 1
     Pot: 60
     Current bet: 40
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 1810
     Player current bet: 20
     Amount to call: 20
     Private cards: ['4♥', 'A♥']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 40
     Min bet amount: 20
     Best hand type: HIGH_CARD
     Best hand score: 100.1404
     Equity (MC): 0.609
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: PRE_FLOP | Player LLM calls with amount 20. Remaining stack: 1790.
maverick: PRE_FLOP | Current pot: 80 | Current bet: 40
maverick: PRE_FLOP | Betting round complete

maverick: FLOP | Dealt flop. Community cards: ['9♣', 'A♣', '2♥']
LLM is requested to take action.
     Current street: FLOP
     Button position: 1
     Pot: 80
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['9♣', 'A♣', '2♥']
     Player stack: 1790
     Player current bet: 0
     Amount to call: 0
     Private cards: ['4♥', 'A♥']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 214.090402
     Equity (MC): 0.854
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.BET: 4> amount=40
     Player decides to BET (40).
maverick: FLOP | Player LLM bets amount 40. Remaining stack: 1750.
maverick: FLOP | Current pot: 120 | Current bet: 40
maverick: FLOP | Player AggroBot raises by 80 chips to total bet 80. Remaining stack: 1110.
maverick: FLOP | Current pot: 200 | Current bet: 80
LLM is requested to take action.
     Current street: FLOP
     Button position: 1
     Pot: 200
     Current bet: 80
     Small blind: 10
     Big blind: 20
     Community cards: ['9♣', 'A♣', '2♥']
     Player stack: 1750
     Player current bet: 40
     Amount to call: 40
     Private cards: ['4♥', 'A♥']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 80
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 214.090402
     Equity (MC): 0.847
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: FLOP | Player LLM calls with amount 40. Remaining stack: 1710.
maverick: FLOP | Current pot: 240 | Current bet: 80
maverick: FLOP | Betting round complete

maverick: TURN | Dealt turn. Community cards: ['9♣', 'A♣', '2♥', '10♥']
LLM is requested to take action.
     Current street: TURN
     Button position: 1
     Pot: 240
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['9♣', 'A♣', '2♥', '10♥']
     Player stack: 1710
     Player current bet: 0
     Amount to call: 0
     Private cards: ['4♥', 'A♥']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 214.100904
     Equity (MC): 0.898
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.BET: 4> amount=80
     Player decides to BET (80).
maverick: TURN | Player LLM bets amount 80. Remaining stack: 1630.
maverick: TURN | Current pot: 320 | Current bet: 80
maverick: TURN | Player AggroBot raises by 160 chips to total bet 160. Remaining stack: 950.
maverick: TURN | Current pot: 480 | Current bet: 160
LLM is requested to take action.
     Current street: TURN
     Button position: 1
     Pot: 480
     Current bet: 160
     Small blind: 10
     Big blind: 20
     Community cards: ['9♣', 'A♣', '2♥', '10♥']
     Player stack: 1630
     Player current bet: 80
     Amount to call: 80
     Private cards: ['4♥', 'A♥']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 160
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 214.100904
     Equity (MC): 0.892
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: TURN | Player LLM calls with amount 80. Remaining stack: 1550.
maverick: TURN | Current pot: 560 | Current bet: 160
maverick: TURN | Betting round complete

maverick: RIVER | Dealt river. Community cards: ['9♣', 'A♣', '2♥', '10♥', '6♥']
LLM is requested to take action.
     Current street: RIVER
     Button position: 1
     Pot: 560
     Current bet: 0
     Small blind: 10
     Big blind: 20
     Community cards: ['9♣', 'A♣', '2♥', '10♥', '6♥']
     Player stack: 1550
     Player current bet: 0
     Amount to call: 0
     Private cards: ['4♥', 'A♥']
     Valid actions: ['FOLD', 'CHECK', 'BET', 'ALL_IN']
     Min raise amount: 0
     Min bet amount: 20
     Best hand type: FLUSH
     Best hand score: 600.1410060402
     Equity (MC): 1.0
root: LLMPlayer produced amount for non-sizing action, stripping.
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.ALL_IN: 6> amount=1550
     Player decides to ALL_IN.
maverick: RIVER | Player LLM goes all-in with 1550 chips.
maverick: RIVER | Current pot: 2110 | Current bet: 1550
maverick: RIVER | Player AggroBot calls with amount 950. Remaining stack: 0.
maverick: RIVER | Current pot: 3060 | Current bet: 1550
maverick: RIVER | Betting round complete

maverick: SHOWDOWN | Player AggroBot has holding 3♠ 2♣ at showdown,
maverick: SHOWDOWN | Player AggroBot has hand PAIR with cards ['2♣', '9♣', 'A♣', '2♥', '10♥'] (score: 202.14101)
maverick: SHOWDOWN | Player LLM has holding 4♥ A♥ at showdown,
maverick: SHOWDOWN | Player LLM has hand FLUSH with cards ['4♥', 'A♥', '2♥', '10♥', '6♥'] (score: 600.14101)
maverick: SHOWDOWN | Player LLM wins 3060 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: ============================== Hand 8 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: FoldBot
maverick: PRE_FLOP | Posting small blind of 10 by player FoldBot. Remaining stack: 930
maverick: PRE_FLOP | Posting big blind of 20 by player LLM. Remaining stack: 3040
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 30 | Current bet: 20
maverick: PRE_FLOP | Betting round complete

maverick: SHOWDOWN | Player LLM wins 30 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: ============================== Hand 9 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: LLM
maverick: PRE_FLOP | Posting small blind of 10 by player LLM. Remaining stack: 3060
maverick: PRE_FLOP | Posting big blind of 20 by player FoldBot. Remaining stack: 910
LLM is requested to take action.
     Current street: PRE_FLOP
     Button position: 1
     Pot: 30
     Current bet: 20
     Small blind: 10
     Big blind: 20
     Community cards: []
     Player stack: 3060
     Player current bet: 10
     Amount to call: 10
     Private cards: ['7♠', '7♣']
     Valid actions: ['FOLD', 'CALL', 'RAISE', 'ALL_IN']
     Min raise amount: 30
     Min bet amount: 20
     Best hand type: PAIR
     Best hand score: 207
     Equity (MC): 0.687
     LLMPlayer raw action output: player_id='019b39c67442454a866e029bee296b8c' action_type=<ActionType.CALL: 3> amount=None
     Player decides to CALL.
maverick: PRE_FLOP | Player LLM calls with amount 10. Remaining stack: 3050.
maverick: PRE_FLOP | Current pot: 40 | Current bet: 20
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 40 | Current bet: 20
maverick: PRE_FLOP | Betting round complete

maverick: SHOWDOWN | Player LLM wins 40 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: ============================== Hand 10 ==============================

maverick: PRE_FLOP | Dealing hole cards. Button: FoldBot
maverick: PRE_FLOP | Posting small blind of 10 by player FoldBot. Remaining stack: 900
maverick: PRE_FLOP | Posting big blind of 20 by player LLM. Remaining stack: 3070
maverick: PRE_FLOP | Player FoldBot folds.
maverick: PRE_FLOP | Current pot: 30 | Current bet: 20
maverick: PRE_FLOP | Betting round complete

maverick: SHOWDOWN | Player LLM wins 30 from the pot.
maverick: SHOWDOWN | Showdown complete

maverick: Hand ended

maverick: Reached maximum number of hands, ending game.
maverick: Game ended
for player in players:
    print(f"{player.name} - Stack: {player.state.stack}")
CallBot - Stack: 0
AggroBot - Stack: 0
FoldBot - Stack: 900
LLM - Stack: 3100
token_usage = llm_player._usage

input_tokens = token_usage["input_tokens"]
output_tokens = token_usage["output_tokens"]
cached_input_tokens = token_usage["input_tokens_details"]["cached_tokens"]

print(f"{llm_player.model} total token usage: ${llm_player._total_call_cost():.6f}\n")

for model_name in KNOWN_LLM_PRICES:
    if model_name == llm_player.model:
        continue  # already printed
    
    cost = llm_call_cost(
        input_tokens=input_tokens,
        output_tokens=output_tokens,
        model=model_name,
        cached_input_tokens=cached_input_tokens,
    )
    print(f"Total cost on {model_name} would be: ${cost:.6f}")
gpt-5-nano total token usage: $0.048079

Total cost on gpt-5.2 would be: $1.682774
Total cost on gpt-5.1 would be: $1.201981
Total cost on gpt-5 would be: $1.201981
Total cost on gpt-5-mini would be: $0.240396