前回のおさらい
さて、前回は当初想定第1シリーズ完走となる、5枚ドローのポーカ手役判定でした。
ストレート系の手役が追加されたので、”ストレート”をいかに表現するかっていう点がメインでした。その他の手役がカードorマークの重複だけなのに、ストレートは数値の連続ということで、これまでとは異なる関数を設定して対応しました。
また、他のファイルで作成した関数を利用するため、importを使っていきました。これができれば、自作関数が誰でも作れるということが理解してもらえたかと思います。
参考までに、前回の記事は以下です。
連載回数 | 実施内容 |
第5回 | ドロー5枚で手役の発生確率を計算してみよう |
第6回 | コードが汚い、さぁリファクタリングだ 〜初心者でも最低限やれるリファクタリング〜 |
第7回 | 仮想プレーヤーと対戦してみよう |
さて、前回延長戦を宣言したので、今回は第5回(延長戦第1回)として、ドロー5枚で手役の発生確率を計算していきます。
実際確率論で計算されている手役の発生確率はどのくらいか?
Joker入れるかどうかで変わってくるのですが、今回作成している52枚山札を前提とすると、確率論(組み合わせ確率)では以下のようになるらしいです。ググれば山ほど出てきますが、だいたいこの程度の水準のようです。

さて、本当に確率論に近似していくのか、実際にコードを使って確認していきます。
実際のコード
import random
import collections
import datetime
import poker_hand_check4card_mark as pkg
class card:
def __init__(self, mark, num):
self.num = num
self.mark = mark
def straight_check(hand_nums):
#ストレートの判定
#1がある場合だけ、14とみなしてストレート判定(数値跨ぎ想定)
if 1 in hand_nums and 10 in hand_nums:
hand_nums.remove(1)
hand_nums.append(14)
#最大値と最小値の差が4になる、平均値の±1の値のカードが含まれる
max_num = max(hand_nums)
min_num = min(hand_nums)
ave_num = (max_num + min_num) / 2
if max_num - min_num == 4 and ave_num in hand_nums and ave_num - 1 in hand_nums and ave_num + 1 in hand_nums:
return True
def check_roll(cards):
hand = [card for card in cards]
hand_nums = [c.num for c in hand]
hand_marks = [c.mark for c in hand]
if pkg.mark_check(hand_marks) == 5 and straight_check(hand_nums) != True:
return 0 #flash!!
elif len(set(hand_nums)) == 4 and pkg.pair_check(hand_nums) == 2:
return 1 #1ペア
elif len(set(hand_nums)) == 3 and pkg.pair_check(hand_nums) == 3:
return 2 #3カード
elif len(set(hand_nums)) == 3 and pkg.pair_check(hand_nums) == 2:
return 3 #2ペア
elif len(set(hand_nums)) == 2 and pkg.pair_check(hand_nums) == 4:
return 4 #4カード
elif straight_check(hand_nums) and pkg.mark_check(hand_marks) != 5:
return 5 #Straight!!
elif len(set(hand_nums)) == 2 and pkg.pair_check(hand_nums) == 3:
return 6 #フルハウス
elif straight_check(hand_nums) and pkg.mark_check(hand_marks) == 5:
if 1 in hand_nums and 10 in hand_nums:
return 8 #ロイヤルストレートフラッシュ
else:
return 7 #ストレートフラッシュ
else:
high = max(hand_nums)
roll = "high card:{}".format(high)
return 9
if __name__ == '__main__':
bill = []
marks = ('♣', '♦', '♥', '♠')
roles = ("flash", "1ペア", "3カード", "2ペア", "4カード", "ストレート", "フルハウス", "ストレートフラッシュ", "ロイヤルストレートフラッシュ", "ブタ")
role_count = [0 for x in range(len(roles))]
bill = [card(k, j) for k in marks for j in range(1, 14)]
random.shuffle(bill)
start_time = datetime.datetime.now()
count = 0
while count < 500000:
count += 1
bill = [card(k, j) for k in marks for j in range(1, 14)]
random.shuffle(bill)
hand = bill[:5]
"""
hand_nums = [c.num for c in hand]
hand_marks = [c.mark for c in hand]
"""
"""
for i in range(len(hand_nums)):
print("カード",i+1,":",hand_nums[i], hand_marks[i])
"""
roll = check_roll(hand)
role_count[roll] += 1
print("-" * 20)
print('{}回施行結果'.format(count))
print("-" * 20)
for i in range(len(roles)):
print('{}: {}回 確率:{}%'.format(roles[i], role_count[i], role_count[i] / count * 100))
end_time = datetime.datetime.now()
print("経過時間:{}".format(end_time - start_time))
実行結果

一定数の施行母数がないと確率的な意味をなさないので、今回は少し少ないですが、50万回実施した結果を表示しています。
こうやってみると、50万回程度実行すると、組み合わせによる確率計算と近似してくるものですね。ただ、ロイヤルストレートフラッシュのようなものは、50万回試した程度では出てくれないそうです。。レア役すぎますねw
まとめ
確率論はいい水準になりました。多数回施行は確率論に近似する(要はコインを投げ続ければ、表裏それぞれ約50%ずつでる)ってやつを体感してみました。
ただ、50万回の施行で、約35秒時間がかかっています。100万回実施すると1分以上かかりそうなコードになってますね。みにくいし。。
ということで、次回はリファクタリングをしていこうかと思います。