Skip to content
[OPEN_POKER]

用不到50行Python代码构建一个扑克机器人

JJoão Carvalho||14 min read

你可以用47行Python构建一个能工作的扑克机器人。它不会赢得任何锦标赛,但它会连接到Open Poker,坐到牌桌上,与其他机器人对战。这是一切的起点。

你真正需要什么才能开始?

只需要Python 3.10+和一个库:

pip install websockets

就这样。不需要SDK,不需要框架,不需要安装游戏引擎。我们刻意保持协议简单:你的机器人通过WebSocket连接,接收JSON消息形式的游戏状态,并以JSON发送操作。如果你能解析字典,就能构建机器人。

你还需要一个Open Poker API密钥:如果还没有的话在这里注册。注册免费,大约30秒完成。

为什么没有SDK?因为SDK是这个问题的错误抽象。它们增加了需要维护的依赖面,隐藏了协议,使调试更困难。当出问题时(一定会的),你想打印原始消息来查看服务器到底发了什么。我们见过的每个机器人框架最终都在与SDK搏斗,而不是在策略上工作。原始WebSocket方法让你保持控制。

完整的机器人长什么样?

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())

保存为bot.py,将your-api-key-here替换为你的真实密钥,运行python bot.py。一旦另一个机器人加入队列,你应该在30秒内看到输出。

这个机器人到底做什么?

它是一个calling station,也是我们的第一个机器人。

轮到你时:能check就check(免费的钱)。不能check就call。不能call就fold。这会慢慢损失筹码,因为你在没有考虑牌力的情况下跟注每一个下注。但它在打合法的扑克,留在牌桌上,并给你一个完整的事件循环来在此基础上构建。

值得理解的四个概念:

set_auto_rebuy 告诉服务器在你破产时自动rebuy 1,500筹码。没有这个,你的机器人在失去所有筹码后就停止游戏了。有了这个,服务器会处理rebuy(有冷却时间),你的机器人可以无限期地继续玩。

join_lobby 把你放入匹配队列。buy_in字段设置带多少筹码到牌桌。有效范围是1,000到5,000;我们默认使用2,000,在10/20盲注结构下是100个大盲注。当队列中有足够的玩家时,匹配系统创建一个6-max牌桌。

turn_token 是防重放令牌。每个your_turn消息包含一个新令牌。你必须在你的操作中返回它。如果你发送了一个来自上一轮的旧令牌,操作会被拒绝。始终使用最新your_turn中的令牌。绝不要缓存它。

client_action_id 用于你自己的追踪。服务器在action_ack中返回它,这样你可以关联哪个操作被接受了。每个操作使用唯一ID;我们只是截取turn token来快速获得唯一字符串。

你的机器人处理哪些WebSocket消息?

你的机器人接收持续的JSON消息流。大多数是信息性的;你只需要响应your_turn。但理解其他消息是你构建更智能机器人的方式。以下是你会遇到的完整集合:

消息含义需要响应?
connected认证成功,你在线了
lobby_joined你在匹配队列中
table_joined你坐在牌桌上了
hand_start新手牌开始,你的座位和庄家
hole_cards你的两张底牌(如["Ah", "Kd"]
your_turn你的有效操作、底池、公共牌是:发送操作
player_action某人(可能是你)采取了行动
community_cards翻牌、转牌或河牌发出
hand_result手牌结束,谁赢了
busted你没筹码了否(auto-rebuy处理)
table_closed牌桌关闭重新加入大厅
season_ended赛季过渡重新加入大厅

完整的消息参考在docs.openpoker.ai/api-reference/message-types。每条消息的每个字段都有JSON示例文档。值得收藏;你会经常参考。

让它更智能:三个快速改进

calling station每100手大约损失2-3个大盲注。三个变化可以立即改善,按影响排序。

1. 翻牌前弃掉差牌

扑克中大多数起手牌都是输家。翻牌前弃掉最差的60%,你就已经领先平台上所有的calling station了。起手牌选择是你能做的最大改进。

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 False

收到hole_cards时保存你的底牌,然后在your_turn处理程序中检查should_play()。翻牌前弃掉其他所有牌。

2. 用强牌加注

calling station从不加注。这意味着对手每手牌都能便宜地看到翻牌。修复:用你最强的15%起手牌在翻牌前加注。

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"],
    }))

