Files
hjha-server/docs/pdk-bot.md
xiaoou 61948049b7 docs: 统一文档 pdk-bot.md + 清理冗余
合并 3 个分散文档为单一 docs/pdk-bot.md:
1. 概述与架构 — 外挂设计/信息隔离/策略可插拔
2. 算法说明 — PdkBot启发式 + IsmctsBot蒙特卡洛 + C路径展望
3. 引擎整合 — 对 PdkGameMain 的 8 处最小改动清单
4. 未来优化 — 短期/中期/长期路线图

删除旧文件: bot-architecture.md bot-algorithms.md ismcts-design.md
清理运行时产物: output.txt gamepack/
2026-07-07 13:34:08 +08:00

7.8 KiB
Raw Blame History

PdkBot — 跑得快自动出牌机器人

1. 概述与架构

1.1 项目定位

PdkBot 为 hjha-server 跑得快游戏提供全自动对局能力。三个机器人在 Console 模式下相互对弈,模拟真人玩家的出牌决策。

1.2 外挂式架构(非侵入)

PdkGameMain.WaitWanJiaShuRu()
  ├─ BotMode == false → Console.ReadLine()(人工输入)
  └─ BotMode == true  → PdkBotView → IPdkBot.DecidePlay()

机器人只在两个决策点介入:

  • case 3 包庄DecideBaoZhuang(view)
  • case 4 打牌DecidePlay(view)

完全不修改游戏状态机、规则引擎、发牌逻辑或结算逻辑。

1.3 信息隔离

PdkBotView 严格裁剪游戏状态,机器人只能看到

信息 来源 说明
自己手牌 CardPack.Cards[myPos-1] (GameState==1) 16 张起
桌面最后出牌 Info.MaxPlayCard GameNum==0 表示新一轮
各家剩余张数 Info.ShenYuCard[] 跑得快公开信息
各家炸弹数 Info.ZhaDans[] 公开统计
游戏规则 Rule 关牌/经典/15张、必压等
游戏阶段 Info.GameZT 3=包庄 4=打牌

不能看到:其他玩家的具体手牌 (CardPack.Cards[other].CardInfos)。

1.4 策略可插拔

public interface IPdkBot
{
    bool DecideBaoZhuang(PdkBotView view);
    PlayOutCardPdkF DecidePlay(PdkBotView view);
}
实现 文件 描述
PdkBot PdkBot.cs 5 维启发式评分(快速基线)
IsmctsBot IsmctsBot.cs 蒙特卡洛搜索(当前默认,推荐)

切换:改 PdkGameMain.InitBots() 一行 new IsmctsBot()new PdkBot()

1.5 文件清单

文件 职责
PdkFriendServer/Logic/PdkBot.cs IPdkBot 接口、PdkBotView、PdkBot 启发式实现
PdkFriendServer/Logic/IsmctsBot.cs ISMCTS 蒙特卡洛搜索机器人(默认)
PdkFriendServer/Logic/PdkGameMain.cs BotMode 开关、BuildBotView、WaitWanJiaShuRu 集成点
hjha-console/Program.cs main.BotMode = true 启动入口

2. 算法说明

2.1 三条路径对比

A: 启发式 (PdkBot) B: ISMCTS (IsmctsBot) C: CNN+MCTS
最优性 高(接近最优) 理论最高
延迟 <1ms 50-200ms 10-50ms
代码量 100 行 300 行 1000+ 行 + 训练管道
训练需求 4 GPU × 2-5 天
预训练模型 不需要 不需要 不存在
状态 已实现 已实现(默认) 未实现

2.2 PdkBot启发式评分已实现非默认

GetTipCard() 返回的每个合法出牌方案打分:

ScorePlay():
  1. 直接获胜(remainingAfter==0)       +10000
  2. 对手威胁(left/right ≤3)          +300/500
  3. 牌效率(剩余越少分越高)             (20-剩余)×15
  4. 炸弹管理(对手快赢+400/自己快赢+200/留着-80)
  5. 出牌量(一次出多张更好)             cardsOut×8
  6. 先手权(非pass)                    +60
  7. 黑桃3首局必出                      +150

ScorePass():
  基础 -80, 对手快赢追加 -400

2.3 IsmctsBot蒙特卡洛搜索当前默认

核心原理

不完全信息蒙特卡洛树搜索——对每个候选出牌方案,模拟 N 局随机对局到结束,统计胜率,选胜率最高的方案。

