統計は冷酷だ

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

リミットHi/LogameのGTO

こんにちは

今回は「The Mathematics of Poker」のP.225のExample18.1の内容に関して書いていこうと思います。



読んでる人いるのかなあ笑
最近は実践的ではない内容ばかり書いていますが、pioの計算が終わったら実践的な内容についても書いていけたらいいと思っています。



今回の内容はリミットゲームのHi/LoポーカーのGTOについてです。


Hi/Loとは強いハンドと弱いハンドを持っている人がそれぞれ半分ずつのpotを獲得できるゲームです。

具体的にはStud8とかFLO8とかです。
ルールはpokerstarsのホームページなどにも載っていると思います。


[0,1]gameの概念を利用するので、何それといった方はこちらからみてもらえると多少はわかりやすいはずです。



note.com





今回想定する状況は以下の通りです。

X,Yの2人のプレイヤーでのリミットゲームのHi/Logameで、
Xはcheck raiseすることができない、
X,Y共にが相手のbet, raiseに対してfoldすることができない、


そして、それぞれのプレイヤーがHi/Loそれぞれのハンドで[0,1]game同様に0%~100%のエクイティを持っているハンドを一様に持っているとします。


X(OOP): Hi/Lo共に[0,1]の一様乱数
Y(IP): Hi/Lo共に[0,1]の一様乱数


Hi/Lo共に数字の小さなハンドが強い、すなわち0がHi/Loそれぞれのエクイティが100%、1が0%という状況を想定しています。


Xのハンドが(0.3, 0.4)という表記を、
Hiに対して上から30%のEquityを持っているハンドを持っていて、Loに関しては上から40%のEquityを持っているハンドを持っているとします。



今回は両者foldできないのでpotの大きさは関係ないです。


Xがfoldできないのは流石に現実離れしすぎだと思ったのですが、FLO8などではRiverで十分にpotが大きくなっていてほとんどfoldしないシチュエーションはあると思うので考える価値はあると思います。




まず、Xのbetとcheckの境界から考えるのですが、
これをXのハンド(x,y)に関して、x+y=1の直線だと仮定して進めていきます。


同様のルールでHiのゲームではXは上位1/2のハンドをbetするので自然だという考えの元であり、また後々x+y=1の直線上のハンドでは、betとcheckのEVが同じであることを確認します。



その上でXがbetしてきた時の、Yのアクションについて考えます。
今、foldはできないのでraiseとcallの境界を考えていきます。


Yのハンド(x,y)でXのbetレンジにraiseしてscoopしてさらなる利益を得られる時と、raiseしてscoopされてさらなる損失を被る時が等しい時がraiseとcallの境界だとわかります。


従って、Yのraiseとcallの境界を


\begin{eqnarray*}
xy &=& \frac{(1-y-x)(1-x-y)}{2}\\
(x-1)^2+(y-1)^2&=& 1
\end{eqnarray*}



と求められます。


xyがraiseしてscoopした時の利益、 \frac{(1-y-x)(1-x-y)}{2}がraiseしてscoopされた時の損失です。


これは円の方程式になっていて、 (1,1)を中心とした半径1の円になっています。
つまり、YはXのbetに対して (x-1)^2+(y-1)^2>1をみたすハンド(x,y)ではraiseするということがわかります。


言い換えるとゲーム開始時のHiのエクイティの二乗とLoのエクイティの二乗の合計が1より大きい時はraiseするということです。


同様にXがcheckした時のYのbetとcheckの境界を求めます。

先ほどと同様に、Yのハンド(x,y)でXのcheckレンジにbetしてscoopしてさらなる利益を得られる時と、betしてscoopされてさらなる損失を被る時が等しい時がbetとcheckの境界です。


f:id:takoagemat:20200409165713p:plain


従って、


\begin{eqnarray*}
(1-x)(1-y) &=& \frac{(y-(1-x))(x-(1-y))}{2}\\
x^2 + y^2 &=& 1
\end{eqnarray*}



と求められます。


