Перейти к основному содержимому
E

dsh-poker

eapp1e/dsh-poker

Texas Hold'em table for DeepSeek Harness: model-callable poker tools, six heuristic opponents, an optional coach, and a browser table whose buttons never post a chat message.

Установка

dsh plugin --profile web add github:eapp1e/dsh-poker

README

dsh-plugin-poker

DeepSeek Harness 的德州扑克插件:无限注规则、机器人对手、真实感牌桌,教练模式讲解打法与赔率。

Texas Hold'em table for DeepSeek Harness: model-callable poker tools, six heuristic opponents, an optional coach, and a browser table whose buttons never post a chat message.

德州扑克牌桌与教练窗口

浏览器牌桌面板(左:绒面牌桌、行动按钮与下注尺度助手;右:教练窗口的实时推理)。

DeepSeek Harness 的无限注德州扑克插件,一个包提供两半:

  • 宿主半:确定性德州引擎 + 6 个模型可调用工具(开桌 / 行动 / 看牌 / 下一手 / 机器人决策 / 胜率)。
  • 浏览器半:Web GUI 把每次工具调用渲染成可视化牌桌卡片,另有侧栏常驻面板——所有按钮直连宿主路由,从不往对话里发消息。

你在对话里用自然语言出牌("跟注""加注到 400""弃牌"),模型调用工具推进牌局;也可以直接点面板按钮,牌桌状态由引擎维护。

特性

  • 完整规则实现:最小加注、不足额全下不重开行动轮、边池分层与合并、未获跟注退款、所有人全下自动发完摊牌;7 选 5 牌力评估;整局可从日志复现(状态内 PRNG)。
  • 六种性格对手:岩石 / 紧凶 / 松凶 / 跟注站 / 疯子 / 老练,翻前按位置与价格定范围,翻后按牌面与赔率下注,并会根据桌上其他人的实际倾向动态调整自己。
  • AI 对手:把若干座位交给任意 OpenAI 兼容接口(/v1/chat/completions)出牌;超时或返回不合法动作时自动退回内置策略,API Key 只存宿主、绝不回传页面。
  • 可选教练层:局面、读牌、底池赔率、MDF、SPR、复盘与概念卡,数字是实时算出来的;一键整层关闭。
  • 2–9 人桌,所有金额附 BB 换算;下注尺度助手给出 1/3–1 池的标准尺度与牌面判读。
  • 零运行时依赖、零构建步骤,npm test 自带 6 个套件 1180+ 条断言。

安装

dsh plugin --profile web add dsh-plugin-poker     # 从 npm
dsh plugin --profile web add link:<本仓库路径>    # 或从本地目录

安装后重启 dsh web 并刷新一次页面(客户端 boot graph 烧在页面 HTML 里)。

开发期也可以手工把一行插入 profile 的 cordis.patch.yml(免重启热组合):

- insert:
    - id: poker
      name: 'dsh-plugin-poker'

两种方式只能选一种,同时使用会加载两次并在工具名上冲突。

快速开始

开一桌德州扑克,3 个机器人,起始筹码 5000,盲注 25/50

之后自然语言出牌即可:"跟注"、"加注到 600"、"我现在胜率多少?"、"让机器人认真想想再打"。 侧栏页脚点 ♠ 牌桌 打开浮动面板:绒面牌桌、座位卡、底池筹码、行动按钮与牌局记录都在里面,可拖动、可固定。 牌没开时面板直接给人数选择器(2–9 人)与"开一桌"按钮。

工具

工具作用
poker_new_table开桌并发第一手牌。可选机器人数(1–8,默认 5 = 六人桌)、性格、筹码、盲注、随机种子。
poker_action执行动作:fold / check / call / bet / raise / allin。amount 是"加注到"的本轮总额。
poker_next_hand当前手结束后发下一手。
poker_table读取当前牌桌状态与最近记录。reveal: true 仅供调试(显示全部底牌)。
poker_opponent切换机器人脑袋(auto 内置策略 / model 模型逐手决策),或为等待中的机器人提交动作。
poker_equity蒙特卡罗估算某席胜率(默认 1500 次)。

对手

内置策略:性格只调松紧、激进度与诈唬率,底层规则相同——翻前按"身后还有几人待行动"查开池范围;面对加注按"价格 + 位置"决定继续范围(压上筹码的加注只跟真货);翻后比较牌力与底池赔率而不是固定门槛;下注尺度跟牌面走(干燥面小注、湿润面保护),也跟对手走(对跟注站停止诈唬)。每个座位都在数别人实际怎么做,并据此微调自己(幅度有界,性格始终认得出来)。实测 3000 手六人桌:白送大盲从 23% 降到 10%,跟全下的范围收成真正的强牌。

