paloma blog

NWエンジニアやってます。技術の備忘など。Pythonもちょっと。タイトルは好きなカクテルから。

2020年上半期見た映画メモ & twitter取得ツール改修

気づけばもう7月に入ってしまいました。

今年はコロナ騒ぎですが、私は4月からリモートワーク含んだ外出自粛生活が始まりました。

上半期の半分は自粛生活ということになりますね。

とは言っても、もともとインドアな人間なのであまり生活に変化はありません。
映画も変わらず週一本ペースで見ています。

というわけで上半期見た映画を振り返ります。

以前作ったtwitter投稿ツール & 取得ツールでいつでも振り返ることができます。

さあ上半期を振り返りましょう

ツールの仕様上原題で申し訳ないですが、見た映画です。
有名なtweepyと言うライブラリを使って取得してます。

(python3) masashi@PC-ubuntu:~/movietweet$ python gettw.py 
2020-07-05 00:12:09 The Green Hornet
2020-06-28 21:20:11 Charlie's Angels: Full Throttle
2020-06-27 22:21:16 Charlie's Angels
2020-06-14 22:47:09 Girls Trip
2020-06-07 08:46:58 Miami Vice
2020-05-30 22:57:37 You Don't Mess with the Zohan
2020-05-23 23:24:04 The Magic of Belle Isle
2020-05-16 23:08:08 Indiana Jones and the Temple of Doom
2020-05-09 23:00:40 Pixels
2020-05-03 22:49:06 The House
2020-04-25 23:01:03 Next
2020-04-18 23:28:44 Molly's Game
2020-04-04 22:59:26 Lowriders
2020-03-28 23:31:46 Pain & Gain
2020-03-21 23:34:21 American Made
2020-03-14 23:05:17 Donnie Brasco
2020-03-08 00:17:50 Midnight Run
2020-02-23 22:16:40 Reservoir Dogs

あら、1月分が入ってないですね。
apiのcount値を増やしても変わらずです。

pageの値を増やしたら取得することができました。
countとpageはどう違うんですかね?
...と思ったら自分で書いてました。
countだけだと200件までしか取れないようです。

とりあえず5ページ分取るようにしたのでしばらくは大丈夫そうです。

(python3) masashi@PC-ubuntu:~/movietweet$ python gettw.py 
2020-07-05 00:12:09 The Green Hornet
2020-06-28 21:20:11 Charlie's Angels: Full Throttle
2020-06-27 22:21:16 Charlie's Angels
2020-06-14 22:47:09 Girls Trip
2020-06-07 08:46:58 Miami Vice
2020-05-30 22:57:37 You Don't Mess with the Zohan
2020-05-23 23:24:04 The Magic of Belle Isle
2020-05-16 23:08:08 Indiana Jones and the Temple of Doom
2020-05-09 23:00:40 Pixels
2020-05-03 22:49:06 The House
2020-04-25 23:01:03 Next
2020-04-18 23:28:44 Molly's Game
2020-04-04 22:59:26 Lowriders
2020-03-28 23:31:46 Pain & Gain
2020-03-21 23:34:21 American Made
2020-03-14 23:05:17 Donnie Brasco
2020-03-08 00:17:50 Midnight Run
2020-02-23 22:16:40 Reservoir Dogs
2020-02-08 23:56:44 Ferris Bueller's Day Off
2020-02-01 23:58:44 Unfinished Business
2020-01-18 22:39:36 Four Brothers
(以降昨年見た作品)

このツールはコピペでパパっと作ってしまったので、あまりマニュアルを読んでません。
pegerもCursor関数でやった方がよさそうな記述がありました。
今後の課題ですね。

差分

diffはこんな感じ。
特に考えず作ってしまいましたが、もう少しループ綺麗にできそうな気がする。

(python3) masashi@PC-ubuntu:~/movietweet$ git diff gettw.py
diff --git a/gettw.py b/gettw.py
index 7e7b545..dc36bab 100644
--- a/gettw.py
+++ b/gettw.py
@@ -3,11 +3,14 @@
 from tweetapi import TWauth
 import datetime
 
-for info in TWauth().user_timeline(count=200):
-    if 'tweet movieinfo' in info.source:
-        Posted = info.created_at + datetime.timedelta(hours=9)
-        Text = info.text[info.text.find('')+1:info.text.find('')]
-        print(Posted, Text)
-    else:
-        pass
+API = TWauth()
+
+for i in range(1, 5):
+    for info in API.user_timeline(count=200, page=i):
+        if 'tweet movieinfo' in info.source:
+            Posted = info.created_at + datetime.timedelta(hours=9)
+            Text = info.text[info.text.find('')+1:info.text.find('')]
+            print(Posted, Text)
+        else:
+            pass

上半期ベスト3

せっかく出したので私の上半期ベスト3を発表します。

  1. Pixels (2015年)
    宇宙人がゲームキャラの姿で攻めてくる話です。
    私はSaints rowというシリーズのゲームが大好きなのですが、
    話のぶっ飛び方といい挿入歌といい同じ波長を感じましたw
    大統領が自ら出陣するのもイイ!w
  2. Molly's Game (2017年)
    元スキージャンププレイヤーのモリーという女性が裏カジノを開く話です。
    前の雇い主のノウハウを盗んで、独立して場を立てていく流れがかっこいいです。
    大事なところで諦めないスポーツマンスピリッツがよかったですね。
  3. Reservoir Dogs (1992年)
    クエンティン・タランティーノ監督の映画です。
    パルプフィクションが大好きなのでずっと見たかった映画です。
    内容的には銀行強盗グループの後日談です。
    仲間が揉めてっちゃうんだよなあ。
    気弱なキャラが多い(気がする)スティーブ・ブシェミですが、この映画では強気キャラですw