(1-x)(1-y)がbetしてscoopできた時の利益で、  \frac{(y-(1-x))(x-(1-y)) }{2}がbetしてscoopされた時の損失です。

こちらも円の方程式になっていて、(0,0)を中心とした半径1の円になっています。
つまり、YはXのcheckに対してx^2 + y^2 < 1をみたすハンド(x,y)ではbetするということがわかります。


ここで、図からXのbetとcheckの境界 x+y=1ではraiseされる損失とbetしてcallされる利益が同じであることがわかります。


まとめると以下のような図でGTOのアクションを図示できることがわかります。


f:id:takoagemat:20200409165717p:plain
Xの戦略



f:id:takoagemat:20200409165723p:plain
Yの戦略



Xがcheckした後にYは \frac{\pi}{4}  \fallingdotseq  78.5\%の割合でbetすることがわかります。


実際のポーカーではない様々な制約のもとで考えましたが、エクイティの二乗を考えることから、Hi/LoゲームではHiとLoのエクイティの合計が同じならば、どちらかのエクイティに偏っていた方が価値が高いことがわかります。


これは本には書いていませんが、このゲームにおけるYの利得は

領域 D = \{  (x,y) \mid 0 \leq x+y \leq 1 , 0 \leq x , y \leq 1   \} でscoopできる時なので、


\begin{eqnarray*}
&& \iint_D \ xy \ \ dxdy = \frac{1}{24}
\end{eqnarray*}



と計算できます。


同じ状況のHiのゲームではYの利得が1/8ですが、Hi/Loゲームではchopになることが多くYの利得が減少しています。


以上です。
コメントや感想などあったらぜひお願いします。

複数ストリートでのNuts/Airのベットサイズ

こんにちは

今回は「The Mathematics of Poker」のP.231のExample19.2の内容について書いていこうと思います。


題名の通り、複数のストリートがあるときのNuts/Airでのベットサイズについての内容です。


一つのストリートのNuts/Airではベットサイズはオールインを利用するのが最適ですが、複数のストリートがあるときはどうなのかを考えていきます。


一つのストリートの特殊な例についてもnoteを書いています。


note.com



ホールデムのTurnでpot:1
eff stack: Pの時のNuts/Airについてです。


X: Nuts y , Air 1-y
Y: middle 1

Xがyの割合のNutsを持っていて、残りはEquityが0のAirであり、Yはブラフキャッチャーのみを持っている状況です。
Riverで何が出てもEquityが変化しないです。



という状況でのGTOについて考えます。



ベットサイズとして、Turnで s_1 , Riverで s_2のベットサイズを選択するとして計算し、最適なベットサイズを求めていきます。


Riverから逆算して考えます。


XがNutsをcheckすることはなく、全てのNutsでベットします。
Nutsをレンジに含んでいるXはYがcallしてもfoldしてもEVが同じになるようにAirを混ぜてベットします。


Riverでのpot:  1+2s_1になっていて、ベットサイズがs_2なので


XはRiverではXは value : bluff = 1+2s_1+s_2 : s_2 となるようにベットします。


次にTurnに関して考えます。


Riverと同様にTurnでもNutsをレンジに含んでいるXはYがcallしてもfoldしても同じEVになるようにAirを混ぜてベットします。



YはRiverでXがベットしてきた時はs_1損し、Xがcheckした時は 1+s_1 得するので、XのRiverでの bet : check = 1+ s_1 : s_1 になるようにbetします。


こうすることでYはTurnでのXのベットに対してcallしてもfoldしてもEVが変わらず、RiverでのXのベットに対してcallしてもfoldしてもEVが変わらないです。



Xにしてみれば、一部のAirをcheckしてギブアップしてTurnでブラフを混ぜてベットし、さらにRiverでブラフの一部をcheckしてギブアップするという構造です。


今、XのNutsの割合をyとして考えています。


XがTurnでベットする割合は、

\begin{eqnarray*}
y \times (1+ \frac{s_2}{1+2s_1+s_2}) \times (1 + \frac{s_1}{1+s_1})
\end{eqnarray*}

