Files
poker_task1/poker/hand_evaluator.py
2025-09-23 09:59:55 +08:00

140 lines
5.4 KiB
Python
Raw 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.
"""
Hand evaluator module for poker game
"""
from typing import List, Tuple, Dict
from collections import Counter
from itertools import combinations
from .card import Card, Rank, Suit
from .hand_ranking import HandRanking, HandType
class HandEvaluator:
"""
手牌评估器类,用于评估扑克手牌
"""
@staticmethod
def evaluate_hand(cards: List[Card]) -> HandRanking:
"""
从7张牌中评估出最好的5张牌组合
"""
if len(cards) != 7:
raise ValueError(f"Expected 7 cards, got {len(cards)}")
best_ranking = None
best_cards = None
# 尝试所有可能的5张牌组合
for five_cards in combinations(cards, 5):
ranking = HandEvaluator._evaluate_five_cards(list(five_cards))
if best_ranking is None or ranking > best_ranking:
best_ranking = ranking
best_cards = list(five_cards)
# 更新最佳ranking的cards
best_ranking.cards = best_cards
return best_ranking
@staticmethod
def _evaluate_five_cards(cards: List[Card]) -> HandRanking:
"""
评估5张牌的手牌类型
"""
if len(cards) != 5:
raise ValueError(f"Expected 5 cards, got {len(cards)}")
# 按点数排序(降序)
sorted_cards = sorted(cards, key=lambda c: c.rank.numeric_value, reverse=True)
ranks = [card.rank for card in sorted_cards]
suits = [card.suit for card in sorted_cards]
# 统计点数出现次数
rank_counts = Counter(ranks)
count_values = sorted(rank_counts.values(), reverse=True)
# 检查是否是同花
is_flush = len(set(suits)) == 1
# 检查是否是顺子
is_straight, straight_high = HandEvaluator._is_straight(ranks)
# 根据牌型返回相应的HandRanking
if is_straight and is_flush:
if straight_high == Rank.ACE and ranks == [Rank.ACE, Rank.KING, Rank.QUEEN, Rank.JACK, Rank.TEN]:
return HandRanking(HandType.ROYAL_FLUSH, [Rank.ACE], sorted_cards)
else:
return HandRanking(HandType.STRAIGHT_FLUSH, [straight_high], sorted_cards)
elif count_values == [4, 1]: # 四条
quad_rank = [rank for rank, count in rank_counts.items() if count == 4][0]
kicker = [rank for rank, count in rank_counts.items() if count == 1][0]
return HandRanking(HandType.FOUR_OF_A_KIND, [quad_rank, kicker], sorted_cards)
elif count_values == [3, 2]: # 三条加一对
trips_rank = [rank for rank, count in rank_counts.items() if count == 3][0]
pair_rank = [rank for rank, count in rank_counts.items() if count == 2][0]
return HandRanking(HandType.FULL_HOUSE, [trips_rank, pair_rank], sorted_cards)
elif is_flush: # 同花
return HandRanking(HandType.FLUSH, ranks, sorted_cards)
elif is_straight: # 顺子
return HandRanking(HandType.STRAIGHT, [straight_high], sorted_cards)
elif count_values == [3, 1, 1]: # 三条
trips_rank = [rank for rank, count in rank_counts.items() if count == 3][0]
kickers = sorted([rank for rank, count in rank_counts.items() if count == 1], reverse=True)
return HandRanking(HandType.THREE_OF_A_KIND, [trips_rank] + kickers, sorted_cards)
elif count_values == [2, 2, 1]: # 两对
pairs = sorted([rank for rank, count in rank_counts.items() if count == 2], reverse=True)
kicker = [rank for rank, count in rank_counts.items() if count == 1][0]
return HandRanking(HandType.TWO_PAIR, pairs + [kicker], sorted_cards)
elif count_values == [2, 1, 1, 1]: # 一对
pair_rank = [rank for rank, count in rank_counts.items() if count == 2][0]
kickers = sorted([rank for rank, count in rank_counts.items() if count == 1], reverse=True)
return HandRanking(HandType.ONE_PAIR, [pair_rank] + kickers, sorted_cards)
else: # 高牌
return HandRanking(HandType.HIGH_CARD, ranks, sorted_cards)
@staticmethod
def _is_straight(ranks: List[Rank]) -> Tuple[bool, Rank]:
"""
检查是否是顺子,返回(是否是顺子, 最高牌)
"""
# 排序点数值
values = sorted([rank.numeric_value for rank in ranks], reverse=True)
# 检查常规顺子
is_regular_straight = True
for i in range(1, len(values)):
if values[i-1] - values[i] != 1:
is_regular_straight = False
break
if is_regular_straight:
# 返回最高牌
highest_rank = None
for rank in ranks:
if rank.numeric_value == values[0]:
highest_rank = rank
break
return True, highest_rank
# 检查A-2-3-4-5的特殊顺子轮子
if values == [14, 5, 4, 3, 2]: # A, 5, 4, 3, 2
return True, Rank.FIVE # 在轮子中5是最高牌
return False, None
@staticmethod
def evaluate_from_input(cards_str: str) -> HandRanking:
"""
从字符串评估手牌,例如 "AsKs AhAdAc6s7s"
"""
cards = Card.parse_cards(cards_str)
return HandEvaluator.evaluate_hand(cards)