Skip to content

Latest commit

 

History

History
266 lines (199 loc) · 11.7 KB

README.zh-CN.md

File metadata and controls

266 lines (199 loc) · 11.7 KB

TexasHoldemSolverJava

release license

README English | 中文

项目介绍

一个完全开源,java实现的高效标准德州扑克和短牌solver, 看看这个 介绍视频 了解更多.

algs

这是一个基于java的德州扑克solver,完全开源,支持跨语言调用(默认支持python和命令行调用),实现了标准德州扑克和德州扑克的一个变种-德州扑克短牌的solver,和piosolver等常见德州扑克solver类似,重点提供翻牌后情况的求解,solver求解结果结果和piosolver对齐。速度上在turn和river上比piosolver快一些,但是flop比piosolver慢。

项目特性:

  • 高效,转牌和河牌计算速度超过piosolver
  • 准确,结果和piosolver几乎相同
  • 完全开源并且免费
  • 拥有一个简单易用的gui界面
  • 支持标准德州扑克和流行的变种玩法短牌
  • 主要聚焦在翻牌后求解
  • 支持命令行和python调用

本项目适合:

  • 德州扑克高级玩家
  • 不完全信息博弈领域研究的学者。

安装

首先需要安装64位 Java Runtime Environment.

下载release 包,release包的结构如下:

--- Solver
 |- resources
 |- java_interface.py
 |- RiverSolver.jar
 |- riversolver.sh

安装就这样完成了,就是这么简单!

其中RiverSolver是德州扑克solver主体程序,java_interface.py 是通过python调用solver的示例程序,其中的测试用例包含了

  • 短牌flop求解示例
  • 短牌turn求解示例
  • 短牌river求解示例
  • 标准德州扑克turn求解示例
  • 标准德州扑克river求解示例

riversolver.sh 包含了命令行调用solver的示例

下载release 包后通过 python3 java_interface.py 来进行测试.

除了需要下载软件本身之外,TexasHoldemSolverJava 还依赖 JRE 11.0.2 作为运行库。如果电脑上没有请安装java JRE 11.0.2。

使用

图形界面

确认你已经安装了正确版本的java(64bit,java 10.x / java 11.x)

双击 riversolver.jar 打开gui.

python 调用方法

虽然 TexasHoldemSolverJava 绝大部分逻辑代码由java撰写,但是默认提供的调用方式是python,所以需要安装额外的一些python依赖。 使用python调用solver需要首先安装如下依赖:

pip3 install jpype
pip3 install numpy
pip3 install yaml
pip3 install networkx
pip3 install matplotlib

python 调用的所有代码均可在release 包中的 java_interface.py中找到。这里简单描述调用过程和一些参数设定。

撰写python代码时,首先保证rsources目录和jar文件(通过release下载)在work dir下,import 各个依赖包:

from jpype import *
import yaml
import numpy as np
import sys
sys.path.append("resources")
from python.TreeBuilder import *

并且启动java虚拟机,加载solver类

startJVM(getDefaultJVMPath(), "-ea", "-Djava.class.path=%s" % "./RiverSolver.jar")
PokerSolver = JClass('icybee.solver.runtime.PokerSolver')

初始化PokerSolver类,这个类主要负责求解策略

ps_holdem = PokerSolver("Dic5Compairer",
    "./resources/compairer/card5_dic_sorted.txt", #加载的compairer字典文件,用于比较牌面大小,holdem和短牌使用不同的字典
    2598961, # 字典行数
    ['A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3'], # 扑克牌牌面
    ['h', 's', 'd', 'c'] # 扑克牌花色
)

以转牌(turn)为例,需要求解具体场景下的策略时,首先和pio solver一样,构建游戏树(game tree)

# 加载一些德州扑克的通用规则(比如check后可以check/raise,raise后可以raise/call/fold)
with open('resources/yamls/general_rule.yaml') as fhdl:
    conf = yaml.load(fhdl)
# 使用RulesBuilder准备将这些规则转化为游戏树
rule = RulesBuilder(
    conf,
    current_commit = [2,2], # 双方玩家下注情况
    current_round =  3, # 当前游戏轮数,1为preflop,2为flop,3为turn,4为river
    raise_limit = 3, # 限制能够raise几次,一般建议限制raise 3次即可
    check_limit = 2, # 能够check 几次,一般就两次,除非是多人游戏
    small_blind = 0.5, # 小盲注大小
    big_blind = 1, # 大盲注大小
    stack = 10, # 游戏双方筹码量,如果两方筹码量不一致,取小,例如player1有 $100 player2 有 $150 ,那么这里应该取小填100
    bet_sizes = ["0.5_pot","1_pot","2_pot","all-in"], # 游戏树中bet 和raise的大小,可以是数字 比如 1,1.5或是以pot为单位,以当前公共奖池中的比例
)
# 根据上面的规则构建游戏树
gameTree = PartGameTreeBuilder(rule)
# 将游戏树存到硬盘上,下次构建使用同样游戏树就不用再建一次了
gameTree.gen_km_json("./.tree.km",limit=np.inf,ret_json=True)

在solver中读取并且在内存中构建这颗游戏树

ps_holdem.build_game_tree("./.tree.km")

开始输入各个求解参数并且求解,这块的各个参数可以参考piosolver。