むすび

2年位前から見た映画の感想をローカルWikiに書いていて、昨年twitterに投稿する形に変えました。
映画の概要は自動で投稿される様にしたので、あとは簡単な感想を書いて視聴記録として残してます。

感想だけ書けばよくなったので、見た後にすぐ投稿・感想を書く様になりました。
お酒飲みながら見てるので記憶が曖昧な時もありますがw

下半期も面白い映画たくさん見たいですね。
ナルコスのメキシコ編もそろそろ見なきゃ。

リポジトリ

github.com

スリーカードポーカーゲームを作りたい unittest編

最近ずっと触っているスリーカードポーカーゲームですが、スクリプトを動かし動かしで作成してきました。
そろそろテストコードも書いてみようということでunittest編です。

私はNW機器の構築が本業なので単体テストというものがずっとピンと来なかったんですよね。 NW機器の単体テストはコマンドで機器のパラメータ取って終わりというものが多いと思います。
(私はteraterm macroでパパっと)

NWの試験はEnd間で疎通を取ってみるほうがバグ出しがし易いですからね。

でもスクリプトやプログラムだと関数の部品ごとに動くかどうか確認することを単体テストと言うんですね。
なるほどねー。

どこまで試験するかはよく課題に上がると思いますが、どこまでやりましょう。
一応動かしてはいるのでとりあえず関数の所だけにしましょう。

単体テストは組み込みのunittestを使ってみます。

unittestの使い方

いろんなサイト見たところ、テストしたい関数を作って戻り値を書いておけばいい様です。
assertEqualでチェックしてるものが多いですね。

でも私のコードでは問題が。

  • returnがない処理
    • カードデッキを作ってシャッフルする関数があるんですがこの場合は何が正になるんでしょうか。
      type classって何が戻ってるのかよくわからないです。
>>> card = pokerapp.Deck()
>>> type(card)
<class 'pokerapp.Deck'>
>>> card.shuffle()
>>> type(card)
<class 'pokerapp.Deck'>
  • 戻り値が変わる場合
    • ポーカーなのでカードを配る関数があります。
      もちろん毎回手札が変わるのですが、これはどうしたら…。
      と思ったら正規表現で判定できそうです。

試験範囲

とりあえず

  • デッキ作成
  • カード配る
  • 手役判定
  • 手札勝負判定
  • ボーナス払い戻し

くらいのテストを作っておきましょう。

デッキ作成

トランプを作ってシャッフルするだけなので戻り値がありません。(たぶん)
どう試験するんだと思ったらassert使わなくてもokになりました。

これは正常に処理されたからOKということになるのかな。

masashi@PC-ubuntu:~/Three-card-poker$ cat test_pokerapp.py 
import unittest
import pokerapp

class TestPokerApp(unittest.TestCase):

    def setUp(self):
        self.card = pokerapp.Deck()
        
    def test_createdeck(self):
        self.card.shuffle()

if __name__ == '__main__':
    unittest.main() 

setUpは各テストごとに呼ばれるので各テストでデッキを作る様に作成してます。
これも継承というか、変数使いまわせるんでしょうか。
でもインスタンス作ってるわけじゃないしね。

とりあえずOKが返ってきました。

masashi@PC-ubuntu:~/Three-card-poker$ python3 test_pokerapp.py 
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
カード配り、役判定

配りから役判定まで書きます。判定の戻り値はタプルで役ランク、手札の数字が返ります。

([1], [2, 5, 11])みたいな感じね。

戻り値がこの形になるよう正規表現を書けばいいですね。
assertRegexという関数で判定できるみたいです。

また、get_roleで役名が返るのでこれも判定しましょう。

役名は全部"One pair!"みたいな形なので".+¥!"でいいでしょう。

同じ形でディーラーの判定も作ります。
ディーラーは手札によっては降りる処理が入ります。

   def test_player(self):
        player = pokerapp.Handout(self.card)

        p_hand = pokerapp.Handcheck(player).result()
        p_role = pokerapp.Handcheck(player).get_role()

        self.assertRegex(str(p_hand), '\(\[\d\], \[(\d*, ){2}\d*\]\)')
        self.assertRegex(str(p_role), '.+\!')

    def test_dealer(self):
        dealer = pokerapp.Handout(self.card)

        d_hand = pokerapp.Handcheck(dealer, flag=True).result()
        d_role = pokerapp.Handcheck(dealer, flag=True).get_role()

        self.assertRegex(str(d_hand), '\(\[\d\], \[(\d*, ){2}\d*\]\)')
        self.assertRegex(str(d_role), '.+\!|.+')
手札勝負

プレイヤー、ディーラーの手札を比較します。 戻り値は

  • 0 : プレイヤー勝利
  • 1 : ディーラー勝利
  • 2 : 引き分け

です。

orが使えて助かりました。

   def test_match(self):
        player = pokerapp.Handout(self.card)
        dealer = pokerapp.Handout(self.card)

        p_hand = pokerapp.Handcheck(player).result()
        d_hand = pokerapp.Handcheck(dealer, flag=True).result()

        match = pokerapp.Match(p_hand, d_hand)
        self.assertEqual(match, 0 or 1 or 2)
