- 本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。
問題 54. ポーカーの手
問題の要約:ファイルのポーカーの対戦結果を見てプレーヤー1の勝った数を求めよ
ファイルに5H5C6S7SKD 2C3S8S8DTDのようにポーカーの対戦結果が1000組書かれているので以下のように勝敗判定を行うという問題です。
Hand | Player 1 | Player 2 | Winner |
---|---|---|---|
1 | 5H 5C 6S 7S KD Pair of Fives |
2C 3S 8S 8D TD Pair of Eights |
Player 2 |
2 | 5D 8C 9S JS AC Highest card Ace |
2C 5C 7D 8S QH Highest card Queen |
Player 1 |
3 | 2D 9C AS AH AC Three Aces |
3D 6D 7D TD QD Flush with Diamonds |
Player 2 |
4 | 4D 6S 9H QH QC Pair of Queens Highest card Nine |
3D 6D 7H QD QS Pair of Queens Highest card Seven |
Player 1 |
5 | 2H 2D 4C 4D 4S Full House Three Fours |
3C 3D 3S 9S 9D Full House with Three Threes |
Player 1 |
ファイルから読む部分は後の方に載せましたので、ここでは以下のように配列に既に格納されているといると考えます。
c5 = ['5C', 'QC', 'QH', 'AS', 'TS']
役の判定の手順
ここから役の判定に必要な情報を取り出します。まず数字を取り出して整数に変換します。エースは一番強いので14にします。
['5', 'Q', 'Q', 'A', 'T'] --> npart = [5, 12, 12, 14, 10]
次にCounter関数のmost_common()メソッドを使って頻度の順番に並び替えます。12が2つあることが分かります。この頻度と数字の部分を分けてnkとnnとします。このnkで役の判定nnで役が同じとき数字の判定を行うという感じです。マークも同様にします。
nmc = Counter(npart).most_common()
# [(12, 2), (5, 1), (14, 1), (10, 1)]
nk = [k for (n,k) in nmc]
# [2, 1, 1, 1] --> One pair
nn = [n for (n,k) in nmc]
# [12, 5, 14, 10]
メインの3つの関数
- cardAttr : 判定に必要な情報を取り出し
- isStraight : ストレートの判定
- handCheck : 役を判定して[役番号, 判定用数字の優先度順の列]を返します(例 [1, 12, 14, 10, 5], 1="One pair")この大小比較で勝敗判定が行えます。
from collections import Counter
import numpy as np
NO = ["?","?","2","3","4","5","6","7","8","9","T","J","Q","K","A"]
def cardAttr(c5):
npart = list(map(NO.index,[s[0] for s in c5] )) # Number part
nmc = Counter(npart).most_common() # most common
spart = [s[1] for s in c5] # Suit part
return npart, [k for (n,k) in nmc], [n for (n,k) in nmc], [k for (s,k) in Counter(spart).most_common()] # Suit part
def isStraight(snpart): # if straight, return max number, else return 0
if (list(np.diff(snpart)).count(1))==4:
return snpart[-1] # return max number
return 0
def handCheck(c5):
npart, nk, nn, sk = cardAttr(c5) # Card attributes
stmax = isStraight(sorted(npart)) # if straight, stmax: max number
if stmax > 0 and sk == [5]: # Straight Flush
if stmax == 14: return [9]+[stmax] # Royal Flush
else: [8]+[stmax] # Straight Flush
if nk == [4, 1]: return [7]+nn # Four of a Kind
if nk == [3, 2]: return [6]+nn # Full House
if sk == [5]: return [5]+sorted(npart,reverse=True) # Flush
if stmax > 0: return [4]+[stmax] # Straight
if nk == [3, 1, 1]: return [3]+[nn[0]]+sorted(nn[1:], reverse=True) # Three of a Kind
if nk == [2, 2, 1]: return [2]+sorted(nn[:2], reverse=True)+[nn[2]] # Two Pairs
if nk == [2, 1, 1, 1]: return[1]+[nn[0]]+sorted(nn[1:], reverse=True) # One Pair
return [0]+sorted(npart,reverse=True) # High Card
すべての結果の勝敗判定
handCheckの出力の配列はそのまま比較すれば勝敗判定が出来るのでプレーヤー1の勝ちの数を数えます。
print(f"Answer : {sum([handCheck(c[0])>handCheck(c[1]) for c in cards ])}")
ファイから読みだして配列に格納するコード
# show upload dialog
from google.colab import files
uploaded = files.upload()
#---- read words from file to name[]
f = open("p054_poker.txt")
handslist = f.readlines()
f.close()
count, cards = 0, []
for hands in handslist:
h12 = hands.split()
cards.append([h12[:5],h12[5:]])
print(cards[0])
(開発環境:Google Colab)