Construye tu primer bot de póquer en Python
Lección 1 de 13: Conectar
Completa la primera mano supervisando la ejecución del bot. Python 3.11 o posterior, una terminal y una clave de API del bot para jugar en línea. Las pruebas locales no requieren una cuenta.
Empieza con un cliente pequeño de Python que se conecte a Open Poker y responda a las acciones legales del servidor. Completar tu primera mano es el objetivo de esta lección; la estrategia vendrá después.
Inicio del curso: Descarga el punto de control de la lección que aparece arriba para seguir la evolución del bot del curso. El ejemplo breve de abajo explica el bucle de conexión. Mantén abierta la guía completa de construcción como referencia.
¿Qué necesitas realmente para empezar?
Usa Python 3.11 o posterior para el curso. El cliente mínimo de abajo necesita una biblioteca:
python -m pip install "websockets>=14,<16"
Eso es todo. No necesitas instalar ningún SDK, framework ni motor de juego. Hemos mantenido el protocolo deliberadamente sencillo: tu bot se conecta mediante WebSocket, recibe el estado de la partida como mensajes JSON y devuelve las acciones también como JSON. Si puedes analizar un diccionario, puedes construir un bot.
También necesitarás una clave de API para bots de Open Poker: inicia sesión, selecciona tu bot y elige Self Host. Guarda la clave en la variable de entorno OPEN_POKER_API_KEY. Consulta la guía de registro. Mantén las credenciales fuera del código fuente y de las capturas de pantalla compartidas.
Esta lección usa directamente mensajes WebSocket para que puedas ver el protocolo. Mientras aprendes, registra los tipos de mensaje y los códigos de error; evita publicar cargas útiles sin filtrar que contengan credenciales o cartas privadas.
¿Cómo es el bot completo?
import asyncio
import json
import os
import uuid
import websockets
API_KEY = os.environ["OPEN_POKER_API_KEY"]
WS_URL = "wss://openpoker.ai/ws"
async def play():
headers = {"Authorization": f"Bearer {API_KEY}"}
async with websockets.connect(WS_URL, additional_headers=headers) as ws:
msg = json.loads(await ws.recv())
print(f"Connected as {msg['name']}")
await ws.send(json.dumps({"type": "set_auto_rebuy", "enabled": True}))
await ws.send(json.dumps({"type": "join_lobby", "buy_in": 2000}))
async for raw in ws:
msg = json.loads(raw)
t = msg.get("type")
if t == "your_turn":
actions = {a["action"]: a for a in msg["valid_actions"]}
if "check" in actions:
act = "check"
elif "call" in actions:
act = "call"
else:
act = "fold"
await ws.send(json.dumps({
"type": "action",
"hand_id": msg["hand_id"],
"action": act,
"client_action_id": str(uuid.uuid4()),
"turn_token": msg["turn_token"],
}))
elif t == "table_closed":
await ws.send(json.dumps({"type": "join_lobby", "buy_in": 2000}))
elif t == "season_ended":
await ws.send(json.dumps({"type": "join_lobby", "buy_in": 2000}))
elif t == "hand_result":
winners = msg.get("winners", [])
if winners:
print(f"Hand won by {winners[0]['name']} (+{winners[0].get('amount', 0)})")
asyncio.run(play())Guárdalo como bot.py, define OPEN_POKER_API_KEY en el entorno local y ejecuta python bot.py. Una conexión correcta muestra el nombre de tu bot. La asignación de asiento y la finalización de manos dependen de que haya otros bots disponibles. Este ejemplo corto se detiene al desconectarse; continúa con las lecciones sobre fiabilidad antes de dejar un bot sin supervisión.
¿Qué hace realmente este bot?
Es un calling station, y también fue nuestro primer bot.
Cuando llega tu turno: pasa si puedes (dinero gratis). Si no puedes pasar, iguala. Si no puedes igualar, retírate. Esto pierde fichas lentamente porque igualas cada apuesta sin tener en cuenta la fuerza de tu mano. Pero juega al póquer legalmente, permanece en la mesa y te proporciona un bucle de eventos completo sobre el que construir.
Estos son los cuatro conceptos que conviene entender:
set_auto_rebuy indica al servidor que recompre automáticamente 1.500 fichas cuando te quedes sin ellas. Sin esto, tu bot deja de jugar después de perder su stack. Con ello, el servidor gestiona las recompras (sujetas a un tiempo de espera) y tu bot puede seguir indefinidamente.
join_lobby te coloca en la cola de emparejamiento. El campo buy_in establece cuántas fichas llevas a la mesa. El rango válido va de 1.000 a 5.000; usamos 2.000 por defecto, que son 100 ciegas grandes con la estructura de ciegas 10/20. Cuando hay suficientes jugadores en cola, el emparejador crea una mesa 6-max.
turn_token es un token contra la repetición de acciones. Cada mensaje your_turn incluye un token nuevo. Debes devolverlo en tu acción. Si envías un token obsoleto de un turno anterior, la acción se rechaza. Usa siempre el token del your_turn más reciente. Nunca lo guardes en caché.
hand_id identifica la mano actual. Devuélvelo junto con el token del mismo mensaje your_turn.
client_action_id identifica un intento de acción. El servidor lo devuelve en action_ack; este ejemplo usa un UUID. El código de recuperación debe conservar la identidad de un intento al resolver una confirmación incierta.
¿Qué mensajes WebSocket gestiona tu bot?
Tu bot recibe un flujo continuo de mensajes JSON. La mayoría son informativos; solo necesitas responder a your_turn. Pero entender los demás es la forma de construir un bot más inteligente. Este es el conjunto completo que encontrarás:
| Mensaje | Qué significa | ¿Respondes? |
|---|---|---|
connected | La autenticación tuvo éxito; estás en línea | No |
lobby_joined | Estás en la cola de emparejamiento | No |
table_joined | Estás sentado en una mesa | No |
hand_start | Comienza una mano nueva; aquí están tu asiento y el dealer | No |
hole_cards | Tus dos cartas privadas (p. ej., ["Ah", "Kd"]) | No |
your_turn | Tus acciones válidas, el bote y la mesa | Sí: envía una acción |
player_action | Alguien (quizá tú) actuó | No |
community_cards | Se repartió el flop, el turn o el river | No |
hand_result | La mano terminó; aquí se indica quién ganó | No |
busted | Te has quedado sin fichas | No (la recompra automática se encarga) |
table_closed | La mesa se cerró | Vuelve al lobby |
season_ended | Transición de temporada | Vuelve al lobby |
La referencia completa de mensajes está en docs.openpoker.ai/api-reference/message-types. Cada campo de cada mensaje está documentado con ejemplos JSON. Conviene guardarla en favoritos; la consultarás constantemente.
Cómo hacerlo más inteligente: tres mejoras rápidas
El calling station es una línea base de conectividad. Estas son tres estrategias que puedes probar cuando la conexión funcione; mide sus efectos en lugar de asumir una tasa de ganancias concreta.
1. Añade un filtro preflop sencillo
La mayoría de las manos iniciales son perdedoras. Un filtro preflop sencillo que se retire con el 60 % inferior antes del flop te coloca por delante de cualquier calling station de la plataforma. La selección de manos iniciales es la mejora individual más importante que puedes hacer.
def should_play(cards):
"""Illustrative starting range, not a calibrated percentile."""
ranks = "23456789TJQKA"
r1 = ranks.index(cards[0][0])
r2 = ranks.index(cards[1][0])
high, low = max(r1, r2), min(r1, r2)
pair = r1 == r2
suited = cards[0][1] == cards[1][1]
if pair: return True # All pairs
if low >= 8: return True # Both cards ten or higher
if suited and high - low == 1 and low >= 7: return True # 98s+
if high == 12 and low >= 5: return True # A7+
return FalseGuarda tus cartas privadas cuando recibas hole_cards y después comprueba should_play() en el gestor de your_turn. Con una mano excluida, comprueba si esa acción es gratuita y legal; de lo contrario, retírate solo cuando fold aparezca en valid_actions.
2. Sube la apuesta con tus manos fuertes
El calling station nunca sube. Eso significa que los rivales pueden ver flops baratos contra ti en todas las manos. Solución: sube con el 15 % más fuerte de tus manos preflop.
if "raise" in actions and should_raise(my_cards):
await ws.send(json.dumps({
"type": "action",
"hand_id": msg["hand_id"],
"action": "raise",
"amount": actions["raise"]["min"], # minimum raise
"client_action_id": next_id(),
"turn_token": msg["turn_token"],
}))La entrada raise de valid_actions te indica exactamente los importes min y max. El campo amount es un importe hasta el que subir (el tamaño total de la apuesta), no un incremento. Si la ciega grande es 20 y quieres subir hasta 60, envía "amount": 60.
3. Usa las pot odds después del flop
Después del flop tienes información real. Las pot odds te indican si igualar es matemáticamente correcto: si el precio que pagas es inferior a tu probabilidad de ganar, iguala. De lo contrario, retírate. Para conocer las matemáticas completas, la entrada del glosario sobre pot odds contiene ejemplos resueltos y trampas que hacen tropezar a los bots principiantes.
def pot_odds_say_call(pot, call_amount, estimated_win_pct=0.3):
if call_amount == 0:
return True
odds = call_amount / (pot + call_amount)
return estimated_win_pct > oddsIncluso una estimación aproximada de tu probabilidad de ganar (30 % por defecto, más alta con pareja máxima y más baja sin nada), combinada con las pot odds, supera ampliamente al calling station puro. El mensaje your_turn incluye el tamaño actual del bote, así que tienes todo lo necesario.
Lo que aprendimos al ejecutar este bot
Ejecuté el calling station durante más de 1.200 manos para obtener una línea base real. Perdió 2,4 ciegas grandes por cada 100 manos; no es catastrófico, pero sí un drenaje constante. La mayor fuga no era igualar demasiadas apuestas. Era igualar apuestas en el river sin tener nada. El calling station no entiende «no he ligado nada y esta apuesta es grande en relación con el bote»; simplemente iguala, cada vez, y se desangra.
Lo segundo que me sorprendió: los tiempos de espera de las recompras automáticas importan más de lo que crees. Después de quedarte sin fichas, hay un tiempo de espera de 5 minutos en el nivel gratuito (2 minutos en Pro) antes de la siguiente recompra. Un bot que se queda sin fichas con frecuencia pasa mucho tiempo ausente. Gestionar bien el stack (no quedarse sin fichas en primer lugar) produce beneficios acumulativos que van más allá de conservar fichas.
Añadir should_play() de la sección anterior redujo la tasa de pérdidas hasta aproximadamente 0,8 bb/100 en nuestras pruebas: una mejora de 3x gracias a una sola función. El bot sigue perdiendo, pero ahora pierde como un jugador mediocre y no como uno roto. Ese es el punto de partida para trabajar en una estrategia real.
No afirmamos que estos tamaños de muestra sean rigurosos. La varianza en 6-max es alta y 1.200 manos son una ventana pequeña. Pero, en términos direccionales, el patrón es consistente: la selección preflop es la primera palanca y la agresión postflop, la segunda.
¿Cómo puedes reproducir la línea base de 1.200 manos?
Trata la ejecución de 1.200 manos del calling station como una línea base de ingeniería, no como una referencia de rentabilidad. El resultado registrado fue de -2,4 bb/100. El resultado de seguimiento con el filtro preflop fue de aproximadamente -0,8 bb/100, pero ese seguimiento no conservó suficientes metadatos de ejecución para sostener una comparación directa limpia. Publicamos esa limitación porque un número sin su método es marketing, no evidencia.
Para una comparación reproducible, fija la revisión del bot y registra estos campos en cada ejecución:
| Campo | Por qué pertenece a la referencia |
|---|---|
| Hash del commit de Git y de la configuración | Demuestra qué política produjo las acciones |
| Hora UTC de inicio y fin | Expone diferencias de campo y disponibilidad |
| IDs de las manos completadas | Hace la muestra auditable y evita contar dos veces |
| Ciegas grandes ganadas o perdidas por cada 100 manos | Normaliza los resultados entre niveles de ciegas |
Recuento de action_rejected | Detecta errores de protocolo disfrazados de pérdidas estratégicas |
| Tiempos de espera del turno y reconexiones | Separa la calidad de las decisiones de los fallos de ejecución |
| Número de rivales y distribución de asientos | Muestra si una mesa dominó el resultado |
Ejecuta la línea base y la candidata con el mismo número mínimo de manos, conserva ambas listas de IDs de manos sin procesar e informa de intervalos de confianza antes de considerar real una mejora. Con 1.200 manos, el resultado sirve para encontrar fugas obvias, como igualar siempre en el river. No basta para clasificar estrategias de póquer.

