paloma blog

NWエンジニアやってます。主に自宅環境のお遊びを書きます。Pythonもちょっと。タイトルは好きなカクテルから。

スリーカードポーカーで勝つことはできるのか

最近このコードばかりいじっています。
シミュレータにチップを賭ける処理を追加したのでやってみましょう。

ここで言う勝つとはカジノへ行ってチップを増やして帰ってくることです。

その前にシミュレータ修正

賭け金勝負する前に勝敗判定部分が3箇所も間違っていました。

  • 判定にいれる手役のミス

手札判定後の値を比較しなければいけないのですが手札をそのまま判定の関数に突っ込んでいました。
正しい比較ができずに判定がバラけてたというわけです。
修正部分の抜粋です。

+               player_hand = pokerapp.Handcheck(player).result() #これをMatchに入れないといけなかった
+               dealer_hand = pokerapp.Handcheck(dealer, flag=True).result() #これをMatchに入れないといけなかった
+
+               match_result = pokerapp.Match(player_hand, dealer_hand)
+

-           player = pokerapp.Handout(deck)
-           dealer = pokerapp.Handout(deck)
+               chips = chips + pays + ante_b + pp_b
 
-           match_result = pokerapp.Match(player, dealer)
  • 判定処理のミス

同役の場合数字が高いカードを持っている方の勝ちですが、後半一部誤ってました。

@@ -133,7 +133,7 @@ def Match(p1, p2):
                                return 0
                        elif p1[1][1] < p2[1][1]:
                                return 1
-                       elif p1[1][1] == p2[1][1]: # こっちが正しい
+                       elif p1[1][1] == p2[1][2]:
                                if p1[1][0] > p2[1][0]:
                                        return 0
                                elif p1[1][0] < p2[1][0]:
  • そもそも引き分け処理がなかった

勝ち負け判定は払い戻し金の処理に効いてくるのですが、その中に引き分け判定が無かったので追加。
そりゃ引き分け0回になりますよね。

if match == 0 and d_hand[0][0] == 0:
    refund_bet = bet * 1
    refund_ante = ante * 2
    return refund_bet + refund_ante
elif match == 0:
    refund_bet = ante * 2
    refund_ante = bet * 2
    return refund_bet + refund_ante
elif match == 1:
    # Forfelt the bet
    refund_bet = 0
    refund_ante = 0
    # Convert negative
    return -((bet * 2) + ante)
#ここがなかった
elif match == 2:
    return 0

正しい結果はこちらです。
改めて50万回まわしてみました。

$ python3 simulater.py 
Three card poker simulator start.
The simulator trials 500,000 times.

=== Probabliry of winning or losing ===

Player 271650 wins. (54.3%)
Dealer 227995 wins. (45.6%)
Draw 355 times. (0.1%)

=== Percentage of Players all hands ===

High card!          74.686%
One Pair!           16.919%
Flash!               4.985%
Straight!            2.974%
Three of a kind!     0.234%
Straight Flash!      0.202%

=== Percentage of Players win(271,650 times) hands ===

High card!          59.368%
One Pair!           25.843%
Flash!               8.636%
Straight!            5.355%
Three of a kind!     0.427%
Straight Flash!      0.371%

引き分けちゃんと出てますね。
判定も修正したので五分五分の勝負では無くなってしまった…?

出る役の比率は同じですが、引き分けが入ったことにより勝った時の役の比率も変わりました。
ペアの割合が増えてハイカードが減りましたね。

賭け金処理を追加

いよいよ本題の賭け金のある状態でシミュレートしてみます。

勝率約50%、ペアプラスの付く率25%でリターン率75%です。
勝つためにはオリも要りますが今回全ツッパでやってみましょう。

賭け金の一連処理

手持ちチップ、ベット金額の処理と勝敗による払い戻しを作ります。

チップ処理

ループ入る前に賭け金を定義して本体(pokerapp.py)と同じように書きます。
勝敗後に勝敗の払い戻しと各ボーナスの払い戻しを足します。

+ante = 10
+pp = 10
+
+def Trials(chips):
        for _ in range(trial):
 
