ジョーカーを1枚加えて合計53枚のトランプカードを使用した場合の、ポーカーの役ができる確率を計算してみました。
"""
ジョーカーを1枚加えて合計53枚のトランプカードを使用した場合の、
ポーカーの役ができる確率を計算する。
"""
# ジョーカー及び13*4枚のカードを用意する
JOKER=(0,0)
CARDS=[JOKER]+[(i%4+1,i//4+1) for i in range(52)]
# 全パターン数
nPatterns=0
# ファイブカードのパターン数
cntFiveOfAKind=0
# ロイヤルストレートフラッシュのパターン数
cntRoyalStraightFlush=0
# ストレートフラッシュのパターン数
cntStraightFlush=0
# フォーカードのパターン数
cntFourOfAKind=0
# フルハウスのパターン数
cntFullHouse=0
# フラッシュのパターン数
cntFlush=0
# ストレートのパターン数
cntStraight=0
# スリーカードのパターン数
cntThreeOfAKind=0
# ツーペアのパターン数
cntTwoPair=0
# ワンペアのパターン数
cntOnePair=0
# ノーペア(ハイカード)のパターン数
cntNoPair=0
以下、引数handにおいて、
$0\le i<j\le4$に対して$0\le H_{i,1}\le H_{j,1}\le4$
$i>0$ならば$H_i\neq \text{JOKER}$
(0<=i<j<=4
に対してhand[i][1]<=hand[j][1]
,
i>0
ならばhand[0]!=JOKER=(0,0)
)
を前提とします。
def isFiveOfAKind(hand):
"""ファイブカードの判定。
・手札にジョーカーがある
かつ
・残り4枚の数字が全て同じ
場合、True。
"""
return hand[0]==JOKER and hand[1][1]==hand[2][1]==hand[3][1]==hand[4][1]
def isRoyalStraightFlush(hand):
"""ロイヤルストレートフラッシュの判定
手札にジョーカーがある場合
・残り4枚のスートが全て同じ
かつ
・残り4枚の数字が(A,10,J,Q,K)の中の4個で構成されている
場合、True。
手札にジョーカーがない場合
・5枚のスートが全て同じ
かつ
・5枚の数字が(A,10,J,Q,K)で構成されている
場合、True。
"""
if hand[0]==JOKER: #ジョーカーが手札にある場合
if not hand[1][0]==hand[2][0]==hand[3][0]==hand[4][0]:
#異なるスートがある場合、ロイヤルストレートフラッシュでない
return False
#残り4枚の数字が(A,10,J,Q),(A,10,J,K),(A,J,Q,K),(10,J,Q,K)ならば
#ロイヤルストレートフラッシュ成立
return tuple(hand[i][1] for i in range(1,len(hand))) in [(1,10,11,12),(1,10,11,13),(1,10,12,13),(1,11,12,13),(10,11,12,13)]
else:
if hand[0][1]!=1:
#0枚目がエースでない場合、ロイヤルストレートフラッシュでない
return False
for i in range(1,len(hand)):
if hand[i][0]!=hand[0][0] or hand[i][1]!=9+i:
#スートが異なる場合、または
#1~4枚目が(10,J,Q,K)でない場合、即ちi枚目が9+iでない場合
#ロイヤルストレートフラッシュでない
return False
return True
(JOKER,10,J,Q,K)の場合もTrueを返却しますが、後の判定順序で重複カウントしないように制御します。
def isStraightFlush(hand):
"""ストレートフラッシュの判定
手札にジョーカーがある場合※ジョーカーを0枚目とカウント
・残り4枚のスートが全て同じ
かつ
・(2枚目の数字-1枚目の数字,3枚目の数字-1枚目の数字,4枚目の数字-1枚目の数字)=(1,2,3),(1,2,4),(1,3,4),(2,3,4)のいずれかの
(または、(2枚目の数字-1枚目の数字,3枚目の数字-2枚目の数字,4枚目の数字-3枚目の数字)=(1,1,1),(1,1,2),(1,2,1),(2,1,1)のいずれかの)
場合、True。
手札にジョーカーがない場合
・5枚のスートが全て同じ
かつ
・1≦i≦4に対して、i枚目の数字=0枚目の数字+iの
(または、i枚目の数字=i-1枚目の数字+1)の
場合、True。
"""
if hand[0]==JOKER: #ジョーカーが手札にある場合
if not hand[1][0]==hand[2][0]==hand[3][0]==hand[4][0]:
#異なるスートがある場合、ストレートフラッシュでない
return False
#i枚目と1枚目の数字の差で判定
return tuple(hand[i][1]-hand[1][1] for i in range(2,len(hand))) in [(1,2,3),(1,2,4),(1,3,4),(2,3,4)]
#(別解)i枚目とi-1枚目の数字の差で判定
#return tuple(hand[i][1]-hand[i-1][1] for i in range(2,len(hand))) in [(1,1,1),(1,1,2),(1,2,1),(2,1,1)]
else:
for i in range(1,len(hand)):
if hand[i][0]!=hand[0][0] or hand[i][1]!=hand[0][1]+i:
#異なるスートがある、またはi枚目と0枚目の数字の差がiでない場合、
#ストレートフラッシュでない
return False
return True
JOKERが手札にある場合、ファイブカードが成立する場合もTrueを返却しますが、後の判定順序で重複カウントしないように制御します。
def isFourOfAKind(hand):
"""フォーカードの判定
手札にジョーカーがある場合
・残り4枚中3枚の数字が同じならばTrue。
手札にジョーカーがない場合
・5枚中4枚の数字が同じならばTrue。
"""
if hand[0]==JOKER: #手札にジョーカーがある場合
#残り4枚中3枚の数字が同じならばフォーカード
return hand[1][1]==hand[2][1]==hand[3][1]\
or hand[2][1]==hand[3][1]==hand[4][1]
else:
#5枚中4枚の数字が同じならばフォーカード
return hand[0][1]==hand[1][1]==hand[2][1]==hand[3][1]\
or hand[1][1]==hand[2][1]==hand[3][1]==hand[4][1]
JOKERが手札にある場合、残り4枚中3枚が同じ数字の場合、フォーカードが成立しますが、フルハウスの判定でもTrueを返却することにしています。
4枚同じ数字の場合は勿論ファイブカードです。
後の判定順序で重複カウントしないように制御します。
def isFullHouse(hand):
"""フォーカードの判定
手札にジョーカーがある場合
・残り4枚中3枚の数字が同じ(フルハウスと言えなくもないが、フォーカードに吸収される)
または
・残り4枚中2枚の数字が同じ、更に残りの2枚の数字が同じ
場合、True。
手札にジョーカーがない場合
・5枚中3枚の数字が同じ、更に残りの2枚の数字が同じならばTrue。
"""
if hand[0]==JOKER: #手札にジョーカーがある場合
#3行ある判定条件のうち、上2行はフォーカードと同じ
return hand[1][1]==hand[2][1]==hand[3][1]\
or hand[2][1]==hand[3][1]==hand[4][1]\
or hand[1][1]==hand[2][1] and hand[3][1]==hand[4][1]
else: #手札にジョーカーがない場合
return hand[0][1]==hand[1][1]==hand[2][1] and hand[3][1]==hand[4][1]\
or hand[0][1]==hand[1][1] and hand[2][1]==hand[3][1]==hand[4][1]
ロイヤルストレートフラッシュ、ストレートフラッシュの場合もTrueを返却しますが、後の判定順序で重複カウントしないように制御します。
def isFlush(hand):
"""フラッシュの判定
・ジョーカーを除くカードが全て同じスートの場合、True。
"""
j=1 if hand[0]==JOKER else 0
for i in range(j+1,len(hand)):
if hand[i][0]!=hand[j][0]:
return False
return True
ロイヤルストレートフラッシュ、ストレートフラッシュの場合もTrueを返却しますが、後の判定順序で重複カウントしないように制御します。
def isRoyalStraight(hand):
"""ストレート(10-J-Q-K-A型)の判定
手札にジョーカーがある場合
・残り4枚の数字が(A,10,J,Q,K)の中の4個で構成されている
または
・残り4枚中、最小の数字以外の3枚と最小の数字との差が
(1,2,3),(1,2,4),(1,3,4),(2,3,4)でいずれかの
(または、4枚を数字で昇順ソートした時、隣り合う2枚の差が
(1,1,1),(1,1,2),(1,2,1),(2,1,1)のいずれかの)
場合、True。
手札にジョーカーがない場合
・5枚の数字が(A,10,J,Q,K)で構成されている
または
・i枚目と0枚目の数字の差がi(または、i枚目とi-1枚目の数字の差が1)
ならばTrue。
"""
if hand[0]==JOKER:
return tuple(hand[i][1] for i in range(1,len(hand))) in [(1,10,11,12),(1,10,11,13),(1,10,12,13),(1,11,12,13),(10,11,12,13)]
else:
if hand[0][1]!=1:
return False
for i in range(1,len(hand)):
if hand[i][1]!=9+i:
return False
return True
def isStraight(hand):
if isRoyalStraight(hand):
return True
if hand[0]==JOKER:
return tuple(hand[i][1]-hand[1][1] for i in range(2,len(hand))) in [(1,2,3),(1,2,4),(1,3,4),(2,3,4)]
else:
for i in range(1,len(hand)):
if hand[i][1]!=hand[0][1]+i:
return False
return True
ファイブカード、フォーカード、フルハウスの場合もTrueを返却しますが、後の判定順序で重複カウントしないように制御します。
def isThreeOfAKind(hand):
"""スリーカードの判定
手札にジョーカーがある場合
・残り4枚中2枚同じ数字があればTrue。
手札にジョーカーがない場合
・5枚中3枚同じ数字があればTrue。
"""
if hand[0]==JOKER:
#ジョーカー以外の4枚中2枚同じ数字があればスリーカード
return hand[1][1]==hand[2][1] or hand[2][1]==hand[3][1] or hand[3][1]==hand[4][1]
else:
#5枚中3枚同じ数字があればスリーカード
return hand[0][1]==hand[1][1]==hand[2][1] or hand[1][1]==hand[2][1]==hand[3][1] or hand[2][1]==hand[3][1]==hand[4][1]
ジョーカーがある場合、ツーペアが成立すれば上位のスリーカードも成立しますが、Trueを返却しておきます。後の判定順序で重複カウントしないように制御します。
def isTwoPair(hand):
"""ツーペアの判定
手札にジョーカーがある場合
・残り4枚中2枚同じ数字があればTrue。(スリーカードと同じ判定条件)
手札にジョーカーがない場合
・5枚中2枚同じ数字、残り3枚中2枚同じ数字ならばTrue。
"""
if hand[0]==JOKER:
#残り4枚中2枚同じ数字ならばツーペア(上位のスリーカードに吸収される)
return hand[1][1]==hand[2][1] or hand[2][1]==hand[3][1] or hand[3][1]==hand[4][1]
else:
#同じ数字の組が2組あればツーペア
return hand[0][1]==hand[1][1] and hand[2][1]==hand[3][1]\
or hand[0][1]==hand[1][1] and hand[3][1]==hand[4][1]\
or hand[1][1]==hand[2][1] and hand[3][1]==hand[4][1]
上位の役の条件で既にTrueになっている場合もTrueを返却しますが、後の判定順序で重複カウントしないように制御します。
def isOnePair(hand):
"""ワンペアの判定
手札にジョーカーがある場合
・無条件でTrue。
手札にジョーカーがない場合
・5枚中2枚同じ数字があればTrue。
"""
if hand[0]==JOKER:
return True
else:
for i in range(1,len(hand)):
if hand[i-1][1]==hand[i][1]:
#数字の昇順に並んでいる前提なので、
#隣り合う2枚の数字が同じならワンペア(以上)
return True
return False
def deal(hand, k):
"""カード配布&判定"""
global nPatterns
global cntFiveOfAKind
global cntRoyalStraightFlush
global cntStraightFlush
global cntFourOfAKind
global cntFullHouse
global cntFlush
global cntStraight
global cntThreeOfAKind
global cntTwoPair
global cntOnePair
global cntNoPair
if len(hand)<5:
#手札が5枚になるまで配布
for i in range(k,len(CARDS)):
hand.append(CARDS[i])
deal(hand,i+1)
hand.pop()
else:
#手札が数字の昇順になっていることを確認
for i in range(1,len(hand)):
assert(hand[i-1][1]<=hand[i][1])
#役判定(順番を間違えるとツーペアなのにワンペアと判定されたりします)
nPatterns+=1
if isFiveOfAKind(hand):
cntFiveOfAKind+=1
elif isRoyalStraightFlush(hand):
cntRoyalStraightFlush+=1
elif isStraightFlush(hand):
cntStraightFlush+=1
elif isFourOfAKind(hand):
cntFourOfAKind+=1
elif isFullHouse(hand):
cntFullHouse+=1
elif isFlush(hand):
cntFlush+=1
elif isStraight(hand):
cntStraight+=1
elif isThreeOfAKind(hand):
cntThreeOfAKind+=1
elif isTwoPair(hand):
cntTwoPair+=1
elif isOnePair(hand):
cntOnePair+=1
else:
cntNoPair+=1
def output(str, cnt):
"""役名(左寄せ21桁)、パターン数(右寄せ8桁)、確率(整数部3桁、小数部6桁)で出力
"""
print(f"{str:<21}:{cnt:>8}({100*cnt/nPatterns:10.6f}%)")
def
がたくさんあったので、if __name__=="__main__":
で始めさせて頂きました。
def main():
deal([],0)
output("Five of a Kind",cntFiveOfAKind)
output("Royal Straight Flush",cntRoyalStraightFlush)
output("Straight Flush",cntStraightFlush)
output("Four of a Kind",cntFourOfAKind)
output("Full House",cntFullHouse)
output("Flush",cntFlush)
output("Straight",cntStraight)
output("Three of a Kind",cntThreeOfAKind)
output("Two Pair",cntTwoPair)
output("One Pair",cntOnePair)
output("No Pair",cntNoPair)
print('-'*len(f"{str():<21}:{int():>8}({float():10.6f}%)"))
output("Total",nPatterns)
if __name__=="__main__":
main()
python poker.py
Five of a Kind : 13( 0.000453%)
Royal Straight Flush : 24( 0.000836%)
Straight Flush : 180( 0.006272%)
Four of a Kind : 3120( 0.108723%)
Full House : 6552( 0.228318%)
Flush : 7804( 0.271946%)
Straight : 20532( 0.715479%)
Three of a Kind : 137280( 4.783800%)
Two Pair : 123552( 4.305420%)
One Pair : 1268088( 44.189101%)
No Pair : 1302540( 45.389651%)
-------------------------------------------
Total : 2869685(100.000000%)
次回、別の視点から検算する予定です。