This commit is contained in:
2025-09-24 16:54:52 +08:00
parent 45f86ba95e
commit 57a7e9216e
22 changed files with 1647 additions and 102 deletions

103
shortdeck/hand_evaluator.py Normal file
View File

@@ -0,0 +1,103 @@
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)