統計は冷酷だ

ポーカー、ゲーム、サッカーなどについて考えていきたい。自分の為にもブログを更新していこうと思う。

piosolverの使い方(SB3betpotの例)

お久しぶりです。以前書いてる途中のものがあったので結構内容を忘れてしまっていましたが公開したいと思います。
やっぱりアウトプットは大事だと思うので、ポーカー以外にも以後積極的に色々自分が学んだことなど書いて行きたいと考えています。
piosolverの使い方を自分用にメモった記事がこのブログで一番閲覧されていたのでタイトルもそうしてみました。
今回は少しpiosolverとプログラミングを用いて何かいい結果がみられないかと思って使ってみた一例です。
最近はプログラミングなどに手を出しているのですが、プログラミングは努力量をリアライズしやすくおすすめです。


こんにちは、またブログを書くのが久しぶりになってしまいました。
多分長くなります。

間違っている点がある可能性は十分あります。
何かアドバイスなどあればぜひお願いします。

前回はこちら

今回はpiosolverの使い方も含めて書いていこうと思います。

よくあるSB3bet vs BTNのGTOについてです。
pot21bb, eff90bb、レンジなどの設定は以下のようにして計算してます。

f:id:takoagemat:20181111043013p:plainf:id:takoagemat:20181111043018p:plain
左:レンジ、右:ベットサイズなど

ちなみにこれらの設定したレンジ、ベットサイズなどのデータは保存して次回以降すぐに使えるようにすることができます。


flop subsetにはこの184flopを利用しています。

www.piosolver.com


generate script -> flop subsetを貼り付ける -> generate script -> run this script
で貼り付けたflopについて計算できます。

よくあるシチュエーションの計算は1755種類全てのflopで計算してしまった方が楽かもしれません。
最近は自分もそうしています。
詳しくはmaspyさんのノートがわかりやすいと思います。

note.mu


twitterで頭いい人がこれ言ってたなーって時はtwitterの高度な検索とか便利かもしれません。
思ったこととかが結構前につぶやかれたりします。

twitter.com


また、出先でpiosolverを動かしたい時はChromeリモートデスクトップが便利だと思います。
なぜか大学のWifiだと使えないのでスマホでpcを一時的にテザリングして使ってます。

今回は特定のハンドでGTOが一つのアクションしか取らない場面について取り上げてみようと思います。

piosolverでaggregate reportをする際に1326handごとのレポートも出すようにすることでハンドごとのEVやエクイティ、ベット頻度を見ることをできます。

aggregate reportで得られたデータ(それぞれのハンド、戦略でのEVが書いてあるファイル)を整理します。
以下はpythonでのコードです。(色々なものを参考にして作りました。)

import pandas as pd
import collections

file = 'root.csv'
df = pd.read_csv(file)
Flop = df["Flop"]
Hand = df["Hand"]
Betb = df["BET64"]
Bets = df["BET32"]
Check = df["CHECK"]

rank_to_num = {'A':14, 'K':13, 'Q':12, 'J':11, 'T':10, '9':9, '8':8, '7':7, '6':6, '5':5, '4':4, '3':3, '2':2}
High, Middle, Low = [], [], []
Boardsum,Rainbow,Twotone,Monotone = [],[],[],[]
ActionBet64,ActionBet32,ActionCheck = [],[],[]
Difference = []
CB=[]

for i in range(35878):
    Card, Suit = [], []
    EV = []
    for j in range(3):
        Card.append(rank_to_num[Flop[i][j*3]])
        Suit.append(Flop[i][j*3+1])
    for k in range(2):
        Card.append(rank_to_num[Hand[i][k*2]])
        Suit.append(Hand[i][k*2+1])


    EV.append(Betb[i])
    EV.append(Bets[i])
    EV.append(Check[i])

    Boardsum.append(Card[0]+Card[1]+Card[2])

    High.append(Card[0])
    Middle.append(Card[1])
    Low.append(Card[2])

    s = collections.Counter(Suit)
    Rainbow.append(s.most_common()[0][1] == 1)
    Twotone.append(s.most_common()[0][1] == 2)
    Monotone.append(s.most_common()[0][1] == 3)

    BestBet64 = False
