from collections import Counter from typing import List, Optional from poker.hand_evaluator import HandEvaluator from poker.card import Suit, Card, ShortDeckRank from .hand_ranking import ShortDeckHandType, ShortDeckHandRanking from itertools import combinations class ShortDeckHandEvaluator(HandEvaluator): @classmethod def evaluate_hand(cls, cards: List[Card]) -> ShortDeckHandRanking: if len(cards) < 5: raise ValueError("Need at least 5 cards to evaluate a hand") best_ranking = None for combo in combinations(cards, 5): ranking = cls.evaluate_5_cards(list(combo)) if best_ranking is None or ranking > best_ranking: best_ranking = ranking return best_ranking @classmethod def evaluate_5_cards(cls, cards: List[Card]) -> ShortDeckHandRanking: if len(cards) != 5: raise ValueError("Must provide exactly 5 cards") from poker.hand_evaluator import HandEvaluator sorted_cards, ranks, suits, rank_counts, count_values, is_flush, _, _ = HandEvaluator._analyze_cards(cards) # 重写顺子判断 is_straight, straight_high = cls.is_straight(ranks) if is_straight and is_flush: if straight_high == ShortDeckRank.ACE: return ShortDeckHandRanking(ShortDeckHandType.ROYAL_FLUSH, [straight_high], sorted_cards) else: return ShortDeckHandRanking(ShortDeckHandType.STRAIGHT_FLUSH, [straight_high], sorted_cards) if 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 ShortDeckHandRanking(ShortDeckHandType.FOUR_OF_A_KIND, [quad_rank, kicker], sorted_cards) # 关键差异:Flush > Full House if is_flush: return ShortDeckHandRanking(ShortDeckHandType.FLUSH, ranks, sorted_cards) if count_values == [3, 2]: # 满堂红 trip_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 ShortDeckHandRanking(ShortDeckHandType.FULL_HOUSE, [trip_rank, pair_rank], sorted_cards) if is_straight: return ShortDeckHandRanking(ShortDeckHandType.STRAIGHT, [straight_high], sorted_cards) if count_values == [3, 1, 1]: # 三条 trip_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], key=lambda x: x.numeric_value, reverse=True) return ShortDeckHandRanking(ShortDeckHandType.THREE_OF_A_KIND, [trip_rank] + kickers, sorted_cards) if count_values == [2, 2, 1]: # 两对 pairs = sorted([rank for rank, count in rank_counts.items() if count == 2], key=lambda x: x.numeric_value, reverse=True) kicker = [rank for rank, count in rank_counts.items() if count == 1][0] return ShortDeckHandRanking(ShortDeckHandType.TWO_PAIR, pairs + [kicker], sorted_cards) if 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], key=lambda x: x.numeric_value, reverse=True) return ShortDeckHandRanking(ShortDeckHandType.ONE_PAIR, [pair_rank] + kickers, sorted_cards) return ShortDeckHandRanking(ShortDeckHandType.HIGH_CARD, ranks, sorted_cards) @classmethod def is_straight(cls, ranks: List[ShortDeckRank]) -> tuple[bool, Optional[ShortDeckRank]]: unique_ranks = sorted(set(rank.numeric_value for rank in ranks)) if len(unique_ranks) < 5: return False, None ace_low_straight = [6, 7, 8, 9, 14] # A(14), 6, 7, 8, 9 if all(val in unique_ranks for val in ace_low_straight): return True, ShortDeckRank.NINE for i in range(len(unique_ranks) - 4): if unique_ranks[i+4] - unique_ranks[i] == 4: high_value = unique_ranks[i+4] high_rank = None for rank in ShortDeckRank: if rank.numeric_value == high_value: high_rank = rank break return True, high_rank return False, None @classmethod def evaluate_from_input(cls, input_str) -> ShortDeckHandRanking: cards = Card.parse_short_deck_cards(input_str) return cls.evaluate_hand(cards)