ボーナス払い戻し

役から賭け金 * n倍の払い戻しをします。
これも数字が返るだけなので"¥d+"でいいですね。

   def test_payoff(self):
        player = pokerapp.Handout(self.card)

        p_hand = pokerapp.Handcheck(player).result()

        pay_ante = pokerapp.Payoff(10, p_hand[0]).ante_bonus()
        pay_pairplus = pokerapp.Payoff(10, p_hand[0]).pairplus_bonus()

        self.assertRegex(str(pay_ante), '\d+')
        self.assertRegex(str(pay_pairplus), '\d+')

動かしてみる

全体はこんな感じです。
重複コードが多いですが、こんなもんなんでしょうか。

  • test_poketapp.py
import unittest
import pokerapp

class TestPokerApp(unittest.TestCase):

    def setUp(self):
        self.card = pokerapp.Deck()
        
    def test_createdeck(self):
        self.card.shuffle()

    def test_player(self):
        player = pokerapp.Handout(self.card)

        p_hand = pokerapp.Handcheck(player).result()
        p_role = pokerapp.Handcheck(player).get_role()

        self.assertRegex(str(p_hand), '\(\[\d\], \[(\d*, ){2}\d*\]\)')
        self.assertRegex(str(p_role), '.+\!')

    def test_dealer(self):
        dealer = pokerapp.Handout(self.card)

        d_hand = pokerapp.Handcheck(dealer, flag=True).result()
        d_role = pokerapp.Handcheck(dealer, flag=True).get_role()

        self.assertRegex(str(d_hand), '\(\[\d\], \[(\d*, ){2}\d*\]\)')
        self.assertRegex(str(d_role), '.+\!|.+')

    def test_match(self):
        player = pokerapp.Handout(self.card)
        dealer = pokerapp.Handout(self.card)

        p_hand = pokerapp.Handcheck(player).result()
        d_hand = pokerapp.Handcheck(dealer, flag=True).result()

        match = pokerapp.Match(p_hand, d_hand)
        self.assertEqual(match, 0 or 1 or 2)

    def test_payoff(self):
        player = pokerapp.Handout(self.card)

        p_hand = pokerapp.Handcheck(player).result()

        pay_ante = pokerapp.Payoff(10, p_hand[0]).ante_bonus()
        pay_pairplus = pokerapp.Payoff(10, p_hand[0]).pairplus_bonus()

        self.assertRegex(str(pay_ante), '\d+')
        self.assertRegex(str(pay_pairplus), '\d+')


if __name__ == '__main__':
    unittest.main() 

詳細がわかりませんが一応エラーなく完了しました!

masashi@PC-ubuntu:~/Three-card-poker$ python3 test_pokerapp.py 
.....
----------------------------------------------------------------------
Ran 5 tests in 0.001s

OK

テストコード書いてみて思ったこと

関数が動くだけでOKにはなりますが、より精度の高いテストをしようとすると
期待する戻り値を正確に理解していないといけませんね。

コードも本体プログラムと似たものになりましたが、プログラムとテストは表裏一体なんですかねぇ。

今後について

一応OKは出ましたが、もともと本体スクリプトで動いていたものをテストコードとして置き換えただけなので、
これが正しいものなのかはわかりません。

また、エラー判定等を考慮していないのでこの辺の対策を進めたいですね。

  • エラー時の処理のしかた
  • 対話型プログラムの試験方法

など。

でも本体プログラムを書き換えてもテストコードが通ればOKという点では1つ手間が減った気がします。

参考サイト

unittest --- ユニットテストフレームワーク — Python 3.8.3 ドキュメント

リポジトリ

github.com

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

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

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

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

賭け金勝負する前に勝敗判定部分が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

スリーカードポーカーの手札の確率を計算してみる

スリーカードポーカーゲーム作成PJですが、あとはunittestを書けばひと段落です。
でもその前にシミュレータを改良しました。

改良点

  • 引き分けの処理を追加した
    • カジノだとベット額そのままで次のゲームに行くようなのですが、一勝負ごとに払い戻しの処理にしているのでベット額がそのまま返ってくるようにしました
  • プレイヤーが勝った手札の比率も集計した
    • 勝ったうちの手札の比率も知りたかったので出してみました。

実行

前回と同じ50万回試行します。

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

=== Probabliry of winning or losing ===

Player 250265 wins.
Dealer 249735 wins.
Draw 0 times.

=== Percentage of Players all hands ===

High card!          74.592%
One Pair!           17.021%
Flash!                4.98%
Straight!            2.966%
Three of a kind!     0.239%
Straight Flash!      0.202%

=== Percentage of Players win(250,265 times) hands ===

High card!          74.515%
One Pair!           17.023%
Flash!               4.982%
Straight!             3.04%
Three of a kind!     0.235%
Straight Flash!      0.206%

なんと勝った時の比率も全体の結果と変わりませんでした!
上手くできてるもんですねぇ。

しかし引き分けが出ませんね。
どこか間違えたかな?
ディーラー勝負時かつ同役かつ同数字の場合だから出ないか…。

確率を計算してみる

組み合わせ

シミュレータ作る前に研究すべきことだと思いますが、比率は本当なのか確率を計算してみましょう。
高校ぶりに数学をやりますw

  • スリーカードポーカーの手札の組み合わせ