とかけます。



Yは全てのハンドをfoldしてもXのEVは同じなので、XがTurnでベットすることができるAirを最大にしようとします。

ここでわかりやすいように、Xがベットした時のTurn, Riverでアクションによってpotが何倍になるかをそれぞれr_1, \ \  r_2とします。


\begin{eqnarray*}
r_1 &=& 1 + 2s_1\\
r_ 2 &=& \frac{1 + 2s_1 + 2s_2} { 1 + 2s_1}
\end{eqnarray*}


です。


以上より、XのTurnでのベットレンジBは

\begin{eqnarray*}
B &=& y \times \frac{ r_1 \ r_2} { \frac{1}{2} r_1 ( r_2 + 1)} \times \frac{r_1}{2(r_1+1)} \\
&=& \frac{4yr_1r_2}{(r_1+1)(r_2+1)}
\end{eqnarray*}


です。


イメージ的にはそれぞれの分母がストリートのアクション前とアクション後のpotの平均、分子がアクション後のpotです。


XはRiverでALL-INするので r_1 \ r_2 = const.
Bを最大化する s_1, \ s_2すなわち r_1 , r_2
 (r_1+1)(r_2+1) が最小になる時です。


これは相加相乗平均から、 r_1 = r_2 の時です。

従って、それぞれのストリートでpotが同じ倍率で大きくなる時、
すなわち同じpot size betを利用することがわかります。




実際piosolverで計算させてみてもそうなります。
ちなみにpiosolverでは残りのストリート数+eをbet sizeに入力するとこのサイズをtreeに入れて計算してくれます。

f:id:takoagemat:20200410000320p:plain
TurnからのNuts/Air


f:id:takoagemat:20200410000326p:plain



Flopから完全なNuts/Airを再現はできなかったのですが、同じようにほとんど一つのベットサイズを利用します。

f:id:takoagemat:20200410000332p:plainf:id:takoagemat:20200410000338p:plain
FlopからのNuts/Air



実践的にも、強いvalueハンドでは複数のストリートで少しずつvalueをとってALL-INに向かいたいという感覚とも合致すると思います。

block betに関して

こんにちは
今回は「The Mathematics of Poker」のP.161のAKQgame#5の内容について書いてきます。

AKQgameの概要についてはこちら

note.com



X,Yのプレイヤーがそれぞれ1枚ずつ存在するA,K,Qのうちの一枚をもち、ショウダウン時の強さはA>K>Qという状況です。
他のルールはホールデムなどと一緒です。

以前はベットサイズが1種類でレイズもない時のことを考えました。


今回はPot:4で可能なベットサイズが1 or 2であり、1 or 2上乗せのレイズも可能な場合を考えます。
X,Yがそれぞれ25%or50%pot betが可能である状況です。

pot: 4
X(OOP): A or K or Q
Y(IP): A or K or Q
bet: 1 or 2
raise: 1 or 2




結論からいうとGTOはこのシチュエーションのXはKで25%pot betすることがあります。


一旦Xが25%pot betしないのがGTOだと仮定し、それを覆すという方法で考えていきます。


Xの50%pot bet選択肢がある場合は以前のnoteでも書きましたが、AとQの割合を保ちながらどれだけのAをbetしても同じEVになります。


その時Yは25%pot betを利用する時は1/10の利得を得られ、50%pot betを利用する時は1/9の利得を得られるので50%pot betを利用します。


これらの考え方より


「Xは任意の割合のAとそれに伴うQを50%pot betし、残りのA,Qと全てのKをcheckする。
YはXのcheck対して全てのAと1/3のQを50%pot betし、Kはcheck
YはXの25%pot betに対して全てのAでcall or raiseし、3/5のKでcall、残りのKとQはfold
YはXの50%pot betに対して全てのAでcall or raiseし、1/3のKでcall、残りのKとQはfold
XはYの50%pot betに対してAでcall or raiseし、Kの一部をcallし、残りのKとQをfold」


