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:
parent
d924e39608
commit
b4ba284dcc
|
@ -0,0 +1,3 @@
|
||||||
|
from poker.rank.hands import rank_hand
|
||||||
|
|
||||||
|
__all__ = ["rank_hand"]
|
|
@ -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}",
|
||||||
|
}
|
|
@ -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
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in New Issue