AI 对手:设置页(设置 → 德州扑克)填 Base URL / API Key / 模型后,前 N 个座位由该接口出牌。接口只许回一个 JSON 动作;超时(默认 8 秒)、离线或胡说八道都退回内置策略,牌局不中断。密钥存在宿主用户设置里,GET /poker/settings 只回是否已保存、不回传密钥。

教练

独立的可拖动窗口,七个板块:🧭 局面、🃏 你的牌、🎯 下注逻辑与建议、🔍 读牌、🧮 数学、📝 复盘、🎓 本手概念。 建议带具体牌型、位置与人数("弃牌:A6o 在 UTG 太弱(后面还有 5 人)"),动作按钮上方重复同一句话并高亮对应按钮; 读牌是量化的——范围宽度由位置、动作与尺度推出,胜率对抗收窄后的范围用蒙特卡罗算出,带 ±误差。 教练只看公开信息,不读对手底牌(有测试断言守着)。💡 一键整层关闭。

两种风格:简洁 / GTO

风格可以在两处切换:设置里的教练风格(改完立即生效,不用点保存),或者教练窗口标题栏上的 简洁 | GTO 开关 —— 后者更顺手,因为"这一手要不要看 EV"是当下的判断,不是一次性配置。

切换会记住(写进设置),并且立刻改变屏幕上的分析:宿主会重存一次当前牌桌(设置就是在那一步盖到牌桌上的), 于是面板拉到的下一份视图已经是新风格。万一没变,窗口会直接写明原因("已保存;当前宿主还不认得教练风格, 重启一次 dsh web 后生效")—— "点了没反应"是最难自己排查的一类失败:宿主侧新增的代码要重启才会加载, 而这一点从界面上看不出来。设置页保存后也会告诉你处于哪种状态(普通「已保存」,还是需要重启一次)。

GTO 风格把同一个局面换成 范围 + 无差点 + EV 的语言,多出两个板块(♟ GTO 建议 / ♟ GTO 数学 / 🧩 范围与角色):

  • 无差点算给你看:面对下注时给出需要的胜率(下注 ÷ 最终底池)、MDF 最低防守频率(底池 ÷ (底池+下注))、 以及对手那个尺度对应的价值:诈唬比例——例如半个池:跟注要 25%、防线 67%、每 3 手价值配 1 手诈唬。
  • 每个合法动作的 EV(弃牌为 0 基准):跟注 = 胜率 × 最终底池 − 跟注;过牌 = 胜率 × 底池; 下注 = 弃牌率 × 底池 + (1−弃牌率) × (被跟后胜率 × 最终底池 − 风险)。算式连数字一起列出来,你可以自己按一遍。 两处细节是"算式看起来对、结论很离谱"的来源,都按真实牌理修正了:
    • 被跟后的胜率要打折:会跟注的范围比你现在对抗的范围更强,而且投入筹码占筹码量的比例越大,跟注范围越紧 (开池 2.5BB 几乎什么都会跟,200BB 全下只有顶端会跟)。早先直接用原始胜率,于是全下永远"很赚"。
    • 最终底池要按对手实际补的算:底池里已有 100、你加到 250 时对手只补 150(不是再放 250), 早先的 底池 + 2×风险 每次加注都凭空多算了一份底池。
  • 弃牌率来自实测:用引擎里每个座位的"面对下注/弃给下注(翻后单独计数)",再按当前还在手的对手数取幂 ——两个人各弃 60% 意味着这注只有 36% 直接收池,不是 60%(这条差异会让诈唬看起来"免费")。 加注的弃牌率另算:面对真加注(超过一个大盲)才打折——盲注不是"下注",对着盲注开池不该被当成反加。
  • 对手全下时没有加注这条线:没人能再弃牌(弃牌率 = 0),也没人能再跟你的加注——加注只是把多出的筹码退回来。 所以这时 EV 表里只有跟或弃两条,并会说明原因。(引擎也跟着改了:没人能跟的加注/全下按钮不再出现, 免得桌上多出一个"全下 20,850"却只比跟注多 250 的选项。)
  • 近似混合频率:EV 接近的两条线按接近程度分配频率(下注 92% / 过牌 8%),并说明为什么; 差距大就直说"不需要混合"。混合是按line(下注/过牌/跟注/弃牌)分的,不是同一动作的四个尺度。 "接近"的门槛很小(底池的 6%,最低 0.2BB):早先门槛宽到 3BB 时,一条落后 40 筹码的线也能拿到 30% 的频率 —— 那已经不是"混合",而是在 30% 的时候建议你打一手更差的牌。
  • 三条保底规则(保证建议本身是对的,而不是"看起来像 GTO"):
    1. EV 明显更差的线绝不推荐 —— 弃牌不要钱的时候,不会让你去跟一个 −40 的注;
    2. EV 等价时按范围选 —— 单街 EV 看不到翻后位置的价值,所以它一度会弃掉按钮位该开池的手牌; 现在这种情况交给引擎自己那套位置范围定夺,EV 表负责解释"为什么两条线都说得过去";
    3. 不制造假混合 —— 如果是按范围选出来的,就明说"按范围选:开池",不会在下面又列出"弃牌 60%"自相矛盾。
  • 范围角色:把这手牌归为 价值 / 半诈唬 / 摊牌价值(bluff-catcher)/ 边缘 / 空气,并说明该类牌在 GTO 计划里怎么打。
  • 翻前也算:翻前没有成牌、听牌和 MDF,但它有这一层的其它全部要素 —— 这手牌在起手牌里排前 X%、 跟注要多少胜率、以及弃牌 / 跟注 / 开池 2.5BB / 3BB / 4BB / 3-bet / 全下各自的 EV(翻前的尺度按大盲的倍数给, 因为没人用"几分之一池"聊翻前)。而且会明说"翻前没有 MDF 可言",不硬套翻后的概念。