ジョーカーなしの52枚から3枚引くので{}_{52}  C _3 \通りですね。
せっかくなのでpythonで計算しましょう。

itertoolsのcombinationsを使います。

>>> val = set(itertools.combinations(range(52),3))
>>> len(val)
22100

22100通りです。
3枚だけでも結構組み合わせがありますね。

確率

役の組み合わせから22100を割れば出せますね。
確率の低いものから出してみます。

  • ストレートフラッシュ

1種類のスートでA,2,3 ~ 12,13,A までの組み合わせです。12通りあります。
スートが4種類なので48通り。

>>> 48 / 22100
0.0021719457013574662

で0.21%です。

  • スリーカード

同じ数字3枚です。
4種類のスートから3枚引く組み合わせは{}_4 C _2 \

>>> len(set(itertools.combinations(range(4),3)))
4

数字が13まであるので4×13で52通りです。

>>> 52 / 22100
0.002352941176470588

で0.23%です。

  • ストレート

3枚連続した数字です。
4種のスートを問わず3枚引くので43で64通り。
ストレートのパターンは12通りなので64×12で768通り。
ストレートフラッシュのパターンが重複してるので768 - 48で720通りです。

>>> 720 / 22100
0.03257918552036199

で3.25%です。

  • フラッシュ

1種類のスートから3枚引くので{}_{13} C _3 \通り。

>>> len(set(itertools.combinations(range(13),3)))
286

スートは4種類あるから286×4で1144通り。
これもストレートフラッシュの重複を抜いて1144 - 48で1096通りです。

>>> 1096 / 22100
0.049592760180995475

で4.95%です。

  • ワンぺア

同じ数2枚の役ですね。

同じ数は4枚あるのでペアの組み合わせは{}_4 C _2 \通り。

>>> len(set(itertools.combinations(range(4),2)))
6

3枚目の手札は何でもいいので上の4枚を除いて48通り

>>> 6 * 48
288

数字は1~13まであるので

>>> 288 * 13
3744
>>> 3744 / 22100
0.16941176470588235

で16.9%ですね。
3枚だと意外と出ないもんですね。

残りはいわゆる役無しなので今までの組み合わせを引いた数を割れば出ます。

イカード以外の役は

>>> 48 + 52 + 720 + 1096 + 3744
5660

で全体から引いて

>>> 22100 - 5660
16440

を全体で割って

>>> 16440 / 22100
0.7438914027149321

で74.3%ですね。

比較する

確率が出たところでシミュレータの結果と比較してみましょう。

計算した確率 シミュレータ(50万回)の確率
ストレートフラッシュ 0.21% 0.202%
スリーカード 0.23% 0.239%
ストレート 3.25% 2.966%
フラッシュ 4.95% 4.98%
ワンペア 16.9% 17.021%
イカード 74.3% 74.592%

ストレートが少しずれてますが、だいたい近似値になってますよね!
ということは私の判定処理、シミュレータの作りは間違ってなかった!

これは嬉しいです。

ちなみに100回だけ試行するとどうなるのか

回数が多いと大数の法則で真の値に近づいていくのですが、100回だとどうなるんでしょうか。

$ python3 simulater.py 
Three card poker simulator start.
The simulator trials 100 times.

=== Probabliry of winning or losing ===

Player 52 wins.
Dealer 48 wins.
Draw 0 times.

=== Percentage of Players all hands ===

High card!            68.0%
One Pair!             20.0%
Flash!                 7.0%
Straight!              3.0%
Three of a kind!       2.0%

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

High card!          71.154%
One Pair!           19.231%
Flash!               7.692%
Three of a kind!     1.923%

やはり回数が少ないと比率はばらけますね。
100回ではストレートフラッシュは出ませんでした。
ストレート3回も出したのに全部負けてますw

むすび

自分で作ったツールと数学の計算結果が合いました。
処理が正しい構造になってたんだと分かって自身が付きますね。

確率計算なんて高校ぶりで、数学は苦手でしたが今回は計算を自動でやってくれるので
取っつきにくさが無かったです。
苦手と思っていたのは計算が面倒なだけだったのかもしれません。

…と言ったものの、確率の答え合わせしたら一部間違えていたのでやはり苦手ですねw

参考サイト

  • ポーカーの確率
  • この他、答え合わせでスリーカードポーカーのサイトも見ました(カジノ系なので伏せます)

参考本

www.amazon.co.jp

リポジトリ

github.com

スリーカードポーカーゲームを作りたい シミュレータ作る編

細かい修正はあるものの前回の記事でゲーム作成は一段落しました。
しかしユニットテストの作成まではこのシリーズとして続けたいと思います。


プログラミングでやってみたかった事の1つはシミュレータの作成です。
疑似ではあるもののとても現実的ではない試行回数でのデータが取れます。

という訳でスリーカードポーカーのシミュレータを作ります。

やりたいこと

勝負を指定回数行い以下の統計を出します。

  • プレーヤーの勝率
  • 手役の確率

コード

試行回数は多いほうがデータの信憑性が増えますので50万回まわしてみます。
本体のコードはゲームとして作ったのでシミュレータコードは別ファイルで作ります。

本体のファイルからクラスをインポートして、ディーラーとの勝負まで似たようなコードを書いて計算処理を書くだけです。
クラスを使いまわせてすごく楽ちんで便利です。
これがオブジェクト指向かー。

  • simulater.py
