Cree un Bot de Poker en Python en Moins de 50 Lignes de Code
Tu peux creer un bot de poker fonctionnel en 47 lignes de Python. Il ne gagnera aucun tournoi, mais il se connectera a Open Poker, s'assiéra a une table et jouera des mains contre d'autres bots. C'est le point de depart pour tout le reste.
De quoi as-tu vraiment besoin pour commencer ?
Juste Python 3.10+ et une bibliotheque :
pip install websockets
C'est tout. Pas de SDK, pas de framework, pas de moteur de jeu a installer. Nous avons deliberement garde le protocole simple : ton bot se connecte par WebSocket, recoit l'etat du jeu sous forme de messages JSON et renvoie des actions en JSON. Si tu sais parser un dictionnaire, tu peux creer un bot.
Tu auras aussi besoin d'une cle API Open Poker : inscris-toi ici si ce n'est pas encore fait. L'inscription est gratuite et prend environ 30 secondes.
Pourquoi pas de SDK ? Parce que les SDKs sont la mauvaise abstraction pour ce probleme. Ils ajoutent une surface de dependance qui doit etre maintenue, cachent le protocole et rendent le debug plus difficile. Quand quelque chose tourne mal (et ca arrivera), tu veux afficher les messages bruts pour voir exactement ce que le serveur a envoye. Chaque framework de bot qu'on a vu finit par se battre contre le SDK au lieu de travailler sur la strategie. L'approche WebSocket brut te laisse le controle.
A quoi ressemble le bot complet ?
import asyncio
import json
import websockets
API_KEY = "your-api-key-here"
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",
"action": act,
"client_action_id": f"a-{msg['turn_token'][:8]}",
"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())Enregistre-le sous bot.py, remplace your-api-key-here par ta vraie cle et lance python bot.py. Tu devrais voir un output dans les 30 secondes une fois qu'un autre bot rejoint la file.
Que fait reellement ce bot ?
C'est une calling station, et c'etait aussi notre premier bot.
Quand c'est ton tour : check si tu peux (argent gratuit). Si tu ne peux pas checker, call. Si tu ne peux pas caller, fold. Ca perd des jetons lentement parce que tu calles chaque mise sans considerer la force de ta main. Mais ca joue du poker legal, reste a la table et te donne une boucle d'evenements complete sur laquelle construire.
Les quatre concepts a comprendre :
set_auto_rebuy dit au serveur de faire automatiquement un rebuy de 1 500 jetons quand tu es elimine. Sans ca, ton bot arrete de jouer apres avoir perdu son stack. Avec, le serveur gere les rebuys (avec cooldown) et ton bot continue indefiniment.
join_lobby te met dans la file de matchmaking. Le champ buy_in definit combien de jetons emmener a la table. La plage valide est de 1 000 a 5 000 ; on utilise 2 000 par defaut, soit 100 big blinds dans la structure 10/20. Quand assez de joueurs sont en file, le matchmaker cree une table 6-max.
turn_token est un jeton anti-replay. Chaque message your_turn inclut un nouveau token. Tu dois le renvoyer dans ton action. Si tu envoies un vieux token d'un tour precedent, l'action est rejetee. Utilise toujours le token du your_turn le plus recent. Ne le mets jamais en cache.
client_action_id est pour ton propre suivi. Le serveur le renvoie dans action_ack pour que tu puisses savoir quelle action a ete acceptee. Utilise un ID unique par action ; on decoupe simplement le turn token pour obtenir une chaine unique rapide.
Quels messages WebSocket ton bot recoit-il ?
Ton bot recoit un flux continu de messages JSON. La plupart sont informatifs ; tu n'as besoin de repondre qu'a your_turn. Mais comprendre les autres est la cle pour construire un bot plus intelligent. Voici l'ensemble complet que tu rencontreras :
| Message | Ce que ca signifie | Tu reponds ? |
|---|---|---|
connected | Auth reussi, tu es en ligne | Non |
lobby_joined | Tu es dans la file de matchmaking | Non |
table_joined | Tu es assis a une table | Non |
hand_start | Nouvelle main qui commence, ton siege et le dealer | Non |
hole_cards | Tes deux cartes privees (ex. : ["Ah", "Kd"]) | Non |
your_turn | Tes actions valides, le pot, le board | Oui : envoie une action |
player_action | Quelqu'un (peut-etre toi) a agi | Non |
community_cards | Flop, turn ou river distribues | Non |
hand_result | Main terminee, qui a gagne | Non |
busted | Tu n'as plus de jetons | Non (auto-rebuy s'en charge) |
table_closed | Table fermee | Rejoindre le lobby |
season_ended | Transition de season | Rejoindre le lobby |
La reference complete des messages est sur docs.openpoker.ai/api-reference/message-types. Chaque champ de chaque message est documente avec des exemples JSON. A mettre en favoris ; tu vas le consulter tout le temps.
Le rendre plus intelligent : trois ameliorations rapides
La calling station perd environ 2-3 big blinds pour 100 mains. Trois changements l'ameliorent immediatement, classes par impact.
1. Fold les mauvaises mains pre-flop
La plupart des mains de depart au poker sont perdantes. Fold les 60 % les plus faibles avant le flop et tu es deja devant toutes les calling stations de la plateforme. La selection des mains de depart est la plus grande amelioration que tu puisses faire.
def should_play(cards):
"""Return True for top ~40% of starting hands."""
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 high >= 10: return True # Any two broadway
if high >= 9 and suited: return True # Suited connectors 9+
if high == 12 and low >= 7: return True # A7+
return FalseStocke tes hole cards quand tu recois hole_cards, puis verifie should_play() dans ton handler your_turn. Fold tout le reste pre-flop.
2. Raise tes mains fortes
La calling station ne raise jamais. Ca veut dire que les adversaires voient des flops pas chers contre toi a chaque main. Correction : raise avec tes 15 % de mains les plus fortes pre-flop.
if "raise" in actions and should_raise(my_cards):
await ws.send(json.dumps({
"type": "action",
"action": "raise",
"amount": actions["raise"]["min"], # minimum raise
"client_action_id": next_id(),
"turn_token": msg["turn_token"],
}))L'entree raise dans valid_actions te donne les montants exacts min et max. Le champ amount est un montant raise-to (taille totale de la mise), pas un increment. Si le big blind est 20 et que tu veux raise a 60, envoie "amount": 60.
3. Utilise les pot odds post-flop
Apres le flop, tu as de vraies informations. Les pot odds te disent si caller est mathematiquement correct : si le prix que tu paies est inferieur a ta probabilite de gagner, call. Sinon, fold. Pour les calculs complets, l'entree du glossaire des pot odds a des exemples resolus et des pieges qui attrapent les bots debutants.
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 > oddsMeme une estimation approximative de ta probabilite de gain (30 % par defaut, plus haute avec top pair, plus basse sans rien) combinee aux pot odds bat largement la calling station pure. Le message your_turn inclut la taille actuelle du pot, donc tu as tout ce qu'il te faut.
Ce qu'on a appris en faisant tourner ce bot
J'ai fait tourner la calling station sur plus de 1 200 mains pour obtenir un vrai baseline. Elle a perdu 2,4 big blinds pour 100 mains — pas catastrophique mais un drain constant. La plus grosse fuite n'etait pas de caller trop de mises. C'etait de caller les mises au river sans rien. La calling station n'a pas le concept de "je n'ai rien touche et cette mise est grosse par rapport au pot" ; elle call, a chaque fois, et saigne.
La deuxieme chose qui m'a surpris : les cooldowns d'auto-rebuy comptent plus que tu ne le penses. Apres une elimination, il y a un cooldown de 5 minutes sur le plan gratuit (2 minutes en Pro) avant le prochain rebuy. Un bot qui se fait eliminer frequemment passe beaucoup de temps a l'ecart. Bien gerer son stack (ne pas se faire eliminer) a des rendements composes au-dela de la simple conservation de jetons.
Ajouter should_play() de la section ci-dessus a fait chuter le taux de perte a environ 0,8 bb/100 dans nos tests — une amelioration de 3x avec une seule fonction. Le bot perd toujours, mais il perd maintenant comme un joueur mediocre plutot que comme un joueur casse. C'est le point de depart pour le vrai travail de strategie.
On ne pretend pas que ce sont des tailles d'echantillon rigoureuses. La variance en 6-max est elevee, et 1 200 mains c'est une petite fenetre. Mais directionnellement, le pattern est coherent : la selection pre-flop est le premier levier, l'agressivite post-flop est le deuxieme.
A quoi s'attendre sur le leaderboard
Avec la calling station de base, tu finiras en bas du leaderboard. Ajoute les trois ameliorations ci-dessus et tu te retrouveras au milieu. Pour atteindre le sommet, tu auras besoin d'evaluation de mains, de modelisation d'adversaires, de gestion de stack et de conscience positionnelle. Le chemin complet est documente dans le plan de 7 jours pour le leaderboard.
Ton bot a besoin d'au moins 10 mains pour apparaitre sur le leaderboard. Avec du jeu continu et l'auto-rebuy active, tu y arrives en quelques minutes.
La documentation complete de la plateforme est sur docs.openpoker.ai. Le guide d'actions et de strategie couvre la semantique de raise, les turn tokens et le comportement de timeout en detail. La documentation de la bibliotheque websockets vaut la lecture si tu veux une gestion de connexion async au-dela des bases montrees ici.
FAQ
Mon bot se connecte mais ne s'assoit jamais a une table. Le matchmaker a besoin de 2+ joueurs dans la file. Si personne d'autre ne joue, ton bot attend. Lance deux bots avec des cles API differentes, ou verifie le leaderboard pour voir si d'autres sont actifs.
Je recois des erreurs action_rejected.
Verifie que tu inclus le turn_token du message your_turn le plus recent. Les tokens perimés sont la cause #1 des rejets. Ne mets pas le token en cache entre les tours.
Mon bot s'est deconnecte et a perdu son siege. Tu as 120 secondes pour te reconnecter. Si tu te reconnectes a temps, ton siege est preserve. Apres 120 secondes, ton stack est retourne a ton solde et tu dois rejoindre le lobby.
Puis-je faire tourner ce bot 24/7 ?
Oui. Active l'auto-rebuy et gere table_closed + season_ended en rejoignant le lobby. Le bot de ce post fait les deux. On l'a fait tourner sur plusieurs jours d'affilee sans aucune intervention.
Combien devrais-je acheter en buy-in ? La plage valide est de 1 000 a 5 000 jetons. On utilise 2 000 dans les exemples (100 big blinds avec des blinds 10/20), ce qui est un montant de depart deep-stack standard. Acheter moins (1 000) reduit la variance mais limite aussi combien tu peux gagner en une seule main. Acheter plus (5 000) est bien une fois que ton bot a une strategie basique de fold/raise ; ne fais pas ca avec une calling station pure.
Pret a lancer ton premier bot ? Inscris-toi pour une cle API et tu joueras des mains en cinq minutes. La calling station est un bon point de depart ; chaque bot competitif du leaderboard a commence de maniere similaire.