task4
This commit is contained in:
10
shortdeck/__init__.py
Normal file
10
shortdeck/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from poker.card import ShortDeckRank
|
||||
from .hand_evaluator import ShortDeckHandEvaluator
|
||||
from .hand_ranking import ShortDeckHandType, ShortDeckHandRanking
|
||||
|
||||
__all__ = [
|
||||
'ShortDeckRank',
|
||||
'ShortDeckHandEvaluator',
|
||||
'ShortDeckHandType',
|
||||
'ShortDeckHandRanking'
|
||||
]
|
||||
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)
|
||||
52
shortdeck/hand_ranking.py
Normal file
52
shortdeck/hand_ranking.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from functools import total_ordering
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
from poker.hand_ranking import HandType, HandRanking
|
||||
from poker.card import ShortDeckRank, Card
|
||||
|
||||
|
||||
@total_ordering
|
||||
class ShortDeckHandType(Enum):
|
||||
"""
|
||||
标准牌型扑克与短牌型扑克的区别:
|
||||
- 同花(Flush) > 满堂红(Full House)
|
||||
- 其他牌型等级不变
|
||||
"""
|
||||
HIGH_CARD = (1, "High Card")
|
||||
ONE_PAIR = (2, "One Pair")
|
||||
TWO_PAIR = (3, "Two Pair")
|
||||
THREE_OF_A_KIND = (4, "Three of a Kind")
|
||||
STRAIGHT = (5, "Straight")
|
||||
FULL_HOUSE = (6, "Full House")
|
||||
FLUSH = (7, "Flush")
|
||||
FOUR_OF_A_KIND = (8, "Four of a Kind")
|
||||
STRAIGHT_FLUSH = (9, "Straight Flush")
|
||||
ROYAL_FLUSH = (10, "Royal Flush")
|
||||
|
||||
def __new__(cls, strength, type_name):
|
||||
obj = object.__new__(cls)
|
||||
obj._value_ = strength
|
||||
obj.strength = strength
|
||||
obj.type_name = type_name
|
||||
return obj
|
||||
|
||||
def __str__(self):
|
||||
return self.type_name
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, ShortDeckHandType):
|
||||
return self.strength == other.strength
|
||||
return NotImplemented
|
||||
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, ShortDeckHandType):
|
||||
return self.strength < other.strength
|
||||
return NotImplemented
|
||||
|
||||
|
||||
class ShortDeckHandRanking(HandRanking):
|
||||
def __init__(self, hand_type: ShortDeckHandType, key_ranks: List[ShortDeckRank], cards: List[Card]):
|
||||
super().__init__(hand_type, key_ranks, cards)
|
||||
|
||||
def __str__(self):
|
||||
return super().__str__()
|
||||
Reference in New Issue
Block a user