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