Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@yopya

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

More than 3 years have passed since last update.

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

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
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
yopya
自然に囲まれた田舎で働きたい。 田舎でPythonの仕事ないっすか?

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?