(X,Yどちらのプレイヤーもbetされた時にAでraiseしても相手はQをfoldするだけなのでcallでもraiseでも同じです。)



という戦略を導くことができます。
実際これはXに25%pot betの選択肢がなければGTOです。

f:id:takoagemat:20200408004711p:plainf:id:takoagemat:20200408004731p:plainf:id:takoagemat:20200408004725p:plain




Xの25%pot betを含めてこれがGTOかどうかを確認します。
つまりXが25%pot betを利用し戦略を変更することで、単独にEVを増加させられるのか確認します。

現状でのXのそれぞれのハンドでのEVは

A:  +\frac{1}{3}
K:  -\frac{2}{3}
Q: 0

となっています。


そこでKで25%pot betしてみるとどうなるのかを考えてみます。
YはXの25%pot betに対してAでcall or raiseをしてきて、全てのQをfoldします。



するとKでのEVは -\frac{1}{2} になります。



従ってXは単独に戦略を変更することでEVを増加することができたので先ほどの戦略はGTOではなかったことがわかりました。


ではXがKを25%pot betしてくる戦略に対してYはどのように対処すればいいのか考えます。


XがKでbetしてくることがあるならば、YはQでのbluff raiseによってXがKで25%pot betしてもcheckしても同じEVになるようにします。


そのためには、YはQで   \frac{ ( \frac{1}{2} -  \frac{1}{3} ) \times 2 }{5}  = \frac{1}{15} の頻度でbluff raiseをすればいいことになります。


Kはそのraiseに対しては必要勝率を満たしていないので単にfoldするだけです。


YはQでのbluffはサイズ関係なくK相手には必ず成功するので、Aにペイオフしてしまう損失を最小にするためにmini raiseを採用します。


ここまで整理すると、
XがKで25%pot betする選択肢が存在する。そのためYはXがKでの25%pot betとcheckのEVが同じになるように一定頻度以上のAでのvalue raiseと \frac{1}{15}のQでのbluff raiseをする。


するとXはAで25%pot betをすることで、XのKに3/5の頻度でcallされ、Qに \frac{1}{15}の頻度でraiseされます。


よって、XのAでの25%pot betした時のEVは、



 \frac{3}{5}  \times  \frac{1}{2}  \times 1  +   \frac{1}{15} \times  \frac{1}{2} \times 2  = \frac{11}{30}  > \frac{1}{3}



となり、check or 50%pot betの時より大きくなります。


従って、Xは全てのAで25%pot betを採用することになります。



また、XはYがQでbluff raiseするときとfoldするときのEVを同じにする必要があるので、
Xはbluff raiseに対してcall:foldの比すなわち、betするAとKの比率を5:2にします。


以上より以下のGTOを導くことができました。



X:
全てのAと2/5のK、1/5のQで25%bet、それ以外をcheck
betしてYにraiseされたら、Aでreraise or call し、Kをfold
checkしてYに25%pot betされたら4/5のKでcall、50%betされたら2/3のKでcall


Y:
Xの50%pot betに対してAをraise or call、1/3のKでcall、Qをfold
Xの25%pot betに対してAと1/15のQでmin raise、3/5のKでcall
Xのcheckに対して全てのAと1/3のQで50%pot bet、全てのKをcheck



実際にpiosolverで計算させてみても同じ結果になります。

f:id:takoagemat:20200408004737p:plainf:id:takoagemat:20200408004743p:plainf:id:takoagemat:20200408004750p:plain


まとめ


今回の例では、


YはQでbetに対してcallすることはなく、Aでfoldすることはありません。
XがYのQでのブラフレイズに対してKでcallすることもありません。
従ってXがKでbetすることで、YのQからvalueを取ったり、YのAをfoldさせることはできません。


つまりKでbetすることはバリューベットでもブラフベットでもないですが、ベットの選択肢を入れた方が得になります。



ベットした方がいい理由はバリューとブラフだけの二種類だけでは当てはまらない時もあります。


繰り返し読んで参考にした記事です。


note.com





XはKでのblock betをうつことで、Yにraise bluffを一定頻度で行わせてAでさらなるEVを得ています。