+               deck = pokerapp.Deck()
+               deck.shuffle()
+
+               player = pokerapp.Handout(deck)
+               dealer = pokerapp.Handout(deck)
+
+               hand_result = pokerapp.Handcheck(player).get_role()[0][0]
+               player_hand = pokerapp.Handcheck(player).result()
+               dealer_hand = pokerapp.Handcheck(dealer, flag=True).result()
+
+               match_result = pokerapp.Match(player_hand, dealer_hand)
+
+               # bonus
+               ante_b = pokerapp.Payoff(ante, player_hand[0]).ante_bonus()
+               pp_b = pokerapp.Payoff(pp, player_hand[0]).pairplus_bonus()
+
+               if match_result == 0:
+                       win_list.append(hand_result)
+               elif match_result == 2: #引き分けはボーナス無し
+                       ante_b = 0
+                       pp_b = 0
+               match_list.append(match_result)
+               hand_list.append(hand_result)
+
+               # pay off
+               pays = Liquidation(match_result, dealer_hand, ante, pp)
 
+               chips = chips + pays + ante_b + pp_b

+       return chips
払い戻し

本体のはメイン処理内にベタ書きですが、シミュレータは関数化しました。
内容はさっきの引き分け処理と同じです。

def Liquidation(match, d_hand, ante, bet):
    if match == 0 and d_hand[0][0] == 0:
        refund_bet = bet * 1
        refund_ante = ante * 2
        return refund_bet + refund_ante

    elif match == 0:
        refund_bet = ante * 2
        refund_ante = bet * 2
        return refund_bet + refund_ante

    elif match == 1:
        # Forfelt the bet
        refund_bet = 0
        refund_ante = 0
        # Convert negative
        return -((bet * 2) + ante)

    elif match == 2:
        return 0

これがうまく動けば本体にも採用します。

勝負!

実際何十万回もプレイするのは現実的ではないので100回にしてみます。

手持ちは$1000でアンティ、ペアプラスは$10ずつ賭けます。

$ python3 simulater.py 
You are first given $1,000.
Three card poker simulator start.
The simulator trials 100 times.
You'll bet ante $10 and pair plus $10 all the time.

=== Probabliry of winning or losing ===

Player 56 wins. (56.0%)
Dealer 44 wins. (44.0%)
Draw 0 times. (0.0%)

=== Percentage of Players all hands ===

High card!            77.0%
One Pair!             15.0%
Straight!              4.0%
Flash!                 4.0%

=== Percentage of Players win(56 times) hands ===

High card!          64.286%
One Pair!           21.429%
Straight!            7.143%
Flash!               7.143%

You finally got $2430

増えてる!

今度は詳細を出してもう一回。
printで出してますが、別ファイルに保存したいですね。
左からプレイヤー手札、勝敗、チップ、払い戻し、アンティボーナス、ペアプラスボーナスです。