import pokerapp
import collections

match_list = []
hand_list = []

trial = 500000

def Trials():
    for _ in range(trial):

        deck = pokerapp.Deck()
        deck.shuffle()

        player = pokerapp.Handout(deck)
        dealer = pokerapp.Handout(deck)

        match_result = pokerapp.Match(player, dealer)
        hand_result = pokerapp.Handcheck(player).get_role()[0][0]
        
        match_list.append(match_result)
        hand_list.append(hand_result)

def Wins():
    for w in collections.Counter(match_list).items():
        if w[0] == 0:
            print('Player {} wins.'.format(w[1]))
        else:
            print('Dealer {} wins.'.format(w[1]))

def Hands():

    # Sort percntages in descending order
    hand_dict = collections.Counter(hand_list)
    sorted_hand_dict = sorted(hand_dict.items(), key=lambda x:x[1], reverse=True)

    for hand, prob in sorted_hand_dict:
        # Convert to str to use rjust
        p  = str(round((prob / trial) * 100, 3))

        shaped_hand = hand.ljust(16)
        shaped_propotion = (p + '%').rjust(10)

        print(shaped_hand, shaped_propotion)

def main():

    print('Three card poker simulator start.')
    print('The simulator trials {} times.'.format(trial))

    Trials()

    print('\n=== Probabliry of winning or losing ===\n')

    Wins()

    print('\n=== Percentage of Players hands ===\n')
    
    Hands()

if __name__ == '__main__':
    main()

動かすとこんな感じ

せっかくなので時間も測りましょう。

サブ機のubuntuで作っているのですが、古いマシンなので低スペックです。

CPU: Corei5-2400
メモリ: 4GB

ですw

f:id:paloma69:20200509111830p:plain

  • 実行

50万回のテストで32秒かかりました。
回数に対して遅い…のか?相場がわかりませんw

でも実際のカードやGTA内で検証するより遥かに早いスピードです。

masashi@PC-ubuntu:~/Three-card-poker$ time python3 simulater.py 
Three card poker simulator start.
The simulator trials 500000 times.

=== Probabliry of winning or losing ===

Dealer 249505 wins.
Player 250495 wins.

=== Percentage of Players hands ===

High card!          74.657%
One Pair!           16.926%
Flash!               5.008%
Straight!            2.971%
Three of a kind!     0.249%
Straight Flash!      0.188%

real    0m32.916s
user    0m32.904s
sys 0m0.012s

結果を分析

分析なんて大層な物ではないですが、結果を見てみましょう。

  • 勝率
Dealer 249505 wins.
Player 250495 wins.

勝率は大体5分ですね。
手札は運任せなので大勝ちはできませんね。

本体の勝敗判定をもう少し細かくすればちゃんとしか結果が出そうです。
同役の場合一番高いカードの番号しか見ていないので、3枚目まで見て判定すればちゃんとした勝敗が出ます。

  • 役の分布

試行回数の中で出た役が高い順にソートしてます。
上手くできたもので配当(ペアプラスボーナス)の低い順になってますね。

手役はほとんどハイカードです。
3枚のポーカーと言えど中々揃いません。
スリーカード、ストレートフラッシュはこのゲームでもレアモノですね。

High card!          74.657%
One Pair!           16.926%
Flash!               5.008%
Straight!            2.971%
Three of a kind!     0.249%
Straight Flash!      0.188%

勝ったパターンのみの手役統計を出せばより良かったかもしれません。

チップを賭けていたらどうなるのか

スリーカードポーカーはペアプラスボーナスとアンティボーナスの払い戻しがベットとは別に返ってきますが、チップの賭け方まで意識して計算したらどうなるのでしょうか?

カジノのゲームとして考えるとゲームの勝敗よりチップを増やすのが本質なので、どう賭けたらチップを増やして帰れるのかというのが見えるかもしれません。

と考えたところで今回はここまでしか作成していないので、また続きは次回以降にしたいと思います。

スリーカードポーカーゲームを作りたい やっとゲームになった編

今月ずっと取り掛かっているスリーカードポーカーゲームですが、やっとゲームっぽいものを完成させることができました。

コード

前回コミットしてからこれだけ追加しました。
長いのでここに乗せるべきではないかもしれませんが備忘として。

小出しにするのもなんだと思い今回の記事まで一気に作成しました。
エラー処理はちょっと甘いです。

--- a/pokerapp.py
+++ b/pokerapp.py
@@ -1,7 +1,5 @@
-
 import random
 
-
 class Deck:
 
    def __init__(self):
@@ -25,7 +23,6 @@ class Deck:
    def shuffle(self):
        random.shuffle(self.deck)
 
-
    def draw(self):
        return self.deck.pop(0)
 
@@ -46,6 +43,37 @@ class Handcheck:
    def result(self):
        return list(self.player[0].values()), self.player[1]
 
+class Payoff:
+
+   # Use "in" to hand return as a list
+
+   def __init__(self, bet, hand):
+       self.bet = bet
+       self.hand = hand
+
+   def anti_bonus(self):
+       if 4 in self.hand:
+           return self.bet * 1
+       elif 5 in self.hand: 
+           return self.bet * 4
+       elif 6 in self.hand:
+           return self.bet * 5
+       else:
+           return 0
+
+   def pairplus_bonus(self):
+       if 2 in self.hand:
+           return self.bet * 1
+       elif 3 in self.hand:
+           return self.bet * 4
+       elif 4 in self.hand:
+           return self.bet * 6
+       elif 5 in self.hand: 
+           return self.bet * 30
+       elif 6 in self.hand: 
+           return self.bet * 40
+       else:
+           return 0
 
 def Handout(card):
    hand = []