25%pot betの選択肢によってYの利得が1/9から1/10に減少しています。


f:id:takoagemat:20200408004757p:plainf:id:takoagemat:20200408004804p:plain






バリューベットとブラフベットがベットをする大きな理由であり、この二種類から利益を得ることができるのは間違いないと思います。


しかし特にOOPでは中途半端なエクイティを持つハンドがcheckレンジの多くを占めると、IPに効率よくバリューベットとブラフベットで利益を得られてしまいます。

このblock betは自分のcheckレンジを調節することで、相手がバリューベットとブラフベットを効率よく行えるのを防いでいるのではないかと思います。


実際にpiosolverでRiverからのsubtreeでOOPに小さいサイズのベットの選択肢を入れて計算させると、ブロックベットをそこそこ広いハンドでするシチュエーションもあります。


以上です。
間違っているところや感想などあったらぜひ教えてください。

レイズされない時のベットサイジング

こんにちは

今回は「The Mathematics of Poker」P.151のExample14.3の[0,1]gameについて書いていきます。

OOPが強制check、raiseなしで、eff stackが無限であるときのGTOです。

この内容「The Mathematics of Poker」の中でも一番理解するのに時間がかかるところの一つだと思います。


状況は
X: [0,1]の一様乱数
Y: [0,1]の一様乱数
pot: 1, eff stack: infinity

X(OOP)が強制check
Y(IP)は自由なベットサイズを選択できる

XはYのベットに対してcall or foldする。raiseをすることはできない。
ショウダウンになったら数字が小さい(大きいでも同義)方が勝ちという仮定の元でのgameを考えたものです。


この条件の元でのGTOについて考えていきます。
ちなみに0がEquityが100%のハンド、1がEquityが0%のハンドだとして考えています。


まずYのベット額tに対してXのcallとfoldの境界をx(t)とします。
つまり、どんな小さなベットに対してもfoldする境目がx(0)です。
このx(0)の値をx_0とします。


Xの戦略に関して

XはYのブラフハンドyでのベット額sのベットに対してブラフハンド最上位のハンドでのcheckとEVが同じになるようにdefenceします 。



\begin{eqnarray*}
y - x(s) - sx(s) &=& k\  \   ~ (kは定数)\\
x(s) &=& \frac{(y-k)}{(1+s)}\\
\end{eqnarray*}


y-x(s)はブラフ成功によるもの、-sx(s)はブラフ失敗によるものです。
この式を最初にみたときしっくりこなかったので、こちらのnoteに自分なりにわかりやすく書いてみました。

note.com




ブラフの最上位のハンドの利益は0なので、 y - k = x_0
従って、 x(s)  = \frac{x_0}{(1+s)}とかけます。

この結果をみてもわかるように、XはYのブラフの最上位のハンドがブラフとcheckのEVを同じになるようにdefenceすればいいということになっています。

Yの戦略に関して

Yのvalue betの利得関数f(s)を考えて、x(s)のコールレンジに対してバリューハンドyでどのくらいバリューベットすれば一番利得が得られるかを求めていきます。


\begin{eqnarray*}
f(s) &=& y(-s) + (x(s) -y)s\\
f(s) &=& sx(s) - 2sy\\
\end{eqnarray*}



y(-s)はさらに強いハンドにコールされてしまう場合、(x(s)-y)sはバリューが取れた場合について考えています。


f(s)s微分すると
f’(s) = sx’(s) + x(s) - 2y


 f’(s) = 0 になる時がf(s)が極大であり、その時のy
       y(s) = \frac{x_0}{2(1+s)^2}
と求められます。


なぜs微分するかですが、Yの特定のハンドyに対して利得関数を求めており、その特定のハンドyに対して最適なベット額を求めているからです。


Xはベット額sによってYのバリューハンドが何かわかるという状態です。(ここはもしかしたら語弊があるかもしれません)


   y(s) =  \frac{x_0}{2(1+s)^2}          より
     dy =  \frac{ x_0}{(1+s)^3 }  ds


