feat: Construct ranked hand with description

Refactors rank test functions to retern details required for
description, then return RankedHand from rank_hand function.
This commit is contained in:
Paul Harrison 2022-11-20 13:39:08 +00:00
parent d924e39608
commit b4ba284dcc
5 changed files with 231 additions and 154 deletions

View File

@ -0,0 +1,3 @@
from poker.rank.hands import rank_hand
__all__ = ["rank_hand"]

View File

@ -0,0 +1,14 @@
from poker.constants import Rank
DESCRIPTIONS = {
Rank.ROYAL_FLUSH: "royal flush: {suit}",
Rank.STRAIGHT_FLUSH: "straight flush: {high}-high {suit}",
Rank.FOUR_OF_A_KIND: "four of a kind: {value}",
Rank.FULL_HOUSE: "full house: {trips} over {pair}",
Rank.FLUSH: "flush: {suit}",
Rank.STRAIGHT: "straight: {high}-high",
Rank.THREE_OF_A_KIND: "three of a kind: {value}",
Rank.TWO_PAIR: "two pair: {high} and {low}",
Rank.PAIR: "pair: {value}",
Rank.HIGH_CARD: "high card: {value}",
}

View File

@ -1,5 +1,8 @@
from typing import Optional, Union
from poker.constants import Rank, Value from poker.constants import Rank, Value
from poker.models import Card, Hand from poker.models import Card, Hand, RankedHand
from poker.rank.descriptions import DESCRIPTIONS
def _is_flush(cards: list[Card]) -> bool: def _is_flush(cards: list[Card]) -> bool:
@ -7,104 +10,165 @@ def _is_flush(cards: list[Card]) -> bool:
return len(set(card.suit for card in cards)) == 1 return len(set(card.suit for card in cards)) == 1
def _is_straight(cards: list[Card]) -> bool: def _is_straight(cards: list[Card]) -> Optional[Value]:
"""Calculate whether hand is a straight.""" """Calculate whether hand is a straight.
Arguments:
cards: List of cards in hand.
Returns:
High card if hand is a straight, None otherwise.
"""
card_values = sorted([card.value for card in cards]) card_values = sorted([card.value for card in cards])
# If card values are not unique it is not a straight # If card values are not unique it is not a straight
if len(set(card_values)) != len(card_values): if len(set(card_values)) != len(card_values):
return False return None
# Check for a ace low straight # Check for a ace low straight
if card_values == [Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.ACE]: if card_values == [Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.ACE]:
return True return Value.FIVE
# If all card differences are 1 it is a straight # If all card differences are 1 it is a straight
diffs = [second - first for first, second in zip(card_values[:-1], card_values[1:])] diffs = [second - first for first, second in zip(card_values[:-1], card_values[1:])]
if set(diffs) == {1}: if set(diffs) == {1}:
return True return max(cards).value
return False return None
def is_royal_flush(hand: Hand) -> bool: def royal_flush(hand: Hand) -> dict[str, Union[str, int]]:
"""Calculate whether hand a royal flush.""" """Calculate whether hand a royal flush."""
if not _is_flush(hand.cards): if not _is_flush(hand.cards):
return False return {}
values = [card.value for card in hand.cards] values = [card.value for card in hand.cards]
return all( _is = all(
value in values value in values
for value in [Value.ACE, Value.KING, Value.QUEEN, Value.JACK, Value.TEN] for value in [Value.ACE, Value.KING, Value.QUEEN, Value.JACK, Value.TEN]
) )
if _is:
return {"suit": hand.cards[0].suit}
return {}
def is_straight_flush(hand: Hand) -> bool: def straight_flush(hand: Hand) -> dict[str, Union[str, int]]:
"""Calculate whether hand a straight flush.""" """Calculate whether hand a straight flush."""
return _is_straight(hand.cards) and _is_flush(hand.cards) high = _is_straight(hand.cards)
if high is not None and _is_flush(hand.cards):
return {
"high": high,
"suit": hand.cards[0].suit,
}
return {}
def is_four_of_a_kind(hand: Hand) -> bool: def four_of_a_kind(hand: Hand) -> dict[str, Union[str, int]]:
"""Calculate whether hand has four of a kind.""" """Calculate whether hand has four of a kind."""
return hand.value_counts[0][1] == 4 if hand.value_counts[0][1] == 4:
return {"value": hand.value_counts[0][0]}
return {}
def is_full_house(hand: Hand) -> bool: def full_house(hand: Hand) -> dict[str, Union[str, int]]:
"""Calculate whether hand is a full house.""" """Calculate whether hand is a full house."""
return hand.value_counts[0][1] == 3 and hand.value_counts[1][1] == 2 if hand.value_counts[0][1] == 3 and hand.value_counts[1][1] == 2:
return {
"trips": hand.value_counts[0][0],
"pair": hand.value_counts[1][0],
}
return {}
def is_flush(hand: Hand) -> bool: def flush(hand: Hand) -> dict[str, Union[str, int]]:
"""Calculate whether hand is a flush.""" """Calculate whether hand is a flush."""
return _is_flush(hand.cards) and not _is_straight(hand.cards) high = _is_straight(hand.cards)
if _is_flush(hand.cards) and high is None:
return {"suit": hand.cards[0].suit}
return {}
def is_straight(hand: Hand) -> bool: def straight(hand: Hand) -> dict[str, Union[str, int]]:
"""Calculate whether hand is a flush.""" """Calculate whether hand is a flush."""
return _is_straight(hand.cards) and not _is_flush(hand.cards) high = _is_straight(hand.cards)
if high is not None and not _is_flush(hand.cards):
return {"high": high}
return {}
def is_three_of_a_kind(hand: Hand) -> bool: def three_of_a_kind(hand: Hand) -> dict[str, Union[str, int]]:
"""Calculate whether hand is a full house.""" """Calculate whether hand is a full house."""
return ( if (
hand.value_counts[0][1] == 3 hand.value_counts[0][1] == 3
and hand.value_counts[1][1] == 1 and hand.value_counts[1][1] == 1
and hand.value_counts[2][1] == 1 and hand.value_counts[2][1] == 1
) ):
return {"value": hand.value_counts[0][0]}
return {}
def is_two_pair(hand: Hand) -> bool: def two_pair(hand: Hand) -> dict[str, Union[str, int]]:
"""Calculate whether hand is a two pair.""" """Calculate whether hand is a two pair."""
return hand.value_counts[0][1] == 2 and hand.value_counts[1][1] == 2 if hand.value_counts[0][1] == 2 and hand.value_counts[1][1] == 2:
values = [hand.value_counts[0][0], hand.value_counts[1][0]]
return {
"high": max(values),
"low": min(values),
}
return {}
def is_pair(hand: Hand) -> bool: def pair(hand: Hand) -> dict[str, Union[str, int]]:
"""Calculate whether hand is a pair.""" """Calculate whether hand is a pair."""
return hand.value_counts[0][1] == 2 and hand.value_counts[1][1] == 1 if hand.value_counts[0][1] == 2 and hand.value_counts[1][1] == 1:
return {"value": hand.value_counts[0][0]}
return {}
def get_rank(hand: Hand) -> Rank: def high_card(hand: Hand) -> dict[str, Union[str, int]]:
"""Get high card."""
return {"value": max(hand.cards).value}
_FUNCTIONS = {
Rank.ROYAL_FLUSH: royal_flush,
Rank.STRAIGHT_FLUSH: straight_flush,
Rank.FOUR_OF_A_KIND: four_of_a_kind,
Rank.FULL_HOUSE: full_house,
Rank.FLUSH: flush,
Rank.STRAIGHT: straight,
Rank.THREE_OF_A_KIND: three_of_a_kind,
Rank.TWO_PAIR: two_pair,
Rank.PAIR: pair,
Rank.HIGH_CARD: high_card,
}
def rank_hand(hand: Hand) -> RankedHand:
"""Get hand rank. """Get hand rank.
TODO: Use a factory pattern to avoid this huge flow control. TODO: Use a factory pattern to avoid this huge flow control.
""" """
if is_royal_flush(hand): out = None
return Rank.ROYAL_FLUSH for rank, func in _FUNCTIONS.items():
elif is_straight_flush(hand): result = func(hand)
return Rank.STRAIGHT_FLUSH if result:
elif is_four_of_a_kind(hand): out = RankedHand(
return Rank.FOUR_OF_A_KIND cards=hand.cards,
elif is_full_house(hand): rank=rank,
return Rank.FULL_HOUSE description=DESCRIPTIONS[rank].format(**result),
elif is_flush(hand): )
return Rank.FLUSH
elif is_straight(hand): if out is None:
return Rank.STRAIGHT raise ValueError("No rank found.")
elif is_three_of_a_kind(hand):
return Rank.THREE_OF_A_KIND return out
elif is_two_pair(hand):
return Rank.TWO_PAIR
elif is_pair(hand):
return Rank.PAIR
else:
return Rank.HIGH_CARD

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "poker" name = "poker"
version = "1.0.0" version = "1.1.0"
description = "Single poker hand ranking service." description = "Single poker hand ranking service."
authors = ["Paul Harrison"] authors = ["Paul Harrison"]
readme = "README.md" readme = "README.md"