DecidePlay(view):
  unknownPool = 全部48张 - 我的手牌 - 已知已出牌

  for each candidate in GetTipCard():
    模拟 800 局:
      ├─ 对手手牌从 unknownPool 随机采样
      ├─ 应用我的候选出牌
      └─ 三人贪心策略(RolloutPolicy)打到终局
    胜率 = 我获胜的局数 / 800

  return 胜率最高的方案

RolloutPolicy模拟策略:用 GetTipCard 取第一个方案(最小牌),快速推进对局。不需要最优——大量采样后统计收敛。

包庄决策

DecideBaoZhuang(view):
  模拟 600 局完整对局 → 统计我第一个出完的概率 P(win)
  P(win) ≥ 45% → 包庄

为什么 45% 而不是 50% 包庄是零和博弈中的多方决策。你不包但别人包了,你也要扣双倍。阈值略微向攻方倾斜以弥补防守的机会成本。

性能

模拟次数 延迟 胜率估计精度
200 ~30ms ±7%
800 ~100ms ±3.5%
2000 ~250ms ±2%

当前默认 800 次。


3. 引擎整合(对原项目的最小修改)

3.1 修改清单

文件 行号 改动 原因
PdkGameMain.cs L46-51 + IPdkBot[] _bots / + BotMode bot 注入点
PdkGameMain.cs L60-66 + InitBots() 懒初始化 3 个 IsmctsBot
PdkGameMain.cs L68-82 + BuildBotView(pos) 仅取可见信息构建视图
PdkGameMain.cs L279 GamePack.Info.WhoPlay = (byte)(i+1) 包庄轮询时修正位置
PdkGameMain.cs L543 + if(DeskGameDo==null) return; Console 模式 NPE 守卫
PdkGameMain.cs L632-710 WaitWanJiaShuRu 加 bot 分支 bot/人工双通道
PdkGameMain.cs L491-494 C:\Users\..../gamepack/ 跨平台路径
Program.cs L14 main.BotMode = true 启动时开启 bot

3.2 未修改的部分

以下完全不动:

  • 游戏状态机GameFaPai → GameBaoZhuang → GameDaPai → GameOver
  • 规则引擎PdkCardAlgorithm、PdkRule
  • 发牌逻辑GameFaPai
  • 结算逻辑GameOver
  • 所有网络/服务器模块

3.3 配置

Program.cs 中设置:

var main = new PdkGameMain(null);
main.BotMode = true;   // true=自动对局, false=Console.ReadLine
main.Test();           // 3 人自动打到结算

4. 未来优化

4.1 短期(提升胜率)

方向 描述 难度
RolloutPolicy 加强 模拟中用启发式代替纯随机最小牌,提高每局模拟质量
自适应模拟次数 手牌多时 1000 次,手牌少时减少到 200 次
对手建模 记录对手出牌倾向(激进/保守),调整模拟中的对手策略
包庄阈值学习 跑 1000 局统计分析最优 P(win) 阈值

4.2 中期(引擎能力)

方向 描述 难度
支持更多玩法 经典玩法16 张必压、15 张玩法,通过 Rule 参数切换
AI vs 人类 将 BotMode 扩展到单个位置(如 pos1=人类, pos2/3=bot
回放分析 每局结束后输出 .json 回放文件,分析 bot 决策质量
性能基准测试 跑 1000 局自对弈统计 PdkBot vs IsmctsBot 胜率

4.3 长期C 路径CNN + MCTS

方向 描述 依赖
Deep Monte Carlo 借鉴 DouZero (ICML 2021) 方法CNN 编码手牌 + MCTS 搜索 4 GPU × 3 天训练
ONNX 推理 PyTorch → ONNX → ML.NET 加载,嵌入 C# 推理 Python 训练管道
自对弈训练 跑得快状态/动作编码,自我对弈产数据 → CNN 策略网络 RLCard/RLlib

C 路径的技术可行性已验证DouZero 在斗地主上击败所有 344 个 AI 对手,排名 Botzone 第一),但目前不存在跑得快的预训练模型,需要从零训练。详见 DouZero 论文: https://arxiv.org/abs/2106.06135


5. 运行

cd ~/projects/hjha-server
dotnet run --project hjha-console

输出示例:

=== HJHA Console Mode - 跑得快 Bot对局 ===
[ISMCTS pos1] 包庄评估: P(win)=31% → 不包
[ISMCTS pos2] 包庄评估: P(win)=52% → 包庄
[ISMCTS pos3] 包庄评估: P(win)=63% → 包庄
[ISMCTS pos2] 800sims winRate:3% => DanZhang:1c
[ISMCTS pos3] 800sims winRate:5% => DanZhang:1c
玩家1 输赢32
玩家2 ,输赢:-64
玩家3 输赢32
test over