ベット額sの変化に対してバリューハンドyがどれくらい変化するかがわかりました。

またYはXがbluff catcherのハンドでcallしてもfoldしても同じEVになるようにするので、
Yのブラフレンジはバリューに比べて  \frac{s}{1+s}  の割合あります。


従ってYのブラフレンジの割合は

  \int ^{\infty} _0 \frac{sx_0}{(1+s)^4} ds      で求められます。


この積分を求めて
    \int ^\infty_0   \frac{sx_0}{(1+s)^4} ds =\frac{1}{6} x_0



従って、    1-x_0 = \frac{1}{6} x_0     なので x_0 = \frac{6}{7}


以上より、
\begin{eqnarray*}
x_0 &=& \frac{6}{7}\\
x(s) &=& \frac{6}{7(1+s)}\\
y(s) &=& \frac{3}{7(1+s)^2}\\
\end{eqnarray*}

であり、



GTO
XはYのベット額sのベットに対して[0,x(s)]でcallして、    [x(s),1 ]でfoldする
Yは      [0,\frac{3}{7} ]のハンドを y(s) に従った額でバリューベット
 [\frac{6}{7},1 ]のハンドをブラフレンジとしてベット額 s に合わせた割合ベット

となります。


これは本には書いてませんが、
ベットサイズを任意に決められることでのIPの利得は


\begin{eqnarray*}
\int ^\infty _0 s (x(s) - 2y(s)) \frac{dy}{ds} ds + \frac{1}{7}  \frac{1}{7}  \frac{1}{2}
&=& \frac{36}{49} \int ^\infty _0 \frac{s^2 } {(1+s)^5} ds + \frac{1}{98}\\
&=& \frac{1}{14}\\
\end{eqnarray*}


です。
第一項はバリューによる利益、第二項はブラフによる利益です。


ポットサイズベットに限定されている時は利益が \frac{1}{18}だったので、任意のベットサイズを選択できることにより得られる利益が増加したことがわかります。



実際piosolverで計算しても似たような結果になります。

f:id:takoagemat:20200406190224p:plain




もちろん有限個の有限なベットサイズしか選択できませんが、下位\frac{6}{44}のハンドはブラフをした方がよく、上位 \frac{17}{44}のハンドは 15%potまでしか入れてませんがバリューベットした方がいいとでています。

IPの利得も\frac{6.89}{100}\frac{1}{14} = 0.0714より少し小さいです。

f:id:takoagemat:20200406190230p:plain

以上です。


もちろん実際のポーカーではOOPはレイズすることが可能です。
もしOOPにレイズされない状況で、お互いのレンジが連続的な同じ分布になってれば、強いハンドは大きく、弱いハンドは小さくベットすることが得になります。

なにか間違っていることや感想などあれば反応いただけると助かります。

ライブポーカー

こんにちは
またブログを書くのが久しぶりになりました。
時間があいても継続して書いていこうと思います。
noteも始めてみました
note.com


しばらくポーカーをしていなかったのですが、結構前にゴールドコーストのthe star casinoにいってきたのでその時のことを書いてみようと思います。

オーストラリアには観光目的でいったのでカジノにいけるのかわからなかったのですが、最終日に4,5時間pokerすることができたので全体的な感想を書いてみようと思います。

まず自分が止まったゴールドコーストサーファーズパラダイスは広大なビーチがあり、波がとても強かったです。

サーフィンも教えてもらったのですが、波と並行な向きに立つのがやっとで、多くの人がイメージするであろう波に乗る姿はとても上手い人だと言うことがわかりました。


f:id:takoagemat:20200202124525j:plain


展望台からの写真です。

f:id:takoagemat:20200204175538j:plain

自然公園にもいったのですが野生のカンガルーとコアラもいました。
カンガルーは両足同時に動かしてかなりの加速力を持っていたのでびっくりしました。

f:id:takoagemat:20200201125832j:plainf:id:takoagemat:20200201125510j:plainf:id:takoagemat:20200201141006j:plain