View File

@ -1,4 +1,5 @@
from itertools import cycle from itertools import cycle
from typing import Optional, Union
import pytest import pytest
@ -7,15 +8,16 @@ from poker.models import Card, Hand
from poker.rank.hands import ( from poker.rank.hands import (
_is_flush, _is_flush,
_is_straight, _is_straight,
is_flush, flush,
is_four_of_a_kind, four_of_a_kind,
is_full_house, full_house,
is_pair, high_card,
is_royal_flush, pair,
is_straight, royal_flush,
is_straight_flush, straight,
is_three_of_a_kind, straight_flush,
is_two_pair, three_of_a_kind,
two_pair,
) )
from tests.conftest import Factory from tests.conftest import Factory
@ -32,7 +34,7 @@ from tests.conftest import Factory
], ],
) )
def test__is_flush( def test__is_flush(
card_factory: Factory[Card], suits: list[Suit], expected: bool card_factory: Factory[Card], suits: list[Suit], expected: dict[str, Union[str, int]]
) -> None: ) -> None:
result = _is_flush([card_factory(suit=suit) for suit in suits]) result = _is_flush([card_factory(suit=suit) for suit in suits])
@ -45,11 +47,11 @@ def test__is_flush(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"values,expected", "values,expected",
[ [
([Value.TWO, Value.TWO, Value.THREE, Value.FOUR, Value.FIVE], False), ([Value.TWO, Value.TWO, Value.THREE, Value.FOUR, Value.FIVE], None),
([Value.THREE, Value.TWO, Value.ACE, Value.TEN, Value.KING], False), ([Value.THREE, Value.TWO, Value.ACE, Value.TEN, Value.KING], None),
([Value.ACE, Value.TWO, Value.THREE, Value.FOUR, Value.FIVE], True), ([Value.ACE, Value.TWO, Value.THREE, Value.FOUR, Value.FIVE], Value.FIVE),
([Value.ACE, Value.KING, Value.TEN, Value.JACK, Value.QUEEN], True), ([Value.ACE, Value.KING, Value.TEN, Value.JACK, Value.QUEEN], Value.ACE),
([Value.THREE, Value.FIVE, Value.TWO, Value.FOUR, Value.SIX], True), ([Value.THREE, Value.FIVE, Value.TWO, Value.FOUR, Value.SIX], Value.SIX),
], ],
ids=[ ids=[
"non-unique", "non-unique",
@ -60,25 +62,26 @@ def test__is_flush(
], ],
) )
def test__is_straight( def test__is_straight(
card_factory: Factory[Card], values: list[Value], expected: bool card_factory: Factory[Card], values: list[Value], expected: Optional[Value]
) -> None: ) -> None:
suits = cycle(Suit) suits = cycle(Suit)
result = _is_straight( result = _is_straight(
[card_factory(suit=suit, value=value) for suit, value in zip(suits, values)] [card_factory(suit=suit, value=value) for suit, value in zip(suits, values)]
) )
if expected: assert result == expected
assert result
else:
assert not result
@pytest.mark.parametrize( @pytest.mark.parametrize(
"values,different_suits,expected", "values,different_suits,expected",
[ [
([Value.TWO, Value.TEN, Value.THREE, Value.FOUR, Value.FIVE], False, False), ([Value.TWO, Value.TEN, Value.THREE, Value.FOUR, Value.FIVE], False, {}),
([Value.ACE, Value.KING, Value.TEN, Value.JACK, Value.QUEEN], False, True), (
([Value.ACE, Value.KING, Value.TEN, Value.JACK, Value.QUEEN], True, False), [Value.ACE, Value.KING, Value.TEN, Value.JACK, Value.QUEEN],
False,
{"suit": Suit.CLUBS},
),
([Value.ACE, Value.KING, Value.TEN, Value.JACK, Value.QUEEN], True, {}),
], ],
ids=[ ids=[
"not-flush", "not-flush",
@ -90,12 +93,12 @@ def test_is_royal_flush(
card_factory: Factory[Card], card_factory: Factory[Card],
values: list[Value], values: list[Value],
different_suits: bool, different_suits: bool,
expected: bool, expected: dict[str, Union[str, int]],
) -> None: ) -> None:
suits = cycle(Suit) suits = cycle(Suit)
if different_suits: if different_suits:
result = is_royal_flush( result = royal_flush(
Hand( Hand(
cards=[ cards=[
card_factory(suit=suit, value=value) card_factory(suit=suit, value=value)
@ -104,22 +107,23 @@ def test_is_royal_flush(
) )
) )
else: else:
result = is_royal_flush( result = royal_flush(
Hand(cards=[card_factory(value=value) for value in values]) Hand(cards=[card_factory(value=value) for value in values])
) )
if expected: assert result == expected
assert result
else:
assert not result
@pytest.mark.parametrize( @pytest.mark.parametrize(
"values,different_suits,expected", "values,different_suits,expected",
[ [
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], True, False), ([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], True, {}),
([Value.TWO, Value.THREE, Value.FIVE, Value.SIX, Value.TEN], False, False), ([Value.TWO, Value.THREE, Value.FIVE, Value.SIX, Value.TEN], False, {}),
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], False, True), (
[Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX],
False,
{"high": Value.SIX, "suit": Suit.CLUBS},
),
], ],
ids=[ ids=[
"straight-only", "straight-only",
@ -131,12 +135,12 @@ def test_is_straight_flush(
card_factory: Factory[Card], card_factory: Factory[Card],
values: list[Value], values: list[Value],
different_suits: bool, different_suits: bool,
expected: bool, expected: dict[str, Union[str, int]],
) -> None: ) -> None:
suits = cycle(Suit) suits = cycle(Suit)
if different_suits: if different_suits:
result = is_straight_flush( result = straight_flush(
Hand( Hand(
cards=[ cards=[
card_factory(suit=suit, value=value) card_factory(suit=suit, value=value)
@ -145,14 +149,11 @@ def test_is_straight_flush(
) )
) )
else: else:
result = is_straight_flush( result = straight_flush(
Hand(cards=[card_factory(value=value) for value in values]) Hand(cards=[card_factory(value=value) for value in values])
) )
if expected: assert result == expected
assert result
else:
assert not result
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -166,7 +167,7 @@ def test_is_straight_flush(
Card(suit=Suit.SPADES, value=Value.TWO), Card(suit=Suit.SPADES, value=Value.TWO),
Card(suit=Suit.CLUBS, value=Value.THREE), Card(suit=Suit.CLUBS, value=Value.THREE),
], ],
True, {"value": Value.TWO},
), ),
( (
[ [
@ -176,7 +177,7 @@ def test_is_straight_flush(
Card(suit=Suit.SPADES, value=Value.TWO), Card(suit=Suit.SPADES, value=Value.TWO),
Card(suit=Suit.CLUBS, value=Value.THREE), Card(suit=Suit.CLUBS, value=Value.THREE),
], ],
False, {},
), ),
], ],
ids=[ ids=[
@ -184,13 +185,12 @@ def test_is_straight_flush(
"not-four-of-a-kind", "not-four-of-a-kind",
], ],
) )
def test_is_four_of_a_kind(cards: list[Card], expected: bool) -> None: def test_is_four_of_a_kind(
result = is_four_of_a_kind(Hand(cards=cards)) cards: list[Card], expected: dict[str, Union[str, int]]
) -> None:
result = four_of_a_kind(Hand(cards=cards))
if expected: assert result == expected
assert result
else:
assert not result
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -204,7 +204,7 @@ def test_is_four_of_a_kind(cards: list[Card], expected: bool) -> None:
Card(suit=Suit.SPADES, value=Value.TWO), Card(suit=Suit.SPADES, value=Value.TWO),
Card(suit=Suit.SPADES, value=Value.THREE), Card(suit=Suit.SPADES, value=Value.THREE),
], ],
True, {"trips": Value.TWO, "pair": Value.THREE},
), ),
( (
[ [
@ -214,7 +214,7 @@ def test_is_four_of_a_kind(cards: list[Card], expected: bool) -> None:
Card(suit=Suit.SPADES, value=Value.TWO), Card(suit=Suit.SPADES, value=Value.TWO),
Card(suit=Suit.CLUBS, value=Value.THREE), Card(suit=Suit.CLUBS, value=Value.THREE),
], ],
False, {},
), ),
], ],
ids=[ ids=[
@ -222,21 +222,22 @@ def test_is_four_of_a_kind(cards: list[Card], expected: bool) -> None:
"not-full-house", "not-full-house",
], ],
) )
def test_is_full_house(cards: list[Card], expected: bool) -> None: def test_is_full_house(cards: list[Card], expected: dict[str, Union[str, int]]) -> None:
result = is_full_house(Hand(cards=cards)) result = full_house(Hand(cards=cards))
if expected: assert result == expected
assert result
else:
assert not result
@pytest.mark.parametrize( @pytest.mark.parametrize(
"values,different_suits,expected", "values,different_suits,expected",
[ [
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], True, False), ([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], True, {}),
([Value.TWO, Value.THREE, Value.FIVE, Value.SIX, Value.TEN], False, True), (
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], False, False), [Value.TWO, Value.THREE, Value.FIVE, Value.SIX, Value.TEN],
False,
{"suit": Suit.CLUBS},
),
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], False, {}),
], ],
ids=[ ids=[
"straight-only", "straight-only",
@ -248,12 +249,12 @@ def test_is_flush(
card_factory: Factory[Card], card_factory: Factory[Card],
values: list[Value], values: list[Value],
different_suits: bool, different_suits: bool,
expected: bool, expected: dict[str, Union[str, int]],
) -> None: ) -> None:
suits = cycle(Suit) suits = cycle(Suit)
if different_suits: if different_suits:
result = is_flush( result = flush(
Hand( Hand(
cards=[ cards=[
card_factory(suit=suit, value=value) card_factory(suit=suit, value=value)
@ -262,20 +263,21 @@ def test_is_flush(
) )
) )
else: else:
result = is_flush(Hand(cards=[card_factory(value=value) for value in values])) result = flush(Hand(cards=[card_factory(value=value) for value in values]))
if expected: assert result == expected
assert result
else:
assert not result
@pytest.mark.parametrize( @pytest.mark.parametrize(
"values,different_suits,expected", "values,different_suits,expected",
[ [
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], True, True), (
([Value.TWO, Value.THREE, Value.FIVE, Value.SIX, Value.TEN], False, False), [Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX],
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], False, False), True,
{"high": Value.SIX},
),
([Value.TWO, Value.THREE, Value.FIVE, Value.SIX, Value.TEN], False, {}),
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], False, {}),
], ],
ids=[ ids=[
"straight-only", "straight-only",
@ -287,12 +289,12 @@ def test_is_straight(
card_factory: Factory[Card], card_factory: Factory[Card],
values: list[Value], values: list[Value],
different_suits: bool, different_suits: bool,
expected: bool, expected: dict[str, Union[str, int]],
) -> None: ) -> None:
suits = cycle(Suit) suits = cycle(Suit)
if different_suits: if different_suits:
result = is_straight( result = straight(
Hand( Hand(
cards=[ cards=[
card_factory(suit=suit, value=value) card_factory(suit=suit, value=value)
@ -301,14 +303,9 @@ def test_is_straight(
) )
) )
else: else:
result = is_straight( result = straight(Hand(cards=[card_factory(value=value) for value in values]))
Hand(cards=[card_factory(value=value) for value in values])
)
if expected: assert result == expected
assert result
else:
assert not result
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -322,7 +319,7 @@ def test_is_straight(
Card(suit=Suit.SPADES, value=Value.TWO), Card(suit=Suit.SPADES, value=Value.TWO),
Card(suit=Suit.SPADES, value=Value.THREE), Card(suit=Suit.SPADES, value=Value.THREE),
], ],
False, {},
), ),
( (
[ [
@ -332,7 +329,7 @@ def test_is_straight(
Card(suit=Suit.SPADES, value=Value.ACE), Card(suit=Suit.SPADES, value=Value.ACE),
Card(suit=Suit.CLUBS, value=Value.THREE), Card(suit=Suit.CLUBS, value=Value.THREE),
], ],
True, {"value": Value.TWO},
), ),
( (
[ [
@ -342,18 +339,17 @@ def test_is_straight(
Card(suit=Suit.SPADES, value=Value.ACE), Card(suit=Suit.SPADES, value=Value.ACE),
Card(suit=Suit.CLUBS, value=Value.THREE), Card(suit=Suit.CLUBS, value=Value.THREE),
], ],
False, {},
), ),
], ],
ids=["full-house", "three-of-a-kind", "not-three-of-a-kind"], ids=["full-house", "three-of-a-kind", "not-three-of-a-kind"],
) )
def test_is_three_of_a_kind(cards: list[Card], expected: bool) -> None: def test_is_three_of_a_kind(
result = is_three_of_a_kind(Hand(cards=cards)) cards: list[Card], expected: dict[str, Union[str, int]]
) -> None:
result = three_of_a_kind(Hand(cards=cards))
if expected: assert result == expected
assert result
else:
assert not result
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -367,7 +363,7 @@ def test_is_three_of_a_kind(cards: list[Card], expected: bool) -> None:
Card(suit=Suit.SPADES, value=Value.THREE), Card(suit=Suit.SPADES, value=Value.THREE),
Card(suit=Suit.DIAMONDS, value=Value.THREE), Card(suit=Suit.DIAMONDS, value=Value.THREE),
], ],
False, {},
), ),
( (
[ [
@ -377,7 +373,7 @@ def test_is_three_of_a_kind(cards: list[Card], expected: bool) -> None:
Card(suit=Suit.SPADES, value=Value.ACE), Card(suit=Suit.SPADES, value=Value.ACE),
Card(suit=Suit.CLUBS, value=Value.THREE), Card(suit=Suit.CLUBS, value=Value.THREE),
], ],
True, {"high": Value.ACE, "low": Value.TWO},
), ),
( (
[ [
@ -387,18 +383,15 @@ def test_is_three_of_a_kind(cards: list[Card], expected: bool) -> None:
Card(suit=Suit.SPADES, value=Value.ACE), Card(suit=Suit.SPADES, value=Value.ACE),
Card(suit=Suit.CLUBS, value=Value.THREE), Card(suit=Suit.CLUBS, value=Value.THREE),
], ],
False, {},
), ),
], ],
ids=["full-house", "two-pair", "three-of-a-kind"], ids=["full-house", "two-pair", "three-of-a-kind"],
) )
def test_is_two_pair(cards: list[Card], expected: bool) -> None: def test_is_two_pair(cards: list[Card], expected: dict[str, Union[str, int]]) -> None:
result = is_two_pair(Hand(cards=cards)) result = two_pair(Hand(cards=cards))
if expected: assert result == expected
assert result
else:
assert not result
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -412,7 +405,7 @@ def test_is_two_pair(cards: list[Card], expected: bool) -> None:
Card(suit=Suit.SPADES, value=Value.THREE), Card(suit=Suit.SPADES, value=Value.THREE),
Card(suit=Suit.DIAMONDS, value=Value.THREE), Card(suit=Suit.DIAMONDS, value=Value.THREE),
], ],
False, {},
), ),
( (
[ [
@ -422,7 +415,7 @@ def test_is_two_pair(cards: list[Card], expected: bool) -> None:
Card(suit=Suit.SPADES, value=Value.ACE), Card(suit=Suit.SPADES, value=Value.ACE),
Card(suit=Suit.CLUBS, value=Value.THREE), Card(suit=Suit.CLUBS, value=Value.THREE),
], ],
False, {},
), ),
( (
[ [
@ -432,15 +425,18 @@ def test_is_two_pair(cards: list[Card], expected: bool) -> None:
Card(suit=Suit.SPADES, value=Value.ACE), Card(suit=Suit.SPADES, value=Value.ACE),
Card(suit=Suit.CLUBS, value=Value.THREE), Card(suit=Suit.CLUBS, value=Value.THREE),
], ],
True, {"value": Value.TWO},
), ),
], ],
ids=["full-house", "two-pair", "pair"], ids=["full-house", "two-pair", "pair"],
) )
def test_is_pair(cards: list[Card], expected: bool) -> None: def test_is_pair(cards: list[Card], expected: dict[str, Union[str, int]]) -> None:
result = is_pair(Hand(cards=cards)) result = pair(Hand(cards=cards))
if expected: assert result == expected
assert result
else:
assert not result def test_high_card(card_factory: Factory[Card]) -> None:
values = [Value.TWO, Value.THREE, Value.FIVE, Value.SIX, Value.TEN]
result = high_card(Hand(cards=[card_factory(value=value) for value in values]))
assert result == {"value": Value.TEN}