Files
SolutionEuler/solutions/0054.PokerHands/euler_54_better.py

102 lines
2.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import time
from collections import Counter
from pathlib import Path
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"{func.__name__} time: {elapsed_time:.6f} seconds")
return result
return wrapper
# 牌面数值映射
VAL = {
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"T": 10,
"J": 11,
"Q": 12,
"K": 13,
"A": 14,
}
def hand_key(cards: tuple[str, ...]) -> tuple[int, ...]:
"""
将一手5张牌映射为规范元组。
元组的字典序即牌力大小,可直接用 > / < 比较。
"""
# 1. 提取数值并降序排列
nums = sorted([VAL[c[0]] for c in cards], reverse=True)
suits = {c[1] for c in cards}
# 2. 判断同花与顺子
is_flush = len(suits) == 1
is_straight = len(set(nums)) == 5 and nums[0] - nums[4] == 4
# 3. 处理 A-2-3-4-5 顺子A作为15-high
if nums == [14, 5, 4, 3, 2]:
is_straight = True
nums = [5, 4, 3, 2, 1]
# 4. 统计出现次数,并按 (次数, 牌面) 降序排列
# 这是整个方法的数学核心:多重集的字典序
counts = Counter(nums)
by_count = sorted(counts.items(), key=lambda x: (x[1], x[0]), reverse=True)
# 5. 按牌型返回规范元组
if is_flush and is_straight:
return (9, nums[0]) # 同花顺皇家同花顺只是顶牌为A
if by_count[0][1] == 4:
return (8, by_count[0][0], by_count[1][0]) # 四条
if by_count[0][1] == 3 and len(by_count) == 2:
return (7, by_count[0][0], by_count[1][0]) # 葫芦
if is_flush:
return (6, *nums) # 同花比全部5张降序
if is_straight:
return (5, nums[0]) # 顺子:比顶牌
if by_count[0][1] == 3:
return (4, by_count[0][0], by_count[1][0], by_count[2][0]) # 三条
if by_count[0][1] == 2 and len(by_count) == 3:
return (3, by_count[0][0], by_count[1][0], by_count[2][0]) # 两对
if by_count[0][1] == 2:
return (
2,
by_count[0][0],
by_count[1][0],
by_count[2][0],
by_count[3][0],
) # 一对
return (1, *nums) # 高牌比全部5张降序
def read_hands(path: Path):
with open(path) as f:
for line in f:
cards = line.strip().split()
if len(cards) == 10:
yield tuple(cards[:5]), tuple(cards[5:])
@timer
def main() -> int:
data_path = Path(__file__).parent / "0054_poker.txt"
wins = sum(1 for p1, p2 in read_hands(data_path) if hand_key(p1) > hand_key(p2))
return wins
if __name__ == "__main__":
print(main())