You are first given $1,000.
Three card poker simulator start.
The simulator trials 100 times.
You'll bet ante $10 and pair plus $10 all the time.
High card! 1 970 -30 0 0 # ハイカードで負けて賭け金$30没収でのこり$970
High card! 1 940 -30 0 0
High card! 1 910 -30 0 0
High card! 1 880 -30 0 0
High card! 0 910 30 0 0
High card! 0 940 30 0 0
One Pair! 0 1000 40 0 20 # ワンペアで勝って配当とペアプラスボーナスで$1000に戻った
High card! 0 1030 30 0 0
High card! 1 1000 -30 0 0
High card! 0 1030 30 0 0
One Pair! 0 1090 40 0 20
High card! 1 1060 -30 0 0
High card! 1 1030 -30 0 0
High card! 1 1000 -30 0 0
One Pair! 1 990 -30 0 20
High card! 1 960 -30 0 0
Straight! 0 1070 30 10 70
High card! 1 1040 -30 0 0
Flash! 0 1130 40 0 50
High card! 0 1170 40 0 0
High card! 1 1140 -30 0 0
High card! 1 1110 -30 0 0
High card! 1 1080 -30 0 0
High card! 0 1110 30 0 0
High card! 0 1150 40 0 0
Flash! 0 1230 30 0 50
One Pair! 0 1290 40 0 20
High card! 1 1260 -30 0 0
High card! 1 1230 -30 0 0
High card! 1 1200 -30 0 0
High card! 0 1230 30 0 0
High card! 0 1260 30 0 0
High card! 1 1230 -30 0 0
One Pair! 0 1290 40 0 20
High card! 1 1260 -30 0 0
High card! 1 1230 -30 0 0
High card! 0 1270 40 0 0
High card! 1 1240 -30 0 0
High card! 1 1210 -30 0 0
High card! 1 1180 -30 0 0
One Pair! 0 1240 40 0 20
High card! 0 1280 40 0 0
High card! 1 1250 -30 0 0
High card! 1 1220 -30 0 0
One Pair! 0 1270 30 0 20
Flash! 0 1350 30 0 50
High card! 0 1380 30 0 0
High card! 1 1350 -30 0 0
High card! 1 1320 -30 0 0
High card! 1 1290 -30 0 0
High card! 1 1260 -30 0 0
High card! 1 1230 -30 0 0
One Pair! 0 1280 30 0 20
High card! 0 1320 40 0 0
High card! 1 1290 -30 0 0
High card! 0 1320 30 0 0
High card! 1 1290 -30 0 0
High card! 0 1330 40 0 0
High card! 1 1300 -30 0 0
High card! 0 1330 30 0 0
High card! 0 1370 40 0 0
One Pair! 1 1360 -30 0 20
High card! 0 1400 40 0 0
High card! 0 1440 40 0 0
High card! 0 1470 30 0 0
High card! 0 1500 30 0 0
High card! 1 1470 -30 0 0
High card! 0 1500 30 0 0
High card! 1 1470 -30 0 0
High card! 1 1440 -30 0 0
High card! 1 1410 -30 0 0
High card! 1 1380 -30 0 0
High card! 0 1420 40 0 0
High card! 1 1390 -30 0 0
High card! 0 1420 30 0 0
High card! 0 1450 30 0 0
High card! 1 1420 -30 0 0
High card! 0 1450 30 0 0
One Pair! 0 1510 40 0 20
High card! 1 1480 -30 0 0
High card! 0 1510 30 0 0
Straight! 0 1630 40 10 70
One Pair! 0 1690 40 0 20
Flash! 0 1780 40 0 50
High card! 0 1810 30 0 0
High card! 0 1850 40 0 0
High card! 1 1820 -30 0 0
High card! 1 1790 -30 0 0
High card! 0 1820 30 0 0
High card! 1 1790 -30 0 0
High card! 1 1760 -30 0 0
High card! 1 1730 -30 0 0
High card! 0 1760 30 0 0
High card! 1 1730 -30 0 0
High card! 0 1760 30 0 0
Straight! 0 1870 30 10 70
High card! 1 1840 -30 0 0
High card! 1 1810 -30 0 0
High card! 0 1850 40 0 0
High card! 1 1820 -30 0 0

…

チップ増えて終了で勝ててますね!
アプラスの配当が貢献してますね。
この詳細見るとLiquidation関数もうまく動いてそうです。

では100回勝負を100セットやったら平均どれくらい貰えるのでしょうか。

$ for a in {1..100} ; do python3 simulater.py | tail -1 ; done | awk -F '$' '{sum+=$NF} END {print sum/NR}'
2522

$1000が約2.5倍になるという結果が出ました。
嬉しい結果ですがホントかなあ。

シミュレーションできたところで実践

シミュレータでは全ツッパでも勝ち越すことができました。
ではGTAオンラインのスリーカードポーカーでこの検証が本当か試してみたいと思います。

というわけで次回実践です。

その他反省

シミュレータの賭け機能部分は全部作ってからcommitしようと思っていたので振り返ると差分探しが大変でした。
機能毎か、ちょっとずつcommitしたほうがいいのか悩みどころですね。

進捗が見える分ちょっとずつcommitの方がいいかな。

リポジトリ

github.com