Captura de pantalla del producto, tomada el 10 de marzo de 2026. Esta es la interfaz de auditoría de resultados, no la ejecución de 1.200 manos del calling station. Los IDs de manos y los resultados de cada mano son el rastro de evidencias que una referencia debería conservar.
Qué esperar de la clasificación
El calling station básico es una prueba de conectividad, no una estrategia competitiva. Añadir las tres mejoras elimina fugas obvias, pero no se deduce de ellas ninguna posición fija en la clasificación. El campo cambia con cada temporada y las muestras cortas son ruidosas. Para seguir mejorando, añade evaluación de manos, modelado de rivales, gestión del stack y conciencia de la posición; después mide cada cambio frente a una línea base fijada.
Tu bot necesita al menos 10 manos para aparecer en la clasificación. El tiempo necesario depende de la disponibilidad de mesas y de la velocidad de juego.
La documentación completa de la plataforma está en docs.openpoker.ai. La guía de acciones y estrategia explica en detalle la semántica de raise, los tokens de turno y el comportamiento ante tiempos de espera. Vale la pena leer la documentación de la biblioteca websockets si quieres gestionar conexiones asíncronas más allá de los conceptos básicos mostrados aquí.
Preguntas frecuentes
Mi bot se conecta, pero nunca consigue asiento. El emparejador necesita 2 o más jugadores en la cola. Si no juega nadie más, tu bot espera. Comprueba la clasificación para ver si hay otros activos; no registres agentes de producción independientes adicionales solo para ocupar un segundo asiento.
Recibo errores action_rejected.
Comprueba el código de rechazo y confirma que hand_id y turn_token proceden del mismo mensaje your_turn actual. No reutilices la autoridad de un turno anterior.
Mi bot se desconectó y perdió su asiento. Tienes 120 segundos para reconectarte. Si lo haces a tiempo, conservas tu asiento. Después de 120 segundos, tu stack vuelve a tu saldo y tendrás que volver a unirte al lobby.
¿Puedo ejecutar este bot las 24 horas? El ejemplo corto está pensado para una primera sesión supervisada. El juego sin supervisión requiere reconexión, recuperación del estado, gestión de plazos y seguimiento de confirmaciones. Completa la etapa de fiabilidad antes de intentarlo.
¿Con cuántas fichas debería entrar? El rango válido va de 1.000 a 5.000 fichas. En los ejemplos usamos 2.000 (100 ciegas grandes con ciegas 10/20), una cantidad inicial estándar para un stack profundo. Entrar con menos (1.000) reduce la varianza, pero también limita cuánto puedes ganar en una sola mano. Entrar con más (5.000) está bien cuando tu bot ya tiene una estrategia básica de retirarse y subir; no lo hagas con un calling station puro.
Después de que tu bot complete una mano, continúa con la siguiente lección de abajo. Si el lobby está esperando, mantén abierta la sesión para que lleguen rivales y usa el punto de control sin conexión para verificar tu cliente mientras tanto.
Comprueba el resultado de la lección 1
- Qué cambia
- Conecta un cliente y envía una acción permitida.
- Salida esperada
- completed_hands: 1
- Comprueba que funciona
- Espera a que termine una mano real. Una prueba con datos simulados no cuenta como una partida en línea.
Descomprime el archivo de la lección, abre la carpeta en una terminal y ejecuta:
python -m pip install -r requirements.txt
python bot.py --lesson 1 --self-test
python bot.py --lesson 1 --hands 3 --report run.jsonLa prueba usa datos simulados sin conexión y muestra checkpoint: passed. Para jugar en línea necesitas OPEN_POKER_API_KEY en el entorno de ejecución y rivales disponibles. Consulta el README incluido para configurar el bot y conocer los límites de recuperación.
Todas las lecciones del curso
- 1. Crea un bot de póker en Python
- 2. Arquitectura básica de un bot de póker
- 3. Cómo depurar errores de WebSocket en tu bot
- 4. Por qué tu bot se queda sin tiempo
- 5. Matemáticas del póker para bots
- 6. Rangos por posición para bots de póker
- 7. Estrategia de apuestas para bots de póker
- 8. Tutorial de PokerKit
- 9. Calculadora de equity por Monte Carlo
- 10. Modelado de rivales
- 11. Arquitectura avanzada de un bot de póker
- 12. Cómo funciona la puntuación de la clasificación
- 13. Arquitectura profesional de un bot de póker