"""Texas Hold'em engines and prompts module - FIXED VERSION.
This module provides LLM configurations and prompts for Hold'em agents.
Fixed variable naming consistency issues.
"""
from haive.core.engine.aug_llm import AugLLMConfig
from haive.core.models.llm.base import AnthropicLLMConfig, AzureLLMConfig
from langchain_core.prompts import ChatPromptTemplate
from haive.games.hold_em.models import (
BettingDecision,
GameSituationAnalysis,
OpponentModel,
PokerAnalysis,
)
# ============================================================================
# PLAYER DECISION PROMPTS
# ============================================================================
[docs]
def create_player_decision_prompt(player_style: str = "balanced") -> ChatPromptTemplate:
"""Create a decision-making prompt based on player style."""
style_instructions = {
"tight": "You are a TIGHT player. You play few hands but play them aggressively. "
"Only enter pots with strong hands (top 15% of hands). Fold weak hands quickly.",
"loose": "You are a LOOSE player. You play many hands and like to see flops. "
"You're willing to play weaker hands and chase draws. Take more risks.",
"aggressive": "You are an AGGRESSIVE player. You bet and raise frequently. "
"You use aggression to win pots and put pressure on opponents. Bluff selectively.",
"passive": "You are a PASSIVE player. You prefer to call rather than bet or raise. "
"You let others lead the action and look for strong hands to win big pots.",
"balanced": "You are a BALANCED player. You mix up your play style based on position, "
"opponents, and game situation. Adapt your strategy dynamically.",
"tricky": "You are a TRICKY player. You use deception, varying bet sizes, and "
"unconventional plays to confuse opponents. Mix up your timing and sizing.",
}
style_instruction = style_instructions.get(
player_style, style_instructions["balanced"]
)
return ChatPromptTemplate.from_messages(
[
(
"system",
f"You are playing Texas Hold'em poker. {style_instruction}\n\n"
"DECISION RULES:\n"
"- Consider your hole cards, community cards, position, and pot odds\n"
"- Factor in opponent behavior and betting patterns\n"
"- Choose from: fold, check, call, bet, raise, all_in\n"
"- Provide bet/raise amounts when applicable\n"
"- Give clear reasoning for your decision\n\n"
"BETTING GUIDELINES:\n"
"- Value bet when you have a strong hand\n"
"- Bluff when you have fold equity\n"
"- Consider pot odds when drawing\n"
"- Adjust bet sizing based on board texture\n",
),
(
"human",
"GAME SITUATION:\n"
"Your hole cards: {hole_cards}\n"
"Community cards: {community_cards}\n"
"Game phase: {phase}\n"
"Your position: {position}\n"
"Your chips: {chips}\n"
"Current bet to call: {current_bet}\n"
"Pot size: {pot}\n"
"Players in hand: {players_in_hand}\n\n"
"ANALYSIS DATA:\n"
"Situation analysis: {situation_analysis}\n"
"Hand analysis: {hand_analysis}\n"
"Opponent analysis: {opponent_analysis}\n\n"
"AVAILABLE ACTIONS: {available_actions}\n"
"Your playing style: {player_style}\n"
"Risk tolerance: {risk_tolerance}\n\n"
"Make your decision and explain your reasoning.",
),
]
)
# ============================================================================
# ANALYSIS PROMPTS - FIXED VARIABLE NAMES
# ============================================================================
situation_analysis_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are analyzing the current poker game situation. Evaluate:\n"
"- Stack sizes and their implications\n"
"- Position advantages/disadvantages\n"
"- Betting action and what it suggests\n"
"- Stage of the hand and how it affects play\n"
"- Pot odds and implied odds\n",
),
(
"human",
"CURRENT SITUATION ANALYSIS:\n"
"Your position: {player_position}\n"
"Pot size: {pot_size}\n"
"Current bet: {current_bet}\n"
"Your chips: {player_chips}\n"
"Players in hand: {players_in_hand}\n"
"Game phase: {game_phase}\n"
"Community cards: {community_cards}\n"
"Recent actions: {recent_actions}\n"
"Stack sizes: {stack_sizes}\n\n"
"Analyze this poker situation comprehensively. Consider position dynamics, "
"stack-to-pot ratios, betting patterns, and strategic implications.",
),
]
)
hand_analysis_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are analyzing poker hand strength. Evaluate:\n"
"- Current hand strength (made hands, draws)\n"
"- Potential to improve on future streets\n"
"- Hand range vs likely opponent ranges\n"
"- Nut potential and relative strength\n"
"- Pot odds and equity calculations\n",
),
(
"human",
"HAND STRENGTH ANALYSIS:\n"
"Your hole cards: {hole_cards}\n"
"Community cards: {community_cards}\n"
"Game phase: {game_phase}\n"
"Number of opponents: {num_opponents}\n"
"Pot odds: {pot_odds}\n"
"Current bet to call: {current_bet}\n"
"Pot size: {pot_size}\n\n"
"Analyze your hand strength comprehensively. Include equity calculations, "
"draw potential, and relative strength against likely opponent ranges.",
),
]
)
opponent_analysis_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are analyzing opponent behavior in poker. Look for:\n"
"- Betting patterns and sizing tells\n"
"- Position-based tendencies\n"
"- Aggression levels and frequency\n"
"- Likely hand ranges based on actions\n"
"- Exploitable tendencies\n",
),
(
"human",
"OPPONENT BEHAVIOR ANALYSIS:\n"
"Opponents in hand: {opponents}\n"
"Betting pattern this hand: {betting_pattern}\n"
"Game phase: {game_phase}\n"
"Community cards: {community_cards}\n"
"Position dynamics: {position_info}\n"
"Stack sizes: {stack_sizes}\n\n"
"Analyze opponent tendencies, likely hand ranges, and exploitable patterns. "
"Consider their betting behavior and position-based play.",
),
]
)
# ============================================================================
# ENGINE BUILDERS - FIXED
# ============================================================================
[docs]
def build_player_engines(
player_name: str, player_style: str, heads_up: bool = False
) -> dict[str, AugLLMConfig]:
"""Build LLM engines for a player agent."""
# Adjust model selection based on complexity needs
if heads_up:
# Use more powerful models for heads-up play
decision_model = AnthropicLLMConfig(
model="claude-3-5-sonnet-20240620", temperature=0.7
)
analysis_model = AzureLLMConfig(model="gpt-4o", temperature=0.6)
else:
# Standard models for multi-way play
decision_model = AzureLLMConfig(model="gpt-4o", temperature=0.7)
analysis_model = AzureLLMConfig(model="gpt-4o", temperature=0.6)
return {
"situation_analyzer": AugLLMConfig(
name=f"{player_name}_situation_analyzer",
llm_config=analysis_model,
prompt_template=situation_analysis_prompt,
structured_output_model=GameSituationAnalysis,
description=f"Situation analyzer for {player_name}",
structured_output_version="v1",
),
"hand_analyzer": AugLLMConfig(
name=f"{player_name}_hand_analyzer",
llm_config=analysis_model,
prompt_template=hand_analysis_prompt,
structured_output_model=PokerAnalysis,
force_tool_choice=True,
description=f"Hand analyzer for {player_name}",
structured_output_version="v1",
),
"opponent_analyzer": AugLLMConfig(
name=f"{player_name}_opponent_analyzer",
llm_config=analysis_model,
prompt_template=opponent_analysis_prompt,
structured_output_model=OpponentModel,
description=f"Opponent analyzer for {player_name}",
structured_output_version="v1",
),
"decision_maker": AugLLMConfig(
name=f"{player_name}_decision_maker",
llm_config=decision_model,
prompt_template=create_player_decision_prompt(player_style),
structured_output_model=BettingDecision,
description=f"Decision maker for {player_name} ({player_style} style)",
structured_output_version="v1",
),
}
[docs]
def build_holdem_game_engines() -> dict[str, AugLLMConfig]:
"""Build engines for the main game agent."""
# Game management engines (if needed for complex decisions)
base_model = AzureLLMConfig(model="gpt-4o-mini", temperature=0.3)
return {
"game_narrator": AugLLMConfig(
name="holdem_game_narrator",
llm_config=base_model,
prompt_template=ChatPromptTemplate.from_messages(
[
(
"system",
"You narrate poker games, describing key moments and decisions.",
),
("human", "Describe this poker situation: {situation}"),
]
),
description="Game narration and commentary",
),
"hand_evaluator": AugLLMConfig(
name="holdem_hand_evaluator",
llm_config=base_model,
prompt_template=ChatPromptTemplate.from_messages(
[
(
"system",
"You evaluate poker hands and determine winners at showdown.",
),
("human", "Evaluate these hands: {hands_to_evaluate}"),
]
),
description="Hand evaluation for showdowns",
structured_output_version="v1",
structured_output_model=PokerAnalysis,
force_tool_choice=True,
),
}
# ============================================================================
# SPECIALIZED PROMPTS FOR DIFFERENT SITUATIONS
# ============================================================================
preflop_decision_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are making a PREFLOP decision in Texas Hold'em. Consider:\n"
"- Hand strength relative to position\n"
"- Stack sizes and effective stacks\n"
"- Opponent tendencies and ranges\n"
"- Table dynamics and image\n"
"- ICM considerations (if tournament)\n",
),
(
"human",
"PREFLOP DECISION CONTEXT:\n"
"Hole cards: {hole_cards}\n"
"Position: {position}\n"
"Action before you: {action_before}\n"
"Players behind: {players_behind}\n"
"Your stack: {stack}\n"
"Blinds: {blinds}\n"
"Game phase: {game_phase}\n"
"Players in hand: {players_in_hand}\n\n"
"Make your preflop decision based on position, hand strength, and opponent action.",
),
]
)
postflop_decision_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are making a POSTFLOP decision in Texas Hold'em. Consider:\n"
"- Board texture and how it hits ranges\n"
"- Your hand strength and potential\n"
"- Opponent's likely holdings\n"
"- Pot odds and implied odds\n"
"- Position and initiative\n",
),
(
"human",
"POSTFLOP DECISION CONTEXT:\n"
"Hole cards: {hole_cards}\n"
"Community cards: {community_cards}\n"
"Game phase: {game_phase}\n"
"Position: {position}\n"
"Pot size: {pot_size}\n"
"Current bet: {current_bet}\n"
"Effective stack: {effective_stack}\n"
"Action this street: {action_this_street}\n"
"Hand analysis: {hand_analysis}\n"
"Players in hand: {players_in_hand}\n\n"
"Make your postflop decision considering board texture, hand strength, and position.",
),
]
)
tournament_decision_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are playing in a TOURNAMENT. Consider ICM implications:\n"
"- Survival vs chip accumulation\n"
"- Stack sizes relative to blinds\n"
"- Bubble factors and pay jumps\n"
"- Risk vs reward in tournament context\n"
"- Opponent stack sizes and desperation\n",
),
(
"human",
"TOURNAMENT DECISION CONTEXT:\n"
"Your stack: {stack}\n"
"Average stack: {average_stack}\n"
"Blinds: {blinds}\n"
"Players remaining: {players_remaining}\n"
"Prize structure: {prize_structure}\n"
"Bubble distance: {bubble_distance}\n"
"Game situation: {game_situation}\n"
"Hole cards: {hole_cards}\n"
"Community cards: {community_cards}\n"
"Position: {position}\n\n"
"Make your tournament decision considering ICM and survival factors.",
),
]
)
[docs]
def create_style_specific_engines(player_style: str) -> dict[str, AugLLMConfig]:
"""Create engines optimized for specific playing styles."""
if player_style == "aggressive":
temp = 0.8 # Higher variance for aggressive play
model = AnthropicLLMConfig(model="claude-3-5-sonnet-20240620", temperature=temp)
elif player_style == "tight":
temp = 0.4 # Lower variance for tight play
model = AzureLLMConfig(model="gpt-4o", temperature=temp)
elif player_style == "loose":
temp = 0.9 # Higher variance for loose play
model = AzureLLMConfig(model="gpt-4o", temperature=temp)
else:
temp = 0.7 # Balanced temperature
model = AzureLLMConfig(model="gpt-4o", temperature=temp)
return {
"preflop_specialist": AugLLMConfig(
name=f"{player_style}_preflop_specialist",
llm_config=model,
prompt_template=preflop_decision_prompt,
structured_output_model=BettingDecision,
force_tool_choice=True,
structured_output_version="v1",
description=f"Preflop specialist for {player_style} style",
),
"postflop_specialist": AugLLMConfig(
name=f"{player_style}_postflop_specialist",
llm_config=model,
prompt_template=postflop_decision_prompt,
structured_output_model=BettingDecision,
force_tool_choice=True,
structured_output_version="v1",
description=f"Postflop specialist for {player_style} style",
),
}
# ============================================================================
# CONTEXT PREPARATION HELPERS
# ============================================================================
[docs]
def prepare_situation_context(game_state, player) -> dict[str, str]:
"""Prepare context dictionary for situation analysis with correct variable names."""
return {
"player_position": player.position,
"pot_size": str(game_state.pot_size),
"current_bet": str(game_state.current_bet),
"player_chips": str(player.chips),
"players_in_hand": str(game_state.players_in_hand),
"game_phase": game_state.phase,
"community_cards": str(game_state.community_cards),
"recent_actions": str(game_state.recent_actions),
"stack_sizes": str(game_state.stack_sizes),
}
[docs]
def prepare_hand_context(game_state, player) -> dict[str, str]:
"""Prepare context dictionary for hand analysis."""
return {
"hole_cards": str(player.hole_cards),
"community_cards": str(game_state.community_cards),
"game_phase": game_state.phase,
"num_opponents": str(len(game_state.players_in_hand) - 1),
"pot_odds": str(game_state.calculate_pot_odds()),
"current_bet": str(game_state.current_bet),
"pot_size": str(game_state.pot_size),
}
[docs]
def prepare_opponent_context(game_state, opponents) -> dict[str, str]:
"""Prepare context dictionary for opponent analysis."""
return {
"opponents": str([opp.name for opp in opponents]),
"betting_pattern": str(game_state.betting_pattern),
"game_phase": game_state.phase,
"community_cards": str(game_state.community_cards),
"position_info": str(game_state.position_info),
"stack_sizes": str(game_state.stack_sizes),
}
[docs]
def prepare_decision_context(game_state, player, analyses) -> dict[str, str]:
"""Prepare context dictionary for final decision making."""
return {
"hole_cards": str(player.hole_cards),
"community_cards": str(game_state.community_cards),
"phase": game_state.phase,
"position": player.position,
"chips": str(player.chips),
"current_bet": str(game_state.current_bet),
"pot": str(game_state.pot_size),
"players_in_hand": str(game_state.players_in_hand),
"situation_analysis": str(analyses.get("situation", "No analysis available")),
"hand_analysis": str(analyses.get("hand", "No analysis available")),
"opponent_analysis": str(analyses.get("opponent", "No analysis available")),
"available_actions": str(game_state.available_actions),
"player_style": player.style,
"risk_tolerance": str(player.risk_tolerance),
}