task4
This commit is contained in:
103
shortdeck/hand_evaluator.py
Normal file
103
shortdeck/hand_evaluator.py
Normal 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)
|
||||
Reference in New Issue
Block a user