@@ -59,7 +87,7 @@ def Judge(hands, dealer=False):
 
    # Devide hands into suits and numbers
    suit = [ hands[0][0] , hands[1][0] , hands[2][0] ]
-   rank = [int(x[1:]) for x in hands]
+   rank = [ int(x[1:]) for x in hands ]
 
    # Sort of numbers for role evaluation
    rank.sort()
@@ -88,21 +116,23 @@ def Judge(hands, dealer=False):
 def Match(p1, p2):
 
    if p1[0] > p2[0]:
-       print('Player WIN!')
+       return 0
    elif p1[0] < p2[0]:
-       print('Dealer WIN!')
+       return 1
    # Compare number in case draw rank hand
    elif p1[0] == p2[0]:
        if p1[1] > p2[1]:
-           print('Player WIN!')
+           return 0
        elif p1[1] < p2[1]:
-           print('Dealer WIN!')
+           return 1
        # Player wins in case of draw
        else:
-           print('Player WIN!')
+           return 0
 
 def Shaping(hand):
+
    h = []
+   
    for i in hand:
        if '11' in i:
            h.append(i.replace('11', 'J'))
@@ -117,9 +147,51 @@ def Shaping(hand):
    return h
 
 # main
-def main():
+def main(chip):
+
+   print('Your chips are {}'.format(chip))
 
    # Ante
+   while True:
+       i = input('Do you want to bet ante?: [y/n]')
+       if i == 'y':
+           ante = input('How much do you bet?: ')
+           try:
+               ante = int(ante)
+               bet_ante = ante
+
+           except:
+               pass
+
+           break 
+
+       elif i == 'n':
+           bet_ante = 0
+           break 
+
+       else:
+           pass
+
+   # Pair plus
+   while True:
+       i = input('Do you want to bet Pair plus?: [y/n]')
+       if i == 'y':
+           pp = input('How much do you bet?: ')
+           try:
+               pp = int(pp)
+               bet_pairplus =  pp
+
+           except:
+               pass
+
+           break
+
+       elif i == 'n':
+           bet_pairplus = 0
+           break
+
+       else:
+           pass
 
    # Game start
    card = Deck()
@@ -137,15 +209,96 @@ def main():
    p_show = Shaping(player)
    d_show = Shaping(dealer)
 
-   print("Player Hand: {} {}\nDealer Hand: {} {}\n".format( \
-       p_show, p_role[0], \
-       d_show, d_role[0] ))
-
-   Match(p_hand, d_hand)
+   print("\n===== Open Your Hand =====\n")
+   print("Your Hand: {} {}\n".format( p_show, p_role[0] ))
 
    # bet, fold
 
+   print('The same chips as Ante is required to match the dealer.')
+   while True:
+
+       if bet_ante == 0 and bet_pairplus == 0:
+           refund_bet = 0
+           refund_ante = 0
+           break
+
+       else:
+
+           i = input('Do you play in your hand?: [y/n]')
+
+           # Open dealer hand
+
+           print("\n===== Open Dealer Hand =====\n")
+           print("Dealer Hand: {} {}\n".format( d_show, d_role[0] ))
+
+           if i == 'y':
+               try:
+                   # b = int(b)
+                   bet_play = bet_ante
+
+                   # Win / Lose Judge
+                   
+                   match = Match(p_hand, d_hand)
+                   if match == 0:
+                       print('You WIN!')
+
+                       refund_bet = bet_play * 2
+                       refund_ante = bet_play * 2
+
+                   elif match == 1:
+                       print('Dealer WIN!')
+
+                       # Forfelt the bet
+                       refund_bet = 0
+
+                       # Forfelt the ante ig there is no bonus
+                       # Make int type with angle brakets to use inequality sign
+                       if p_hand[0][0] < 3:
+                           refund_ante = 0
+                       else:
+                           pass
+
+                       chip = chip - (bet_play + bet_ante + bet_pairplus)
+
+               except:
+                   pass
+
+           elif i == 'n':
+               print('You are fold.')
+
+               # bet confiscation
+               chip = chip - (bet_ante + bet_pairplus)
+
+               refund_bet = 0
+               refund_ante = 0
+
+           break
+
    # pay off
+   # Win refund
+   all_refund = refund_ante + refund_bet
+
+   # Hand bonus
+   pay_ante = Payoff(bet_ante, p_hand[0]).anti_bonus()
+   pay_pairplus = Payoff(bet_pairplus, p_hand[0]).pairplus_bonus()
+
+   # To reuse the chips for main loop
+   global total
+
+   total = chip + (all_refund + pay_ante + pay_pairplus)
+
+   print('Your chips are {}'.format(total))
 
 if __name__ == '__main__':
-   main()
+
+   # Initial chips
+   chip = 1000
+
+   while True:
+       main(chip)
+       i = input('Continue?: [y/n]')
+       if i == 'y':
+           chip = total
+       else:
+           break
+

以前の整形からベット処理を丸々追加したのでコードが倍くらいになってしまいました。
ゲームとして作成するとどうしても対話型になるので分岐処理がややこしいですね。