#EV差があるハンドを抽出    
 if EV[0]-max(min(EV[0],EV[1]),min(EV[0],EV[2]),min(EV[1],EV[2])) > 3.0:
        BestBet64 = True
    ActionBet64.append(BestBet64)

    BestBet32 = False
    if EV[1]-max(min(EV[0],EV[1]),min(EV[0],EV[2]),min(EV[1],EV[2])) > 3.0:
        BestBet32 = True
    ActionBet32.append(BestBet32)

    BestCheck = False
    if EV[2]-max(min(EV[0],EV[1]),min(EV[0],EV[2]),min(EV[1],EV[2])) > 3.0:
        BestCheck = True
    ActionCheck.append(BestCheck)

    CB.append(max(EV[0],EV[1])-EV[2])

    Difference.append(max(EV[0],EV[1],EV[2])-max(min(EV[0],EV[1]),min(EV[0],EV[2]),min(EV[1],EV[2])))

df["CB"] = CB
df["Diff"] = Difference
df["Bet64"],df["Bet32"],df["Check"] = ActionBet64,ActionBet32,ActionCheck
df["High"], df["Middle"], df["Low"] = High, Middle, Low
df["Boardsum"] = Boardsum
df["Rainbow"], df["Twotone"], df["Monotone"] = Rainbow, Twotone, Monotone

df.to_csv(file, index=False)

こんな感じのcsvファイルが出来上がります。

f:id:takoagemat:20190809134639p:plain
root.csv

ここでは一番EVが高いアクションと2番目に高いアクションのEVの差が3以上あるハンドかどうかを認識させています。
EV差が大きい方から重要というわけではないかもしれませんが、GTOがそのアクションしか取らないシーンを抜き出して考察することはプラスになるのではないかと考えています。

このファイルのDIffでソートすればGTOでのEV差が大きいシチュエーションを上からみていくこともできます。

期待値3(1bb 100)の差があれば今回計算した選択肢でのGTOがそのアクションしか取らないとして十分だと思います。
cfrのアルゴリズムでは有限な計算回数Tではアクションを取る確率が0にはなりませんが、EVの差がexploitableの数値から求められる閾値以上ならばGTOがこのアクションを取らないと言えるはずです。(ということを以下から学びました。)

https://twitter.com/maspy_stars/status/1042395618801414145
https://twitter.com/maspy_stars/status/1042380563921825792

こうして整形されたデータをRを使ってプロットしてみます。(こちらも色々参考にしています。)

#Root
par(yaxs="i", xaxs="i")
GTO <- read.csv("root.csv",header=T)
x_name <- "Equity"
y_name <- "CB"
x <- subset(GTO$Equity, GTO$Check == "True")
y <- subset(GTO$Diff, GTO$Check == "True")
plot(x, y, col="green", xlab=x_name, ylab=y_name,cex = 0.3 ,las=1, xlim = c(0,100), ylim = c(0,210), pch = 20)
x <- subset(GTO$Equity, GTO$Check == "False")
y <- subset(GTO$CB, GTO$Check == "False")
par(new=T)  
plot(x, y, col="blue", xlab=x_name, ylab=y_name,cex = 0.3, las=1, xlim = c(0,100), ylim = c(0,210), pch = 20)
legend("topleft",legend = c("CB","Check"),col = c("blue","green") ,pch=19)
title(main="IPaction%")

こんな感じでプロットしてみると以下のような感じになります。(他のシチュエーションも同様に処理してみました。)
一番左がOOPがflopでCBを打つかどうか
真ん中はOOPに32%potのCBを打たれた時のIPのアクション
一番右はIPにBMCBを打たれた時のOOPのアクション
の3つで
一番EVが高い戦略が次点の戦略とどれくらいEVの差があるか(縦軸)とエクイティ(横軸)をプロットしたものです。
(クリックすれば大きく表示されます。)

f:id:takoagemat:20190809144900p:plainf:id:takoagemat:20190809134619p:plainf:id:takoagemat:20190809134654p:plain
左:CBを打つかどうか、真ん中:32%potbetCBを打たれた時、右:32%BMCB打たれた時

SPRが低いのもあってか、フロップでは強いハンドはエクイティが半分くらいのハンドと混ぜてcallで次のストリートに進み、ある程度エクイティがあるハンドをブラフを混ぜてレイズした方がいいという結果のように見えます。