0
0

More than 1 year has passed since last update.

【Project Euler】Problem 54: ポーカーの手

Posted at
  • 本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。

問題 54. ポーカーの手

原文 Problem 54: Poker hands

問題の要約:ファイルのポーカーの対戦結果を見てプレーヤー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つあることが分かります。この頻度と数字の部分を分けてnknnとします。この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)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0