簡単な解説

すべての解説はしきれないのですが、
追記したところは

  • アンティ、ペアプラスベットの選択
  • カードドロー # 作成済み
  • 手役判定 # 作成済み
  • 手札勝負ベット (アンティと同額)
  • 払い戻し

です。

アンティ、ペアプラスは金額を賭けるまでループさせるようにしました。
払い戻しは前回の通り手役判定のリターンからベット額をn倍するだけです。

各ベット、勝負、払い戻しはすべてメイン処理に書いてしまいました。
アンティ、ペアプラスベット時のループも関数にできそうですが、
とりあえず早く動くものを作りたかったので今回はメイン内にべた書きで仕上げてしまいました。
(当初は手札引いて判定するところまでしか構想になかったので関数化が間に合いませんでした。)

ゲームプレイはこんな感じ

ゲーム開始時に1000$もらえます。(単位つけ忘れました)
アプリを終了させるまでゲーム終了時のチップを持ち越すことができます。

$ python3 pokerapp.py 
Your chips are 1000
Do you want to bet ante?: [y/n]y
How much do you bet?: 10
Do you want to bet Pair plus?: [y/n]y
How much do you bet?: 10

===== Open Your Hand =====

Your Hand: ['♢2', '♡10', '♡3'] ['High card!']

The same chips as Ante is required to match the dealer.
Do you play in your hand?: [y/n]y

===== Open Dealer Hand =====

Dealer Hand: ['♠6', '♣K', '♡J'] ['High card!']

Dealer WIN!
Your chips are 970
Continue?: [y/n]y
Your chips are 970
Do you want to bet ante?: [y/n]y
How much do you bet?: 100
Do you want to bet Pair plus?: [y/n]y
How much do you bet?: 100

===== Open Your Hand =====

Your Hand: ['♢7', '♣A', '♢A'] ['One Pair!']

The same chips as Ante is required to match the dealer.
Do you play in your hand?: [y/n]y

===== Open Dealer Hand =====

Dealer Hand: ['♡9', '♡6', '♠9'] ['One Pair!']

You WIN!
Your chips are 1470
Continue?: [y/n]y
Your chips are 1470
Do you want to bet ante?: [y/n]y
How much do you bet?: 100
Do you want to bet Pair plus?: [y/n]y
How much do you bet?: 100

===== Open Your Hand =====

Your Hand: ['♡5', '♣J', '♡A'] ['High card!']

The same chips as Ante is required to match the dealer.
Do you play in your hand?: [y/n]y

===== Open Dealer Hand =====

Dealer Hand: ['♢8', '♢3', '♠7'] ["Less than Queen-high. Dealer can't play"]

You WIN!
Your chips are 1870
Continue?: [y/n]n

対話型になってるし、終了させるまで続けられるのでゲームになってますよね!
いい感じじゃないでしょうか。
英文の正しい文法等はいったん置いておきます。

何もしない判定

私しかプレイしないので、アンティもペアプラスも賭けないというパターンは発生しませんが、
どちらも賭けない場合の処理も作っておきます。
ゲームとして成立しないのですが、何もせず次のゲームに行くという処理にします。

アンティもペアプラスも賭けないと次のゲームに移ります。
余計な文も出てるしもう少しいい処理にしたかったのですが、今回は断念してエラーを無くすことに集中しました。

$ python3 pokerapp.py 
Your chips are 1000
Do you want to bet ante?: [y/n]n
Do you want to bet Pair plus?: [y/n]n

===== Open Your Hand =====

Your Hand: ['♣8', '♡2', '♠6'] ['High card!']

The same chips as Ante is required to match the dealer.
Your chips are 1000
Continue?: [y/n]n

むすび

テキストオンリーではありますが、初めてゲームと呼べるものを作れたと思います。
一から自分で作ると動いたときに感慨深いものがありますね。

ゲームだと似た処理の繰り返しになるので、自作クラス・関数の有効性も理解が進みました。

今月は丸々これの作成に費やしてしまいましたが、プログラミングについて色々勉強にもなりました。
ファイル構成・書き方の作法等はまだまだですが、クラス系は割と使えるようになったんじゃないかな。

これをリファクタリングしつつ、シミュレータの作成、テストコードの作成などやっていきたいと思います。

直したいところ
  • チップ不足時の終了処理
    • これは早めに
  • 役判定時に数字を出力する
    • 「○○のワンぺア」的な
  • メイン処理内を関数化する
  • 払い戻しのレートを出力する
    • 初めてやる人用
  • クラスとかは別ファイル出し?
  • 英語の文法を直す

タスクがどんどん増えていきますねえ。


本シリーズ

リポジトリ

github.com

スリーカードポーカーゲームを作りたい 払い戻し機能を作る編

作成中のスリーカードポーカーゲームですがゲームのメインであるベット、払い出しの作成が残っています。

賭け系の処理は

  • 賭ける
  • 勝負
  • 払い戻し

の順となりますが、ゲームを続ける限りチップの増減が発生します。
実装するにはこれらの流れをループさせないと行けません。

自分のレベルだと気が遠くなりそうな処理が残ってますが、遠くなるのはまだ早いです。
出来るところからやりましょう。

という訳で今回は払い戻し機能を作ります。

スリーカードポーカーの払い戻し