result = ps_holdem.train(
    "AA:0.5,KK:0.9,QQ:0.8,97,96,87,86,76", # player1 的range,字符串用','间隔开,range写法有"AA:0.5"代表 player有对Arange的权重为0.5;还有一种忽略权重的写法"AA" 代表 player有对A的权重是1,这里的定义和piosolver一致
    "AA:0.8,KK:0.2,QQ:0.3,86,76:0.9", # player 2的range
    "Kd,Jd,Td,7s", # 已经亮出来的公共牌,转牌圈有4张公共牌
    50, # cfr算法求解的轮数
    10, # 打印exploitability的间隔,10为每求解10轮打印一次,注意
    False, # 是否打印debug信息
    True, # 是否采用并行化技术
    "output_strategy.json", # 输出策略json文件地址,如果为None则会在方法调用的返回中直接给出策略json
    "log.txt", # cfr求解中的log会被写入这个文件
    "discounted_cfr", # 求解器方法,支持"cfr"原始cfr算法,"cfr_plus" 更快速的cfr+算法,"discounted_cfr" 作者在这个项目中提出的discounted cfr ++算法,速度最快
    "none", # 是否使用monte coral算法,一般在游戏树非常大的时候用,有两种选项: "none"表示不实用monte coral算法, "public" 表示使用 public chance monte coral 算法,在游戏树非常大的时候会加速收敛
    -1, # 使用的线程数量,1表示单线程,2表示两个线程...,-1表示使用计算机上的cpu数量的线程
    1, # action fork 概率,和求解器性能有关,值域 0~1
    1, # chance fork 概率,和求解器性能有关,值域 0~1
    1, # fork every tree depth,和求解器性能有关,值域 0~正无穷
    4, # fork minimal size,和求解器性能有关,值域 0~正无穷
)

执行上面的代码,求解器就会开始工作,求解时间和游戏树大小,双方range复杂程度,还有计算机配置有关,在我的mac book pro上,求解river肯定可以在1秒内完成,turn的求解一般在10秒内也可以完成,机器配置越好求解越快。

命令行调用方法

参考release 包中的riversolver.sh,调用参数和python接口相同

分析求解器产生的结果

首先求解器运行的时候会输出类似如下的日志:

Iter: 0
player 0 exploitability 1.653075
player 1 exploitability 2.146374
Total exploitability 47.493111 precent
-------------------
Iter: 11
player 0 exploitability 0.040586
player 1 exploitability 0.322102
Total exploitability 4.533607 precent
-------------------
......
-------------------
Iter: 41
player 0 exploitability -0.114473
player 1 exploitability 0.168947
Total exploitability 0.680923 precent
.Using 4 threads

注意其中的 exploitability的收敛情况,一般来说 小于0.5就完全够用了

solver运行完毕后会输出一个output_strategy.json文件包含了求解出来的策略,建议用firefox(对,就是那个浏览器)打开这个文件,根据游戏树的不同大小,这个文件可能会有几kb到几G大

打开后可以看到类似下图的结果:

algs

player : 1

这个字段表示当前节点是player1进行动作

actions:
    0: "CHECK"
    1: "BET 4.0"

而strategy字段下就是不同手牌应该采取的策略:

algs

在strategy的具体每一个项中,则包含了拿到该手牌时的"最佳策略"

algs

比如在上图中的信息就代表player1在拿到 Qd7c (方块Q,梅花7) 手牌的时候,最优策略就是以 34%的概率去check,以65%概率去 bet。

编译release包

一般情况下release包不需要编译,而可以直接从项目release 下载 如果需要对项目进行二次开发,则需要重新编译release包。本项目是一个IDEA项目,需要在IDEA环境下编译release包,具体步骤:

  1. 安装IntellIJ IDEA
  2. 从github上下载本项目,并且加载到IntellIJ IDEA中
  3. 菜单栏 build -> build project 编译项目
  4. 菜单栏 build -> build artifacts -> all artifacts -> build 生成release包
  5. 编译完成的release包可以在工程根目录下的out 路径中找到

对照实验

和piosolver的速度对比实验如下,同一个牌面下turn 和river的速度和piosolver仍处于接近水平, 但是flop比piosolver慢很多,由于flop的代码尚未很好的优化.

flop sample turn sample river sample
piosolver 7.91s 1.5s 0.56s
TexasHoldemSolverJava 98s 4.21s 0.06s

上面实验中的pio格式输入,和结果对比列在下面表格中,任何人均可复现:

flop sample turn sample river sample
输入 (pio格式) flop turn river
输入 (图片格式) flop turn river
结果对比 flop turn river

结果策略上和Piosolver的略微不同是由于TexasHoldemSolverJava采用了和Piosolver略微不同的游戏树构建算法,并且两个算法停止时均为完全收敛.

算法

如图,得益于实现的最新算法的变种 discounted cfr++, 在算法上可以保证比cfr+等传统算法快得多的速度。 algs

c++ 版本

如果你觉得这个java版本还不够快,可以尝试一下我们的c++版本 ,c++版本在turn和river上会比java版本快,但是有两个缺点:

  • 仅支持Linux机器
  • 使用前必须重新编译
  • 没有很好的优化, 在flop的计算上会占用数量惊人的内存空间 比c++版本快5倍以上

License

MIT © bupticybee

联系方式

icybee@yeah.net