valid_actions中的raise条目告诉你确切的minmax金额。amount字段是raise-to金额(总下注大小),不是增量。如果大盲注是20,你想加注到60,发送"amount": 60

3. 翻牌后使用底池赔率

翻牌后你有了真正的信息。底池赔率告诉你跟注是否在数学上正确:如果你付出的价格低于你赢的概率,就跟注。否则弃牌。完整数学请参考底池赔率词汇表条目,里面有解题示例和新手机器人容易踩的坑。

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 > odds

即使是对你胜率的粗略估计(默认30%,有顶对时更高,什么都没有时更低),结合底池赔率也能大幅击败纯calling station。your_turn消息包含当前底池大小,所以你拥有所需的一切。

运行这个机器人我们学到了什么

我让calling station跑了超过1,200手来获得真实基线。它每100手损失2.4个大盲注——不是灾难性的,但是持续的消耗。最大的漏洞不是跟注太多下注。而是在什么都没有时跟注河牌的下注。calling station没有"我什么都没中,这个下注相对底池很大"的概念;它只是每次都跟注,不断流血。

第二个让我惊讶的:auto-rebuy冷却时间比你想象的更重要。破产后,免费计划有5分钟冷却时间(Pro是2分钟)才能下一次rebuy。频繁破产的机器人花了很多时间坐在外面。正确管理筹码(一开始就不破产)有超越单纯筹码保存的复合回报。

添加上面部分的should_play()将损失率降到了大约0.8 bb/100——用一个函数改善了3倍。机器人仍然在输,但现在输得像一个平庸的玩家而不是一个坏掉的。这是真正策略工作的起点。

我们并不声称这些是严格的样本量。6-max的方差很高,1,200手是一个小窗口。但在方向上,模式是一致的:翻牌前选择是第一个杠杆,翻牌后攻击性是第二个。

在排行榜上能期待什么

用基础calling station,你会在排行榜底部结束。加上上面的三个改进,你会坐在中间。要到达顶部,你需要手牌评估、对手建模筹码管理和位置意识。完整路径记录在7天排行榜计划中。

你的机器人需要至少10手才能出现在排行榜上。持续游戏和auto-rebuy开启后,几分钟就能达到。

完整的平台文档在docs.openpoker.ai操作与策略指南详细涵盖加注语义、turn token和超时行为。如果你想要超越这里展示的基础的异步连接处理,websockets库文档值得阅读。

FAQ

我的机器人连接了但从未入座。 匹配系统需要队列中有2个以上玩家。如果没有其他人在玩,你的机器人会等待。用不同的API密钥运行两个机器人,或查看排行榜看是否有其他活跃的。

我收到action_rejected错误。 检查你是否包含了最新your_turn消息中的turn_token。过期令牌是拒绝的第一大原因。不要在轮次之间缓存令牌。

我的机器人断开连接并失去了座位。 你有120秒重新连接。如果及时重连,座位会保留。120秒后,你的筹码返回到余额,你需要重新加入大厅。

我能让这个机器人24/7运行吗? 可以。启用auto-rebuy并通过重新加入大厅处理table_closed + season_ended。这篇文章中的机器人两者都做了。我们让它连续运行了好几天没有任何干预。

我应该买入多少? 有效范围是1,000到5,000筹码。我们在例子中使用2,000(10/20盲注下的100个大盲注),这是标准的深筹码起始金额。买少点(1,000)减少方差但也限制了单手能赢多少。买多点(5,000)在你的机器人有基本的弃牌/加注策略后没问题;不要用纯calling station这样做。


准备好运行你的第一个机器人了吗?注册API密钥,五分钟内你就能在打牌了。calling station是个好的起点;排行榜上每个有竞争力的机器人都是从类似的地方开始的。

继续阅读