326 lines
14 KiB
Python
326 lines
14 KiB
Python
"""
|
|
Tests for Short Deck Hand Evaluator
|
|
"""
|
|
|
|
import pytest
|
|
from poker.card import Card, ShortDeckRank, Suit # 使用统一的Card类
|
|
from shortdeck.hand_evaluator import ShortDeckHandEvaluator
|
|
from shortdeck.hand_ranking import ShortDeckHandType, ShortDeckHandRanking
|
|
|
|
|
|
class TestShortDeckHandEvaluator:
|
|
"""Test cases for ShortDeckHandEvaluator class"""
|
|
|
|
def test_evaluate_hand_insufficient_cards(self):
|
|
"""Test evaluation with less than 5 cards"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.HEARTS),
|
|
Card(ShortDeckRank.QUEEN, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.JACK, Suit.CLUBS)
|
|
]
|
|
|
|
with pytest.raises(ValueError, match="Need at least 5 cards"):
|
|
ShortDeckHandEvaluator.evaluate_hand(cards)
|
|
|
|
def test_evaluate_hand_exactly_five_cards(self):
|
|
"""Test evaluation with exactly 5 cards"""
|
|
# Royal flush
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.SPADES),
|
|
Card(ShortDeckRank.QUEEN, Suit.SPADES),
|
|
Card(ShortDeckRank.JACK, Suit.SPADES),
|
|
Card(ShortDeckRank.TEN, Suit.SPADES)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_hand(cards)
|
|
assert result.hand_type == ShortDeckHandType.ROYAL_FLUSH
|
|
|
|
def test_evaluate_hand_seven_cards(self):
|
|
"""Test evaluation with 7 cards - should find best 5"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.ACE, Suit.HEARTS),
|
|
Card(ShortDeckRank.ACE, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.ACE, Suit.CLUBS), # Four Aces
|
|
Card(ShortDeckRank.KING, Suit.SPADES),
|
|
Card(ShortDeckRank.SIX, Suit.HEARTS),
|
|
Card(ShortDeckRank.SEVEN, Suit.DIAMONDS)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_hand(cards)
|
|
assert result.hand_type == ShortDeckHandType.FOUR_OF_A_KIND
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
|
|
def test_evaluate_five_cards_wrong_count(self):
|
|
"""Test evaluate5Cards with wrong number of cards"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.HEARTS)
|
|
]
|
|
|
|
with pytest.raises(ValueError, match="Must provide exactly 5 cards"):
|
|
ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
|
|
def test_royal_flush(self):
|
|
"""Test royal flush detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.SPADES),
|
|
Card(ShortDeckRank.QUEEN, Suit.SPADES),
|
|
Card(ShortDeckRank.JACK, Suit.SPADES),
|
|
Card(ShortDeckRank.TEN, Suit.SPADES)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.ROYAL_FLUSH
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
|
|
def test_straight_flush(self):
|
|
"""Test straight flush detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.KING, Suit.HEARTS),
|
|
Card(ShortDeckRank.QUEEN, Suit.HEARTS),
|
|
Card(ShortDeckRank.JACK, Suit.HEARTS),
|
|
Card(ShortDeckRank.TEN, Suit.HEARTS),
|
|
Card(ShortDeckRank.NINE, Suit.HEARTS)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.STRAIGHT_FLUSH
|
|
assert result.key_ranks[0] == ShortDeckRank.KING
|
|
|
|
def test_four_of_a_kind(self):
|
|
"""Test four of a kind detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.ACE, Suit.HEARTS),
|
|
Card(ShortDeckRank.ACE, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.ACE, Suit.CLUBS),
|
|
Card(ShortDeckRank.KING, Suit.SPADES)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.FOUR_OF_A_KIND
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
assert result.key_ranks[1] == ShortDeckRank.KING # Kicker
|
|
|
|
def test_flush_beats_full_house(self):
|
|
"""Test that flush beats full house in short deck"""
|
|
# Flush
|
|
flush_cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.SPADES),
|
|
Card(ShortDeckRank.QUEEN, Suit.SPADES),
|
|
Card(ShortDeckRank.JACK, Suit.SPADES),
|
|
Card(ShortDeckRank.NINE, Suit.SPADES)
|
|
]
|
|
|
|
# Full house
|
|
full_house_cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.ACE, Suit.HEARTS),
|
|
Card(ShortDeckRank.ACE, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.KING, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.HEARTS)
|
|
]
|
|
|
|
flush_result = ShortDeckHandEvaluator.evaluate_5_cards(flush_cards)
|
|
full_house_result = ShortDeckHandEvaluator.evaluate_5_cards(full_house_cards)
|
|
|
|
assert flush_result.hand_type == ShortDeckHandType.FLUSH
|
|
assert full_house_result.hand_type == ShortDeckHandType.FULL_HOUSE
|
|
assert flush_result > full_house_result # Flush beats Full House in Short Deck!
|
|
|
|
def test_full_house(self):
|
|
"""Test full house detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.ACE, Suit.HEARTS),
|
|
Card(ShortDeckRank.ACE, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.KING, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.HEARTS)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.FULL_HOUSE
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE # Trips
|
|
assert result.key_ranks[1] == ShortDeckRank.KING # Pair
|
|
|
|
def test_flush(self):
|
|
"""Test flush detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.SPADES),
|
|
Card(ShortDeckRank.QUEEN, Suit.SPADES),
|
|
Card(ShortDeckRank.JACK, Suit.SPADES),
|
|
Card(ShortDeckRank.NINE, Suit.SPADES) # Not a straight
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.FLUSH
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
|
|
def test_straight_regular(self):
|
|
"""Test regular straight detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.HEARTS),
|
|
Card(ShortDeckRank.QUEEN, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.JACK, Suit.CLUBS),
|
|
Card(ShortDeckRank.TEN, Suit.SPADES)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.STRAIGHT
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
|
|
def test_straight_low_wheel(self):
|
|
"""Test short deck wheel straight: A-6-7-8-9"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.NINE, Suit.HEARTS),
|
|
Card(ShortDeckRank.EIGHT, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.SEVEN, Suit.CLUBS),
|
|
Card(ShortDeckRank.SIX, Suit.SPADES)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.STRAIGHT
|
|
assert result.key_ranks[0] == ShortDeckRank.NINE # 9-high straight in short deck wheel
|
|
|
|
def test_three_of_a_kind(self):
|
|
"""Test three of a kind detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.ACE, Suit.HEARTS),
|
|
Card(ShortDeckRank.ACE, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.KING, Suit.SPADES),
|
|
Card(ShortDeckRank.QUEEN, Suit.HEARTS)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.THREE_OF_A_KIND
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
assert ShortDeckRank.KING in result.key_ranks
|
|
assert ShortDeckRank.QUEEN in result.key_ranks
|
|
|
|
def test_two_pair(self):
|
|
"""Test two pair detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.ACE, Suit.HEARTS),
|
|
Card(ShortDeckRank.KING, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.KING, Suit.SPADES),
|
|
Card(ShortDeckRank.QUEEN, Suit.HEARTS)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.TWO_PAIR
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE # Higher pair first
|
|
assert result.key_ranks[1] == ShortDeckRank.KING # Lower pair second
|
|
assert result.key_ranks[2] == ShortDeckRank.QUEEN # Kicker
|
|
|
|
def test_one_pair(self):
|
|
"""Test one pair detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.ACE, Suit.HEARTS),
|
|
Card(ShortDeckRank.KING, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.QUEEN, Suit.SPADES),
|
|
Card(ShortDeckRank.JACK, Suit.HEARTS)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.ONE_PAIR
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
assert ShortDeckRank.KING in result.key_ranks
|
|
assert ShortDeckRank.QUEEN in result.key_ranks
|
|
assert ShortDeckRank.JACK in result.key_ranks
|
|
|
|
def test_high_card(self):
|
|
"""Test high card detection"""
|
|
cards = [
|
|
Card(ShortDeckRank.ACE, Suit.SPADES),
|
|
Card(ShortDeckRank.KING, Suit.HEARTS),
|
|
Card(ShortDeckRank.QUEEN, Suit.DIAMONDS),
|
|
Card(ShortDeckRank.JACK, Suit.CLUBS),
|
|
Card(ShortDeckRank.NINE, Suit.HEARTS) # Not a straight (missing 10)
|
|
]
|
|
|
|
result = ShortDeckHandEvaluator.evaluate_5_cards(cards)
|
|
assert result.hand_type == ShortDeckHandType.HIGH_CARD
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
|
|
def test_is_straight_regular_straights(self):
|
|
"""Test isStraight with regular straights"""
|
|
# T-J-Q-K-A
|
|
ranks1 = [ShortDeckRank.ACE, ShortDeckRank.KING, ShortDeckRank.QUEEN, ShortDeckRank.JACK, ShortDeckRank.TEN]
|
|
is_straight1, high1 = ShortDeckHandEvaluator.is_straight(ranks1)
|
|
assert is_straight1 == True
|
|
assert high1 == ShortDeckRank.ACE
|
|
|
|
# 6-7-8-9-T
|
|
ranks2 = [ShortDeckRank.TEN, ShortDeckRank.NINE, ShortDeckRank.EIGHT, ShortDeckRank.SEVEN, ShortDeckRank.SIX]
|
|
is_straight2, high2 = ShortDeckHandEvaluator.is_straight(ranks2)
|
|
assert is_straight2 == True
|
|
assert high2 == ShortDeckRank.TEN
|
|
|
|
def test_is_straight_wheel(self):
|
|
"""Test isStraight with short deck wheel: A-6-7-8-9"""
|
|
ranks = [ShortDeckRank.ACE, ShortDeckRank.NINE, ShortDeckRank.EIGHT, ShortDeckRank.SEVEN, ShortDeckRank.SIX]
|
|
is_straight, high = ShortDeckHandEvaluator.is_straight(ranks)
|
|
assert is_straight == True
|
|
assert high == ShortDeckRank.NINE # 9-high straight in short deck wheel
|
|
|
|
def test_is_straight_not_straight(self):
|
|
"""Test isStraight with non-straight"""
|
|
ranks = [ShortDeckRank.ACE, ShortDeckRank.KING, ShortDeckRank.QUEEN, ShortDeckRank.JACK, ShortDeckRank.NINE]
|
|
is_straight, high = ShortDeckHandEvaluator.is_straight(ranks)
|
|
assert is_straight == False
|
|
assert high is None
|
|
|
|
def test_is_straight_insufficient_cards(self):
|
|
"""Test isStraight with less than 5 unique ranks"""
|
|
ranks = [ShortDeckRank.ACE, ShortDeckRank.ACE, ShortDeckRank.KING, ShortDeckRank.KING] # Only 2 unique
|
|
is_straight, high = ShortDeckHandEvaluator.is_straight(ranks)
|
|
assert is_straight == False
|
|
assert high is None
|
|
|
|
def test_evaluate_from_input_valid(self):
|
|
"""Test evaluateFromInput with valid input"""
|
|
# Four of a kind
|
|
result = ShortDeckHandEvaluator.evaluate_from_input("AsAhAdAc Ks")
|
|
assert result.hand_type == ShortDeckHandType.FOUR_OF_A_KIND
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
|
|
def test_evaluate_from_input_seven_cards(self):
|
|
"""Test evaluateFromInput with 7 cards"""
|
|
result = ShortDeckHandEvaluator.evaluate_from_input("AsAhAdAc KsQsJs")
|
|
assert result.hand_type == ShortDeckHandType.FOUR_OF_A_KIND
|
|
assert result.key_ranks[0] == ShortDeckRank.ACE
|
|
|
|
def test_evaluate_from_input_invalid_cards(self):
|
|
"""Test evaluateFromInput with invalid cards (should reject 2,3,4,5)"""
|
|
with pytest.raises(ValueError, match="Invalid rank for short deck"):
|
|
ShortDeckHandEvaluator.evaluate_from_input("2s3s4s5s6s")
|
|
|
|
def test_all_possible_straights(self):
|
|
"""Test all possible straights in short deck"""
|
|
# All possible short deck straights:
|
|
# A-6-7-8-9, 6-7-8-9-T, 7-8-9-T-J, 8-9-T-J-Q, 9-T-J-Q-K, T-J-Q-K-A
|
|
|
|
straight_combinations = [
|
|
([ShortDeckRank.ACE, ShortDeckRank.NINE, ShortDeckRank.EIGHT, ShortDeckRank.SEVEN, ShortDeckRank.SIX], ShortDeckRank.NINE),
|
|
([ShortDeckRank.TEN, ShortDeckRank.NINE, ShortDeckRank.EIGHT, ShortDeckRank.SEVEN, ShortDeckRank.SIX], ShortDeckRank.TEN),
|
|
([ShortDeckRank.JACK, ShortDeckRank.TEN, ShortDeckRank.NINE, ShortDeckRank.EIGHT, ShortDeckRank.SEVEN], ShortDeckRank.JACK),
|
|
([ShortDeckRank.QUEEN, ShortDeckRank.JACK, ShortDeckRank.TEN, ShortDeckRank.NINE, ShortDeckRank.EIGHT], ShortDeckRank.QUEEN),
|
|
([ShortDeckRank.KING, ShortDeckRank.QUEEN, ShortDeckRank.JACK, ShortDeckRank.TEN, ShortDeckRank.NINE], ShortDeckRank.KING),
|
|
([ShortDeckRank.ACE, ShortDeckRank.KING, ShortDeckRank.QUEEN, ShortDeckRank.JACK, ShortDeckRank.TEN], ShortDeckRank.ACE),
|
|
]
|
|
|
|
for ranks, expected_high in straight_combinations:
|
|
is_straight, high = ShortDeckHandEvaluator.is_straight(ranks)
|
|
assert is_straight == True, f"Failed to detect straight: {[r.symbol for r in ranks]}"
|
|
assert high == expected_high, f"Wrong high card for {[r.symbol for r in ranks]}: expected {expected_high.symbol}, got {high.symbol if high else None}" |