应用CFR实现德州扑克对战
857 字
4 分钟
应用CFR实现德州扑克对战
引言
反事实后悔最小化(Counterfactual Regret Minimization, CFR)是求解不完全信息博弈纳什均衡的经典算法。本文先用最小博弈 Kuhn Poker 给出完整 CFR 实现,再给出在德州扑克中的工程化落地思路(抽象与对手建模)。
1. CFR 核心思想
- 信息集(Information Set):在不完全信息下,玩家对状态的可观测等价类
- 策略:在每个信息集上对可行动作的概率分布 σ(I)
- 后悔值:选择动作 a 与平均策略相比的损失
- CFR 迭代:累计正后悔值 R⁺(I,a),按比例归一化得到新策略(Regret Matching)
2. Kuhn Poker 简介
- 牌:3 张(J, Q, K),每人一张,先手/后手
- 行动:过/跟/加注(精简为 bet/pass + call)
- 结算:摊牌比大小或对手弃牌
3. Python 实现(Kuhn Poker CFR)
import randomfrom collections import defaultdict
CARDS = ['J', 'Q', 'K']
class KuhnCFR: def __init__(self): self.regret_sum = defaultdict(lambda: [0.0, 0.0]) # 每信息集两动作: [pass, bet] self.strategy_sum = defaultdict(lambda: [0.0, 0.0])
def get_strategy(self, info_set): regrets = self.regret_sum[info_set] # Regret-Matching positive = [max(r, 0.0) for r in regrets] normalizer = sum(positive) if normalizer > 0: strategy = [r/normalizer for r in positive] else: strategy = [0.5, 0.5] # 累加平均策略 for i in range(2): self.strategy_sum[info_set][i] += strategy[i] return strategy
def get_average_strategy(self, info_set): s = self.strategy_sum[info_set] total = sum(s) if total > 0: return [x/total for x in s] return [0.5, 0.5]
def cfr(self, cards, history, p0, p1): plays = len(history) player = plays % 2 opponent = 1 - player # 终止判断 if len(history) >= 2: terminal_pass = history[-1] == 'p' double_bet = history[-2:] == 'bb' if terminal_pass: # p 或 bp if history == 'pp': return 1 if cards[player] > cards[opponent] else -1 else: return 1 elif double_bet: return 2 if cards[player] > cards[opponent] else -2 # 信息集:牌 + 历史 info_set = cards[player] + history strategy = self.get_strategy(info_set) # 递归对手价值 if player == 0: v_pass = -self.cfr(cards, history + 'p', p0*strategy[0], p1) v_bet = -self.cfr(cards, history + 'b', p0*strategy[1], p1) else: v_pass = -self.cfr(cards, history + 'p', p0, p1*strategy[0]) v_bet = -self.cfr(cards, history + 'b', p0, p1*strategy[1]) v = strategy[0]*v_pass + strategy[1]*v_bet # 反事实后悔 regrets = [v_pass - v, v_bet - v] if player == 0: reach = p1 else: reach = p0 self.regret_sum[info_set][0] += reach * regrets[0] self.regret_sum[info_set][1] += reach * regrets[1] return v
def train(self, iterations=100000): util = 0.0 deck = CARDS[:] for _ in range(iterations): random.shuffle(deck) util += self.cfr(deck[:2], '', 1.0, 1.0) print(f"Average game value for Player 0: {util/iterations:.4f}") # 输出平均策略 strat = {I: self.get_average_strategy(I) for I in self.strategy_sum} return strat
if __name__ == '__main__': solver = KuhnCFR() avg_strategy = solver.train(50000) for I in sorted(avg_strategy.keys()): s = avg_strategy[I] print(I, { 'pass': round(s[0],3), 'bet': round(s[1],3) })运行后,你将得到接近文献中的纳什近似策略。例如持 K 倾向下注、持 J 更倾向过牌等。
4. 扩展到德州扑克的工程实践
CFR 在完整德州扑克上需要“抽象”以压缩庞大状态空间:
- 信息集抽象:按公共牌纹理、权益(hand strength/board texture)、行动序列分桶
- 行动抽象:将下注空间离散为固定档位(如 0.33pot/0.66pot/pot)
- 公共牌抽样:Public Chance Sampling 或外部抽样(External Sampling CFR)
- 策略表示:表格策略或函数逼近(NFSP/CFR+/Deep CFR 结合神经网络)
- 加速:多进程自对弈,增量保存平均策略,CFR+ 使用正则化与截断加速收敛
实践建议:
- 先在 Leduc Poker(小型公共牌博弈)上验证抽象与 CFR+ 实现
- 逐步接入德扑回合(翻前/翻牌/转牌/河牌)与行动抽象
- 评估用 exploitability 或与固定对手对战胜率衡量
5. 结语
本文提供了可运行的 CFR 基础实现与向德州扑克落地的工程路线。下一步可引入 CFR+、外部抽样、函数逼近策略网络,实现规模化训练与对战。
🧠 文章思维导图
mindmap 引言# 1. CFR 核心思想# 2. Kuhn Poker 简介# 3. Python 实现(Kuhn Poker CFR)# 4. 扩展到德州扑克的工程实践# 5. 结语#
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
引用本文
@article{2024-6-30-应用cfr实现德州扑克对战,
author = {枫语},
title = {应用CFR实现德州扑克对战},
journal = {枫语},
year = {2024},
month = {jun},
url = {https://meiluosi.github.io/posts/2024-6-30-%E5%BA%94%E7%94%A8cfr%E5%AE%9E%E7%8E%B0%E5%BE%B7%E5%B7%9E%E6%89%91%E5%85%8B%E5%AF%B9%E6%88%98/}
}相关文章智能推荐
1
多人博弈AI设计
Game AI三人以上博弈的策略与联盟
2
MCTS算法深度解析
Game AI蒙特卡洛树搜索的原理与变种
3
Alpha Zero算法实现五子棋
AI Applications深度强化学习在游戏AI中的创新应用
4
多智能体强化学习
Reinforcement Learning合作与竞争:MARL 的关键技术
5
1B模型全链路实验计划:MacBook Air M4 本地实战
Deep Learning从SFT到DPO再到部署,用1B参数大模型在Apple Silicon上跑通LLM训练全流程
随机文章随机推荐