诚实的边界:这是一个牌面的算术,不是求解器——没有全树、没有逐街范围迭代,未来街用一个粗算项代替。 它给的是"这一步的赔率与 EV 到底是多少、为什么两条线都说得过去",这正是桌边能用的那一半 GTO。

(翻前曾经被排除在外,理由是"没有公共牌时单街 EV 没东西可算"—— 这是个错误:大多数决定就是翻前, 于是玩家切换风格后发现内容完全没变。现在翻前也有自己的无差点与 EV 表。)

设置

设置 → 德州扑克(注册在 settings.section 的独立一页),五组:

分组内容
常规启用插件(关掉即隐藏侧栏入口与牌桌卡片;工具照常可用,不会把进行中的对话搞断)、教练层、教练风格(简洁 / GTO)、建议徽章、牌局记录
新牌桌默认值机器人数、对手性格、起始筹码、大小盲 —— 只在调用方没自己传时生效,模型调工具时仍以它传的参数为准
面板与回放回放节奏(0.5× / 标准 / 2×,整条时间轴一起缩)、页面加载时自动展开
AI 对手启用、席位数、Base URL、API Key、模型、温度、超时、JSON 模式、最大回复长度、测试连通
关于与更新安装版本(读自本安装包的 package.json,不用手填)、仓库(可改成你自己的 fork)、更新 / 重新检查
  • 自动检查更新:页面加载时自动查一次(宿主缓存 10 分钟,所以一个窗口只会打扰 GitHub 一次)。有新版本时, 侧栏入口上会多一个小黄点(鼠标悬浮写明版本),设置页打开就直接显示结果 —— 不需要你先点任何按钮。
  • 先给结论,再给按钮:这一行平时显示的是一句话 —— 已是最新版本(v0.1.0) / 有新版本 v0.2.0(当前 v0.1.0) / 暂时查不到最新版本。只有真的有新版本时才出现「更新到 v0.2.0」按钮, 旁边还有一个「本次忽略」把它按下去(本次会话不再提);已是最新时按钮位置是一个安静的「重新检查」链接 (绕过缓存再查一次)加一个「发布页」链接。
  • 一键更新会真的去更新:
    • 插件是 git 检出(目录里有 .git)→ 跑 git -C <目录> pull --ff-only(本包还没发布到 npm,npm install 只会 404);
    • 否则 → 在安装目录里按 lock 文件选 pnpm add / yarn add / npm install,装的是发布页报出的那个确切版本。
    • 跑完把结果(含命令输出尾部)写在按钮旁边,并提示"刷新页面;若提示宿主还不认得,再重启一次 dsh web"。
    • 页面上不能指定任何东西:包名、版本、目录都由宿主决定,版本号还要先过一遍严格校验 —— 页面只能说"要这个仓库的最新发布"。
    • 失败也不装死:把真实报错、手动命令和发布页链接一起给你。
  • 说明放在悬浮里:设置页每行的解释挂在行本身(鼠标悬浮显示),正文只留标签和控件 —— 一页设置不该把 自己的说明书全印在屏幕上。页头也只有标题,"保存后立即生效"跟着保存键走。
  • 测试连通(就在 AI 那一组里,挨着它测的那些字段):点一下就用屏幕上当前的值(含还没保存的 Key)发一次最小请求, 回你三种结果之一——✅ 连通:382ms · 模型 · 回复"pong"、❌ 失败:HTTP 401 · invalid api key、❌ 失败:超时(8000ms)。测试不落库、不改设置。
  • 保存:页脚只有「保存」和旁边一句"保存后立即生效"(面板级的开关——回放节奏等——刷新页面后生效,这句挂在悬浮里)。
  • 密钥只进不出:Key 写进宿主的用户设置文档;GET /poker/settings 只回 hasApiKey: true/false,页面把它渲染成密码框(已保存时显示占位点)。
  • 页面自绘:GET /poker/settings 除了值,还回一份字段描述(分组、控件类型、上下限、选项、帮助文字、action/info 这类特殊控件),设置页照描述画控件 —— 以后加设置只改 lib/settings.js 一处,前端一行都不用动。页面标题用的是和侧栏入口同一枚手绘黑桃(设置列表本身没有 per-plugin 图标位,所以标记画在模块内部)。
  • 保存即生效:插件开关、教练层立刻生效;AI 席位数会平移到当前这张牌桌(不用重开);面板级的开关(记录、徽章、节奏)刷新页面后生效。

