LoginSignup
1
1

More than 5 years have passed since last update.

Project Euler 54「ポーカーハンド」

Last updated at Posted at 2018-03-16

一旦書き上げて、後から綺麗で簡潔なコードに修正しようと思っていた時期が僕にもありました。

Problem 54 「ポーカーハンド」

カードゲームのポーカーでは, 手札は5枚のカードからなりランク付けされている. 役を低い方から高い方へ順に並べると以下である.

役無し(ハイカード): 一番値が大きいカード
ワン・ペア: 同じ値のカードが2枚
ツー・ペア: 2つの異なる値のペア
スリーカード: 同じ値のカードが3枚
ストレート: 5枚の連続する値のカード
フラッシュ: 全てのカードが同じスート (注: スートとはダイヤ・ハート・クラブ/スペードというカードの絵柄のこと)
フルハウス: スリーカードとペア
フォーカード: 同じ値のカードが4枚
ストレートフラッシュ: ストレートかつフラッシュ
ロイヤルフラッシュ: 同じスートの10, J, Q, K, A
ここでカードの値は小さい方から2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, Aである. (訳注:データ中で10は'T'と表される)

もし2人のプレイヤーが同じ役の場合には, 役を構成する中で値が最も大きいカードによってランクが決まる: 例えば, 8のペアは5のペアより強い (下の例1を見よ). それでも同じランクの場合には (例えば, 両者ともQのペアの場合), 一番値が大きいカードによってランクが決まる (下の例4を見よ). 一番値が大きいカードが同じ場合には, 次に値が大きいカードが比べれられ, 以下同様にランクを決定する.

例:

試合 プレイヤー1 プレイヤー2 勝者
1 5H 5C 6S 7S KD
5のペア
2C 3S 8S 8D TD
8のペア
プレイヤー2
2 5D 8C 9S JS AC
役無し, A
2C 5C 7D 8S QH
役無し, Q
プレイヤー1
3 2D 9C AS AH AC
Aのスリーカード
3D 6D 7D TD QD
ダイヤのフラッシュ
プレイヤー2
4 4D 6S 9H QH QC
Qのペア, 9
3D 6D 7H QD QS
Qのペア, 7
プレイヤー1
5 2H 2D 4C 4D 4S
4-2のフルハウス
3C 3D 3S 9S 9D
3-9のフルハウス
プレイヤー1

poker.txt には1000個のランダムな手札の組が含まれている. 各行は10枚のカードからなる (スペースで区切られている): 最初の5枚がプレイヤー1の手札であり, 残りの5枚がプレイヤー2の手札である. 以下のことを仮定してよい

全ての手札は正しい (使われない文字が出現しない. 同じカードは繰り返されない)
各プレイヤーの手札は特に決まった順に並んでいるわけではない
各勝負で勝敗は必ず決まる
1000回中プレイヤー1が勝つのは何回か? (訳注 : この問題に置いてA 2 3 4 5というストレートは考えなくてもよい)

from collections import Counter

def hoge(path):
    def duel(cards):
        P1, P2 = calc(cards[:5]), calc(cards[5:])
        p1, p2 = next(P1), next(P2)
        while p1 == p2:
            p1, p2 = next(P1), next(P2)
        return p1 > p2
#
    def calc(hand):
        n = Counter([ card_score[x[0]] for x in hand ])
        nk, nv = n.keys(), n.values()
        s = set([ x[1] for x in hand ])
        f = lambda d, x: max([ k for k, v in d.items() if v == x ])
        # Royal Flush
        if sorted(nk) == list(range(10, 15)) and len(s) == 1:
            yield 10**10
        # Straight flush
        if sorted(nk) == list(range(min(nk), min(nk)+5)) and len(s) == 1:
            yield 10**9 + max(nk)
        # Flush
        if len(s) == 1:
            yield 10**6 + max(nk)
        # Straight
        if sorted(nk) == list(range(min(nk), min(nk)+5)):
            yield 10**5 + max(nk)
        while n:
            # Four of a kind
            if 4 in nv:
                yield 10**8 + pop_and_return_key(n, f(n, 4))
            # Full house
            if sorted(nv) == [2, 3]:
                yield 10**7 + pop_and_return_key(n, f(n, 3))
            # Three of a kind
            if 3 in nv:
                yield 10**4 + pop_and_return_key(n, f(n, 3))
            # Two pair  
            if sorted(nv) == [1, 2, 2]:
                yield 10**3 + pop_and_return_key(n, f(n, 2))
            # One pair
            if 2 in nv:
                yield 10**2 + pop_and_return_key(n, f(n, 2))
            # High card
            yield pop_and_return_key(n, f(n, 1))
#
    def pop_and_return_key(d, k):
        d.pop(k)
        return k
#
    card_score = { x:i for i, x in enumerate(list('23456789TJQKA'), 2) }
    data = open(path).read().strip().split('\n')
    return sum( duel(d.split()) for d in data )

print(hoge('poker.txt'))
1
1
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
1
1