ポーカーのカード交換の最善手の統計を取るプログラムを作りました。
Q&A
Closed
pythonで一人用ポーカーのプログラムを作りました。
手札5枚を0枚から5枚交換する32通りのカードチェンジを行い、それぞれの役を判定。最も高い役をカウントして役の出現率の統計を取りました。ハイカードは役としてカウントしていません。
ちなみに10万回試行した場合の統計は次の通りでした。
追記
手札が2,3,4,5,7(スートバラバラ)、山に9,10,J,Q,Xとあった場合、xがどの値であっても
必ずストレート又はワンペアが成立します。なのでノーペアは出現しません。
3回目の変更でノーペアの出現を確認しました。前言撤回します。
ワンペア 12023
ツーペア 32377
スリーカード 35833
ストレート 9195
フラッシュ 5465
フルハウス 4341
フォーカード 718
ストレートフラッシュ 40
ロイヤルストレートフラッシュ 8
合計 100000
解決したいこと
何度かテストしましたが、もしバグや欠陥があればお知らせ下さい。
import itertools
import random
from collections import Counter
# ポーカーハンドのランク
RANKS = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
# スート
SUITS = ['♠', '♥', '♦', '♣']
# 役のランク
HAND_RANKS = ['ノーペア', 'ワンペア', 'ツーペア', 'スリーカード', 'ストレート', 'フラッシュ',
'フルハウス', 'フォーカード', 'ストレートフラッシュ', 'ロイヤルストレートフラッシュ']
# デッキを作成
deck = [rank + suit for rank in RANKS for suit in SUITS]
# 役の統計を保存する辞書
hand_statistics = Counter()
# 役判定関数
def judge(hand):
ranks = sorted([card[:-1] for card in hand], key=RANKS.index)
suits = [card[-1] for card in hand]
rank_count = Counter(ranks)
suit_count = Counter(suits)
# ロイヤルストレートフラッシュ
if ranks == RANKS[8:] and len(set(suits)) == 1:
return "ロイヤルストレートフラッシュ"
# ストレートフラッシュ
elif len(set(suits)) == 1 and "".join(ranks) in "".join(RANKS):
return "ストレートフラッシュ"
# フォーカード
elif 4 in rank_count.values():
return "フォーカード"
# フルハウス
elif set(rank_count.values()) == {2, 3}:
return "フルハウス"
# フラッシュ
elif len(set(suits)) == 1:
return "フラッシュ"
# ストレート
elif "".join(ranks) in "".join(RANKS):
return "ストレート"
# スリーカード
elif 3 in rank_count.values():
return "スリーカード"
# ツーペア
elif list(rank_count.values()).count(2) == 2:
return "ツーペア"
# ワンペア
elif 2 in rank_count.values():
return "ワンペア"
else:
return "ノーペア"
# 1万回の試行
for _ in range(10000):
# デッキからランダムに5枚のカードを選ぶ
hand = random.sample(deck, 5)
# 手札を0枚から5枚までチェンジする32通りの組み合わせを考える
best_hand = hand.copy()
best_rank = judge(hand)
for i in range(6):
for cards_to_change in itertools.combinations(range(5), i):
new_hand = hand.copy()
for j in cards_to_change:
remaining_deck = [card for card in deck if card not in new_hand]
new_hand[j] = random.choice(remaining_deck)
new_rank = judge(new_hand)
if HAND_RANKS.index(new_rank) > HAND_RANKS.index(best_rank):
best_hand, best_rank = new_hand, new_rank
hand_statistics[best_rank] += 1
print(hand_statistics)
コード変更1回目
ご指摘を受けて次のようにデバッグしました。
1,山から取ったカードは山から取り除く。(忘れてました)
2.A-2-3-4-5 の時もストレート、ストレートフラッシュ判定に加える。
import itertools
import random
from collections import Counter
# ポーカーハンドのランク
RANKS = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
# スート
SUITS = ['♠', '♥', '♦', '♣']
# 役のランク
HAND_RANKS = ['ノーペア', 'ワンペア', 'ツーペア', 'スリーカード', 'ストレート', 'フラッシュ',
'フルハウス', 'フォーカード', 'ストレートフラッシュ', 'ロイヤルストレートフラッシュ']
# デッキを作成
deck = [rank + suit for rank in RANKS for suit in SUITS]
# 役の統計を保存する辞書
hand_statistics = Counter()
# 役判定関数
def judge(hand):
ranks = sorted([card[:-1] for card in hand], key=RANKS.index)
suits = [card[-1] for card in hand]
rank_count = Counter(ranks)
suit_count = Counter(suits)
# ロイヤルストレートフラッシュ
if ranks == RANKS[8:] and len(set(suits)) == 1:
return "ロイヤルストレートフラッシュ"
# ストレートフラッシュ
elif len(set(suits)) == 1 and ("".join(ranks) in "".join(RANKS) or ranks == RANKS[:4] + ['A']):
return "ストレートフラッシュ"
# フォーカード
elif 4 in rank_count.values():
return "フォーカード"
# フルハウス
elif set(rank_count.values()) == {2, 3}:
return "フルハウス"
# フラッシュ
elif len(set(suits)) == 1:
return "フラッシュ"
# ストレート
elif "".join(ranks) in "".join(RANKS) or ranks == RANKS[:4] + ['A']:
return "ストレート"
# スリーカード
elif 3 in rank_count.values():
return "スリーカード"
# ツーペア
elif list(rank_count.values()).count(2) == 2:
return "ツーペア"
# ワンペア
elif 2 in rank_count.values():
return "ワンペア"
else:
return "ノーペア"
# 1万回の試行
for _ in range(10000):
# デッキからランダムに5枚のカードを選ぶ
hand = random.sample(deck, 5)
# 手札を0枚から5枚までチェンジする32通りの組み合わせを考える
best_hand = hand.copy()
best_rank = judge(hand)
for i in range(6):
for cards_to_change in itertools.combinations(range(5), i):
new_hand = hand.copy()
remaining_deck = [card for card in deck if card not in new_hand]
for j in cards_to_change:
new_card = random.choice(remaining_deck)
new_hand[j] = new_card
remaining_deck.remove(new_card) # 選ばれたカードを山から除く
new_rank = judge(new_hand)
if HAND_RANKS.index(new_rank) > HAND_RANKS.index(best_rank):
best_hand, best_rank = new_hand, new_rank
hand_statistics[best_rank] += 1
print(hand_statistics)
改めて10万回試行しました。
ワンペア 11609
ツーペア 31548
スリーカード 35811
ストレート 10396
フラッシュ 5546
フルハウス 4275
フォーカード 757
ストレートフラッシュ 54
ロイヤルストレートフラッシュ 4
合計 100000
変更2回目
A-2-3-4-5の時も必ずストレート判定になるように変更しました。
ワンペアの判定を改善しました。
import itertools
import random
from collections import Counter
# ポーカーハンドのランク
RANKS = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
# スート
SUITS = ['♠', '♥', '♦', '♣']
# 役のランク
HAND_RANKS = ['ノーペア', 'ワンペア', 'ツーペア', 'スリーカード', 'ストレート', 'フラッシュ',
'フルハウス', 'フォーカード', 'ストレートフラッシュ', 'ロイヤルストレートフラッシュ']
# デッキを作成
deck = [rank + suit for rank in RANKS for suit in SUITS]
# 役の統計を保存する辞書
hand_statistics = Counter()
# 役判定関数
def judge(hand):
ranks = sorted([card[:-1] for card in hand], key=RANKS.index)
suits = [card[-1] for card in hand]
rank_count = Counter(ranks)
suit_count = Counter(suits)
# ロイヤルストレートフラッシュ
if ranks == RANKS[8:] and len(set(suits)) == 1:
return "ロイヤルストレートフラッシュ"
# ストレートフラッシュ
elif len(set(suits)) == 1 and ("".join(ranks) in "".join(RANKS) or ranks == ['A', '2', '3', '4', '5']):
return "ストレートフラッシュ"
# フォーカード
elif 4 in rank_count.values():
return "フォーカード"
# フルハウス
elif set(rank_count.values()) == {2, 3}:
return "フルハウス"
# フラッシュ
elif len(set(suits)) == 1:
return "フラッシュ"
# ストレート
elif "".join(ranks) in "".join(RANKS) or ranks == ['A', '2', '3', '4', '5']:
return "ストレート"
# スリーカード
elif 3 in rank_count.values():
return "スリーカード"
# ツーペア
elif list(rank_count.values()).count(2) == 2:
return "ツーペア"
# ワンペア
elif list(rank_count.values()).count(2) == 1:
return "ワンペア"
else:
return "ノーペア"
# 1万回の試行
for _ in range(10000):
# デッキからランダムに5枚のカードを選ぶ
hand = random.sample(deck, 5)
# 手札を0枚から5枚までチェンジする32通りの組み合わせを考える
best_hand = hand.copy()
best_rank = judge(hand)
for i in range(6):
for cards_to_change in itertools.combinations(range(5), i):
new_hand = hand.copy()
remaining_deck = [card for card in deck if card not in new_hand]
for j in cards_to_change:
new_card = random.choice(remaining_deck)
new_hand[j] = new_card
remaining_deck.remove(new_card) # 選ばれたカードを山から除く
new_rank = judge(new_hand)
if HAND_RANKS.index(new_rank) > HAND_RANKS.index(best_rank):
best_hand, best_rank = new_hand, new_rank
hand_statistics[best_rank] += 1
print(hand_statistics)
コード変更3回目
1,必ず交換するカードは山の一番上から取っていく
2,1ゲーム終了後、新たに52枚のフルデッキを作り直す
3,ノーペアが出現します。
import itertools
import random
from collections import Counter
# ポーカーハンドのランク
RANKS = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
# スート
SUITS = ['♠', '♥', '♦', '♣']
# 役のランク
HAND_RANKS = ['ノーペア', 'ワンペア', 'ツーペア', 'スリーカード', 'ストレート', 'フラッシュ',
'フルハウス', 'フォーカード', 'ストレートフラッシュ', 'ロイヤルストレートフラッシュ']
# 役判定関数
def judge(hand):
ranks = sorted([card[:-1] for card in hand], key=RANKS.index)
suits = [card[-1] for card in hand]
rank_count = Counter(ranks)
suit_count = Counter(suits)
# ロイヤルストレートフラッシュ
if ranks == RANKS[8:] and len(set(suits)) == 1:
return "ロイヤルストレートフラッシュ"
# ストレートフラッシュ
elif len(set(suits)) == 1 and ("".join(ranks) in "".join(RANKS) or ranks == ['A', '2', '3', '4', '5']):
return "ストレートフラッシュ"
# フォーカード
elif 4 in rank_count.values():
return "フォーカード"
# フルハウス
elif set(rank_count.values()) == {2, 3}:
return "フルハウス"
# フラッシュ
elif len(set(suits)) == 1:
return "フラッシュ"
# ストレート
elif "".join(ranks) in "".join(RANKS) or ranks == ['A', '2', '3', '4', '5']:
return "ストレート"
# スリーカード
elif 3 in rank_count.values():
return "スリーカード"
# ツーペア
elif list(rank_count.values()).count(2) == 2:
return "ツーペア"
# ワンペア
elif list(rank_count.values()).count(2) == 1:
return "ワンペア"
else:
return "ノーペア"
# 役の統計を保存する辞書
hand_statistics = {hand_rank: 0 for hand_rank in HAND_RANKS}
# 1万回の試行
for _ in range(10000):
# デッキを作成し、シャッフルする
deck = [rank + suit for rank in RANKS for suit in SUITS]
random.shuffle(deck)
# デッキの上から5枚のカードを取る
hand = [deck.pop() for _ in range(5)]
# 手札を0枚から5枚までチェンジする32通りの組み合わせを考える
best_hand = hand.copy()
best_rank = judge(hand)
for i in range(6):
for cards_to_change in itertools.combinations(range(5), i):
new_deck = deck.copy() # デッキをコピー
new_hand = hand.copy()
for j in cards_to_change:
new_hand[j] = new_deck.pop() # 山から新しいカードを取る
new_rank = judge(new_hand)
if HAND_RANKS.index(new_rank) > HAND_RANKS.index(best_rank):
best_hand, best_rank = new_hand, new_rank
hand_statistics[best_rank] += 1
print(hand_statistics)
試行統計
10万回の試行統計は次の通りです。
ノーペア 2800
ワンペア 37137
ツーペア 34083
スリーカード 11663
ストレート 6391
フラッシュ 3974
フルハウス 3498
フォーカード 415
ストレートフラッシュ 34
ロイヤルストレートフラッシュ 5
合計 100000