浏览器牌桌

  • 两个表面:对话内的牌桌卡片(每次工具调用替换成当时局面)+ 侧栏页脚的常驻浮动面板(带实时摘要,跨会话保留位置与状态)。
  • 直连宿主:按钮点击走 POST /poker/action 等同源路由,规则拒绝只在面板上显示红色告示,一个字不进对话;路由不可用时才退回对话。
  • 过程回放:机器人行动按真实节奏一拍一拍放(含落牌动画与行动进度),弃牌后仍能看到整手打完。
  • 细节:金额全部带 BB 药丸(筹码/BB 双向联动输入);尺度助手按底池与翻前 BB 两种口径给标准尺度;庄家按钮、成牌名、台词、结算徽章齐全;浅色/深色主题都能读。

架构

lib/
  cards.js      # 牌、种子 PRNG、洗牌
  evaluator.js  # 牌力评估与牌型名
  engine.js     # 牌局状态机(下注轮、边池、摊牌)
  bots.js       # 内置策略对手 + 蒙特卡罗胜率
  render.js     # 模型可读的牌桌文本
  ai.js         # AI 对手(OpenAI 兼容接口、失败回退)
  settings.js   # 全部设置:schema + 分组描述(设置页据此自绘)+ 掩码
  gto.js        # GTO 风格:无差点、各动作 EV、近似混合频率、范围角色
  update.js     # 检查更新:GitHub 最新 release/tag 与本地版本比较
  coach.js      # 教练(可关)
  impl.js       # 工具实现层(可热重载)
  index.js      # 入口:6 个工具、/poker 路由、牌桌注册表
  client.js     # 浏览器半:牌桌卡片 + 侧栏面板
test/           # 6 个套件,见下

设计要点:引擎是唯一事实来源,工具只做翻译;牌桌状态按会话隔离(WeakMap)+ 一张全场共享的"当前牌桌";impl.js 及其依赖改完即热重载(牌桌不丢,改坏了自动回退上一份可用代码),只有入口层和 patch 需要重启。

测试

npm test              # 全部六套,一次跑完(零依赖,不需要 npm install)
node test/run.mjs     # 引擎与不变量(含 2000-3000 手随机对局)
node test/host.mjs    # Host 契约(工具定义、教练 payload、不泄露底牌)
node test/client.mjs  # 客户端 bundle(无浏览器)
node test/coach.mjs   # 教练(含 GTO 风格的无差点与 EV)
node test/hot.mjs     # 热重载
node test/pack.mjs    # 打包契约(dsh.bundle、一行描述、许可证等)

每次 push 都会在 GitHub Actions 上跑(Node 20 / 22 矩阵)。

License

MIT

Похожие плагины