これが昼に通った時に通ったカジノthe starの写真です。
ゴールドコースト内の移動はtramが便利でした。
治安は安全だと聞いていましたが、実際そうだと感じました。
両替は現地の両替所が調べていた通り一番レートがよかったです。

f:id:takoagemat:20200202130856j:plainf:id:takoagemat:20200308034657j:plain

近くにあるアウトレットのbetty’s burgerのハンバーガーがとても美味しかったのでおすすめです。

午後7時半くらいにカジノにつきました。
エレベーターでカジノのある階層まで上がると190cmくらいありそうな警備員が二人いました。
日本人は幼くみられ年齢確認されることが多いと聞いていたのですが年齢確認はされなかったです。


中で写真をとるのは禁止らしいので写真はないのですが、平日と言うこともあり人はそこまで多くはなかったです。

まずフロントのようなところでメンバーズカードを作ってもらいました。
パスポートを見せるとメンバーズカードの暗証番号はどうすると聞かれ設定しました。
やたら流暢な日本語で「暗証番号」といっていました笑

f:id:takoagemat:20200331100411j:plain

初めてきた人がプレイした分に応じてカジノで遊べるチップがもらえるプロモをやっていたのですが、ポーカーには適用されないと言われました。(´・ω・`)


ポーカーテーブルの方に向かってウェイティングに入れてもらい現金をチップに変えてもらいました。
座ろうと思った$1-3のテーブルは2つあり、ウェイティングが5人くらいいました。
25分くらい待ってテーブルに座ることができました。

結果は深夜1時くらいまでの5時間くらいやって40bbくらい勝って帰りました。
朝までやろうかと思っていたのですが、段々と人が減っていき残り自分入れて4人になった上に眠くて疲れを感じたのでホテルに帰りました。

rakeは10%切り捨てでディーリングはスムーズでした。
聞いていた通りテーブルはソフトでプリフロップでは多くのプレイヤーが参加してきました。
人が6人くらいになり、自分のSBまでfoldで回ってきて自分もfoldした時はディーラーもびっくりしていました。

フロップ以降はバリューしか打たないような人いれば、ドローハンドをレイズするような人もいました。
わかりやすいテルっぽいのがみられる人などもいました。
それぞれの人をしっかり観察してどのような思考をするプレイヤーなのか理解してエクスプロイトすることが大事だと感じました。

当たり前のことしか書いていない気もしますが、ポーカーテーブルの雰囲気はよく楽しかったです。
自分は英検準一級を持っている程度なのですが、もっとお洒落に英語で会話できたらよかったとも思いました。

ビーチでもクリケットをしている人を見かけていて、カジノのテレビでもやっていたのでルールを聞いたら教えてくれたのですが、あんまりというかほとんどわかりませんでした笑

何か聞きたいことなどあったらtwitterなどで聞いてくれればおそらくお答えします。

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で次のストリートに進み、ある程度エクイティがあるハンドをブラフを混ぜてレイズした方がいいという結果のように見えます。

確率変数(´・∀・`)

こんにちは、今回はポーカー関係ないです。
内容的には数学です。

突然ですが「確率変数(random variable)」って何か説明できるでしょうか

確率変数って言葉自体はほとんどの人が聞いたことあったりすると思うのですが、理系大学生や卒業生でも答えられないもいるんじゃないかと思います。

結構前ですが、自分が大学教授に確率統計の問題を説明するタイミングがありました。(もちろん教授はその内容をわかっています。)

その時に、はじめの二言目くらいで
「確率変数Xが...」
と話し始めたところ

教授から
「確率変数って何?」
と突っ込まれ

「(´・∀・`)」
となりました。

確かその時は「確率によって変わる変数です。」みたいなことを言った気がするのですが、違いました。

確率変数は標本空間から実数の空間への写像でした。
測度論を学ぶにあたっても理解することが大事な部分だったと思います。

riverで1outを引かれるという事象に約2%という実数が結びついているという感じです。

このブログの確率のところがとてもわかりやすかったです。
ogyahogya.hatenablog.com