スリーカードポーカーは3つの払い戻しがあって

  • 手札で勝つ
    • アンティを賭けないと勝負できない
    • 払い戻しはベット額の2倍
  • アンティボーナス
    • 手札役ボーナス
    • 手札で負けてももらえる
  • アプラスボーナス
    • アンティとは別にベットする
    • 手札役ボーナス
    • 手札で負けてももらえる

があります。

細かい配当

  • アンティボーナス
    • ストレート 1:1
    • スリーカード 5:1
    • ストレートフラッシュ 6:1
  • アプラスボーナス
    • ワンペア 1:1
    • フラッシュ 4:1
    • ストレート 6:1
    • スリーカード 30:1
    • ストレートフラッシュ 40:1

カジノによってレートが違うそうですが、GTAオンラインのレートを採用しています。

作成済みの役判定の戻り値が使えそうなので、今回は試しにペアプラスボーナスを作ってみます。
チップを賭ける部分は作ってないですが、役ごとにn倍でいいですね。

手札判定の戻り値

手札判定後は役名と役ランク値が辞書型で返ってきます。
この役ランク値を流用しましょう。 強い役順に6~1のランクが返ってくるのでこの値を払い戻しの役判定に使います。

  • 役判定の関数
def Judge(hands, dealer=False):

    # Devide hands into suits and numbers
    suit = [ hands[0][0] , hands[1][0] , hands[2][0] ]
    rank = [int(x[1:]) for x in hands]

    # Sort of numbers for role evaluation
    rank.sort()

    # Evaluation of hand
    if len(set(suit)) == 1 and rank[1] == rank[0] + 1 and rank[2] == rank[1] + 1:
        return {'Straight Flash': 6}, max(rank)
    elif len(set(suit)) == 1 and rank[0] == 14 and rank[1] == 2 and rank[2] == 1:
        return {'Straight Flash': 6}, max(rank)
    elif len(set(rank)) == 1:
        return {'Three of a kind!': 5}, max(rank)
    elif rank[1] == rank[0] + 1 and rank[2] == rank[1] + 1:
        return {'Straight!': 4}, max(rank)
    elif rank[0] == 14 and rank[1] == 2 and rank[2] == 1:
        return {'Straight!': 4}, max(rank)
    elif len(set(suit)) == 1:
        return {'Flash!': 3}, max(rank)
    elif len(set(rank)) == 2:
        return {'One Pair!': 2}, max(rank)
    elif max(rank) < 12 and dealer == True:
        return {'Less than Queen-high. Dealer can\'t play': 0}, max(rank)
    elif max(rank):
        return {'High card!': 1}, max(rank)
        

アプラスの払い戻しをコードにするとこんな感じです。
handrankの部分にJudgeで帰ってきた役ランク値を入れます。

def Pair_plus(handrank, chip):
     if 2 in handrank:
         return chip * 1
     elif 3 in handrank:
         return chip * 4
     elif 4 in handrank:
         return chip * 6
     elif 5 in handrank:
         return chip * 30
     elif 6 in handrank:
         return chip * 40
     else:
         return None

動かしてみる

肝心のチップを賭ける処理が作れていないので、試しにこの部分だけターミナルで動かしてみます。
本当はpython使いとしてpdbとか使って動かしてみるのがいいんでしょうけど難しいので手動でやります。

>>> import pokerapp
>>> 
>>> d = pokerapp.Deck()
>>> d.shuffle()
>>> 
>>> 
>>> player = pokerapp.Handout(d)
>>> player
['♢7', '♢13', '♢2']
>>> 
>>> h = pokerapp.Judge(player)
>>> h
({'Flash!': 3}, 13)
>>> 
>>> def Pair_plus(handrank, chip):
...     if 2 in handrank:
...         return chip * 1
...     elif 3 in handrank:
...         return chip * 4
...     elif 4 in handrank:
...         return chip * 6
...     elif 5 in handrank:
...         return chip * 30
...     elif 6 in handrank:
...         return chip * 40
...     else:
...         return None
... 
>>> 
>>> Pair_plus(h[0].values(),100)
400

手札はダイヤのフラッシュです。
チップを100賭けていたとして、ちゃんと4倍の400が返ってきますね。

ここで思ったのですが、アンティボーナスも同じ仕組みなので
払い戻しは同じ機能を持たせる様にクラス化しちゃった方が良さそうですね。
(クラスを作るってのはこういう事か…?)

払い戻しの疑似コード

クラス化はさっき思い付いたばかりなのでとりあえず疑似コードで残しておきます。

class payoff (bet, hand)

    def anti

      if hand == straight 
        bet * 1
      elif hand == three of a kind 
        bet * 4
      elif hand == straignt flash 
        bet * 5
      else 
        none

    def pairplus

      if hand == pair 
        bet * 1
      elif hand == flush 
        bet * 4
      elif hand == straight 
        bet * 6
      elif hand == three of a kind 
        bet * 30
      elif hand == straight flush 
        bet * 40
      else
        none

    def check
      bet + anti + pairplus

これでいけそうな気がします。
あとはゲーム開始~終了までの賭け金処理をどうするかですね。

スリーカードポーカーのややこしいところは勝負に負けても役ボーナスで配当が発生するところなんですよね。
ベット額、アンティ、ペアプラスのタプルとかにしてそれぞれの値を入れ換えていくのがいいのかな?
そして勝負後に各払い戻し分を足して返ってくると。

これが出来てから一連の処理をループさせればゲームになりそうですね。
先がちょっと見えてきました。


本シリーズ