parent
5393e7b799
commit
d924e39608
|
@ -3,6 +3,21 @@ from enum import IntEnum, auto
|
||||||
from poker.utils.enum import AutoName
|
from poker.utils.enum import AutoName
|
||||||
|
|
||||||
|
|
||||||
|
class Rank(IntEnum):
|
||||||
|
"""Poker rank enum."""
|
||||||
|
|
||||||
|
ROYAL_FLUSH = 1
|
||||||
|
STRAIGHT_FLUSH = 2
|
||||||
|
FOUR_OF_A_KIND = 3
|
||||||
|
FULL_HOUSE = 4
|
||||||
|
FLUSH = 5
|
||||||
|
STRAIGHT = 6
|
||||||
|
THREE_OF_A_KIND = 7
|
||||||
|
TWO_PAIR = 8
|
||||||
|
PAIR = 9
|
||||||
|
HIGH_CARD = 10
|
||||||
|
|
||||||
|
|
||||||
class Suit(AutoName):
|
class Suit(AutoName):
|
||||||
"""Card suit enum."""
|
"""Card suit enum."""
|
||||||
|
|
||||||
|
@ -15,7 +30,6 @@ class Suit(AutoName):
|
||||||
class Value(IntEnum):
|
class Value(IntEnum):
|
||||||
"""Card value enum."""
|
"""Card value enum."""
|
||||||
|
|
||||||
ACE = 1
|
|
||||||
TWO = 2
|
TWO = 2
|
||||||
THREE = 3
|
THREE = 3
|
||||||
FOUR = 4
|
FOUR = 4
|
||||||
|
@ -28,3 +42,12 @@ class Value(IntEnum):
|
||||||
JACK = 11
|
JACK = 11
|
||||||
QUEEN = 12
|
QUEEN = 12
|
||||||
KING = 13
|
KING = 13
|
||||||
|
ACE = 14
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""Return string representation."""
|
||||||
|
if self.value in [1, 11, 12, 13]:
|
||||||
|
out: str = self.name.lower()
|
||||||
|
else:
|
||||||
|
out = str(self.value)
|
||||||
|
return out
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, validator
|
||||||
|
|
||||||
from poker.constants import Rank, Suit, Value
|
from poker.constants import Rank, Suit, Value
|
||||||
|
@ -43,6 +45,11 @@ class Hand(BaseModel):
|
||||||
raise ValueError("Hand must have five cards.")
|
raise ValueError("Hand must have five cards.")
|
||||||
return cards
|
return cards
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value_counts(self) -> list[tuple[Value, int]]:
|
||||||
|
"""Return count of each card value in hand."""
|
||||||
|
return Counter([card.value for card in self.cards]).most_common()
|
||||||
|
|
||||||
|
|
||||||
class RankedHand(Hand):
|
class RankedHand(Hand):
|
||||||
"""Ranked hand domain model class."""
|
"""Ranked hand domain model class."""
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
from poker.constants import Rank, Value
|
||||||
|
from poker.models import Card, Hand
|
||||||
|
|
||||||
|
|
||||||
|
def _is_flush(cards: list[Card]) -> bool:
|
||||||
|
"""Calculate whether hand is a flush."""
|
||||||
|
return len(set(card.suit for card in cards)) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def _is_straight(cards: list[Card]) -> bool:
|
||||||
|
"""Calculate whether hand is a straight."""
|
||||||
|
card_values = sorted([card.value for card in cards])
|
||||||
|
|
||||||
|
# If card values are not unique it is not a straight
|
||||||
|
if len(set(card_values)) != len(card_values):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check for a ace low straight
|
||||||
|
if card_values == [Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.ACE]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If all card differences are 1 it is a straight
|
||||||
|
diffs = [second - first for first, second in zip(card_values[:-1], card_values[1:])]
|
||||||
|
if set(diffs) == {1}:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_royal_flush(hand: Hand) -> bool:
|
||||||
|
"""Calculate whether hand a royal flush."""
|
||||||
|
if not _is_flush(hand.cards):
|
||||||
|
return False
|
||||||
|
|
||||||
|
values = [card.value for card in hand.cards]
|
||||||
|
return all(
|
||||||
|
value in values
|
||||||
|
for value in [Value.ACE, Value.KING, Value.QUEEN, Value.JACK, Value.TEN]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def is_straight_flush(hand: Hand) -> bool:
|
||||||
|
"""Calculate whether hand a straight flush."""
|
||||||
|
return _is_straight(hand.cards) and _is_flush(hand.cards)
|
||||||
|
|
||||||
|
|
||||||
|
def is_four_of_a_kind(hand: Hand) -> bool:
|
||||||
|
"""Calculate whether hand has four of a kind."""
|
||||||
|
return hand.value_counts[0][1] == 4
|
||||||
|
|
||||||
|
|
||||||
|
def is_full_house(hand: Hand) -> bool:
|
||||||
|
"""Calculate whether hand is a full house."""
|
||||||
|
return hand.value_counts[0][1] == 3 and hand.value_counts[1][1] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def is_flush(hand: Hand) -> bool:
|
||||||
|
"""Calculate whether hand is a flush."""
|
||||||
|
return _is_flush(hand.cards) and not _is_straight(hand.cards)
|
||||||
|
|
||||||
|
|
||||||
|
def is_straight(hand: Hand) -> bool:
|
||||||
|
"""Calculate whether hand is a flush."""
|
||||||
|
return _is_straight(hand.cards) and not _is_flush(hand.cards)
|
||||||
|
|
||||||
|
|
||||||
|
def is_three_of_a_kind(hand: Hand) -> bool:
|
||||||
|
"""Calculate whether hand is a full house."""
|
||||||
|
return (
|
||||||
|
hand.value_counts[0][1] == 3
|
||||||
|
and hand.value_counts[1][1] == 1
|
||||||
|
and hand.value_counts[2][1] == 1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def is_two_pair(hand: Hand) -> bool:
|
||||||
|
"""Calculate whether hand is a two pair."""
|
||||||
|
return hand.value_counts[0][1] == 2 and hand.value_counts[1][1] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def is_pair(hand: Hand) -> bool:
|
||||||
|
"""Calculate whether hand is a pair."""
|
||||||
|
return hand.value_counts[0][1] == 2 and hand.value_counts[1][1] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def get_rank(hand: Hand) -> Rank:
|
||||||
|
"""Get hand rank.
|
||||||
|
|
||||||
|
TODO: Use a factory pattern to avoid this huge flow control.
|
||||||
|
"""
|
||||||
|
if is_royal_flush(hand):
|
||||||
|
return Rank.ROYAL_FLUSH
|
||||||
|
elif is_straight_flush(hand):
|
||||||
|
return Rank.STRAIGHT_FLUSH
|
||||||
|
elif is_four_of_a_kind(hand):
|
||||||
|
return Rank.FOUR_OF_A_KIND
|
||||||
|
elif is_full_house(hand):
|
||||||
|
return Rank.FULL_HOUSE
|
||||||
|
elif is_flush(hand):
|
||||||
|
return Rank.FLUSH
|
||||||
|
elif is_straight(hand):
|
||||||
|
return Rank.STRAIGHT
|
||||||
|
elif is_three_of_a_kind(hand):
|
||||||
|
return Rank.THREE_OF_A_KIND
|
||||||
|
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 = "0.2.4"
|
version = "1.0.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"
|
||||||
|
|
|
@ -0,0 +1,446 @@
|
||||||
|
from itertools import cycle
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from poker.constants import Suit, Value
|
||||||
|
from poker.models import Card, Hand
|
||||||
|
from poker.rank.hands import (
|
||||||
|
_is_flush,
|
||||||
|
_is_straight,
|
||||||
|
is_flush,
|
||||||
|
is_four_of_a_kind,
|
||||||
|
is_full_house,
|
||||||
|
is_pair,
|
||||||
|
is_royal_flush,
|
||||||
|
is_straight,
|
||||||
|
is_straight_flush,
|
||||||
|
is_three_of_a_kind,
|
||||||
|
is_two_pair,
|
||||||
|
)
|
||||||
|
from tests.conftest import Factory
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"suits,expected",
|
||||||
|
[
|
||||||
|
([Suit.CLUBS, Suit.DIAMONDS, Suit.SPADES, Suit.HEARTS, Suit.CLUBS], False),
|
||||||
|
([Suit.CLUBS, Suit.CLUBS, Suit.CLUBS, Suit.CLUBS, Suit.CLUBS], True),
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
"non-unique",
|
||||||
|
"unique",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test__is_flush(
|
||||||
|
card_factory: Factory[Card], suits: list[Suit], expected: bool
|
||||||
|
) -> None:
|
||||||
|
result = _is_flush([card_factory(suit=suit) for suit in suits])
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"values,expected",
|
||||||
|
[
|
||||||
|
([Value.TWO, Value.TWO, Value.THREE, Value.FOUR, Value.FIVE], False),
|
||||||
|
([Value.THREE, Value.TWO, Value.ACE, Value.TEN, Value.KING], False),
|
||||||
|
([Value.ACE, Value.TWO, Value.THREE, Value.FOUR, Value.FIVE], True),
|
||||||
|
([Value.ACE, Value.KING, Value.TEN, Value.JACK, Value.QUEEN], True),
|
||||||
|
([Value.THREE, Value.FIVE, Value.TWO, Value.FOUR, Value.SIX], True),
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
"non-unique",
|
||||||
|
"not-flush",
|
||||||
|
"low-ace",
|
||||||
|
"high-ace",
|
||||||
|
"middle-flush",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test__is_straight(
|
||||||
|
card_factory: Factory[Card], values: list[Value], expected: bool
|
||||||
|
) -> None:
|
||||||
|
suits = cycle(Suit)
|
||||||
|
result = _is_straight(
|
||||||
|
[card_factory(suit=suit, value=value) for suit, value in zip(suits, values)]
|
||||||
|
)
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"values,different_suits,expected",
|
||||||
|
[
|
||||||
|
([Value.TWO, Value.TEN, Value.THREE, Value.FOUR, Value.FIVE], False, 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),
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
"not-flush",
|
||||||
|
"royal-flush",
|
||||||
|
"flush-not-royal",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_is_royal_flush(
|
||||||
|
card_factory: Factory[Card],
|
||||||
|
values: list[Value],
|
||||||
|
different_suits: bool,
|
||||||
|
expected: bool,
|
||||||
|
) -> None:
|
||||||
|
suits = cycle(Suit)
|
||||||
|
|
||||||
|
if different_suits:
|
||||||
|
result = is_royal_flush(
|
||||||
|
Hand(
|
||||||
|
cards=[
|
||||||
|
card_factory(suit=suit, value=value)
|
||||||
|
for suit, value in zip(suits, values)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result = is_royal_flush(
|
||||||
|
Hand(cards=[card_factory(value=value) for value in values])
|
||||||
|
)
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"values,different_suits,expected",
|
||||||
|
[
|
||||||
|
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], True, False),
|
||||||
|
([Value.TWO, Value.THREE, Value.FIVE, Value.SIX, Value.TEN], False, False),
|
||||||
|
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], False, True),
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
"straight-only",
|
||||||
|
"flush-only",
|
||||||
|
"straight-flush",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_is_straight_flush(
|
||||||
|
card_factory: Factory[Card],
|
||||||
|
values: list[Value],
|
||||||
|
different_suits: bool,
|
||||||
|
expected: bool,
|
||||||
|
) -> None:
|
||||||
|
suits = cycle(Suit)
|
||||||
|
|
||||||
|
if different_suits:
|
||||||
|
result = is_straight_flush(
|
||||||
|
Hand(
|
||||||
|
cards=[
|
||||||
|
card_factory(suit=suit, value=value)
|
||||||
|
for suit, value in zip(suits, values)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result = is_straight_flush(
|
||||||
|
Hand(cards=[card_factory(value=value) for value in values])
|
||||||
|
)
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cards,expected",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.TWO),
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TEN),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.TWO),
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
"four-of-a-kind",
|
||||||
|
"not-four-of-a-kind",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_is_four_of_a_kind(cards: list[Card], expected: bool) -> None:
|
||||||
|
result = is_four_of_a_kind(Hand(cards=cards))
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cards,expected",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.THREE),
|
||||||
|
],
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.TWO),
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
"full-house",
|
||||||
|
"not-full-house",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_is_full_house(cards: list[Card], expected: bool) -> None:
|
||||||
|
result = is_full_house(Hand(cards=cards))
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"values,different_suits,expected",
|
||||||
|
[
|
||||||
|
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], True, False),
|
||||||
|
([Value.TWO, Value.THREE, Value.FIVE, Value.SIX, Value.TEN], False, True),
|
||||||
|
([Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.SIX], False, False),
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
"straight-only",
|
||||||
|
"flush-only",
|
||||||
|
"straight-flush",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_is_flush(
|
||||||
|
card_factory: Factory[Card],
|
||||||
|
values: list[Value],
|
||||||
|
different_suits: bool,
|
||||||
|
expected: bool,
|
||||||
|
) -> None:
|
||||||
|
suits = cycle(Suit)
|
||||||
|
|
||||||
|
if different_suits:
|
||||||
|
result = is_flush(
|
||||||
|
Hand(
|
||||||
|
cards=[
|
||||||
|
card_factory(suit=suit, value=value)
|
||||||
|
for suit, value in zip(suits, values)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result = is_flush(Hand(cards=[card_factory(value=value) for value in values]))
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"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], False, False),
|
||||||
|
],
|
||||||
|
ids=[
|
||||||
|
"straight-only",
|
||||||
|
"flush-only",
|
||||||
|
"straight-flush",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_is_straight(
|
||||||
|
card_factory: Factory[Card],
|
||||||
|
values: list[Value],
|
||||||
|
different_suits: bool,
|
||||||
|
expected: bool,
|
||||||
|
) -> None:
|
||||||
|
suits = cycle(Suit)
|
||||||
|
|
||||||
|
if different_suits:
|
||||||
|
result = is_straight(
|
||||||
|
Hand(
|
||||||
|
cards=[
|
||||||
|
card_factory(suit=suit, value=value)
|
||||||
|
for suit, value in zip(suits, values)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result = is_straight(
|
||||||
|
Hand(cards=[card_factory(value=value) for value in values])
|
||||||
|
)
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cards,expected",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.THREE),
|
||||||
|
],
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.ACE),
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TEN),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.ACE),
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
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:
|
||||||
|
result = is_three_of_a_kind(Hand(cards=cards))
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cards,expected",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.THREE),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.ACE),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.ACE),
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.ACE),
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ids=["full-house", "two-pair", "three-of-a-kind"],
|
||||||
|
)
|
||||||
|
def test_is_two_pair(cards: list[Card], expected: bool) -> None:
|
||||||
|
result = is_two_pair(Hand(cards=cards))
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cards,expected",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.THREE),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.ACE),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.ACE),
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.DIAMONDS, value=Value.TWO),
|
||||||
|
Card(suit=Suit.HEARTS, value=Value.KING),
|
||||||
|
Card(suit=Suit.SPADES, value=Value.ACE),
|
||||||
|
Card(suit=Suit.CLUBS, value=Value.THREE),
|
||||||
|
],
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ids=["full-house", "two-pair", "pair"],
|
||||||
|
)
|
||||||
|
def test_is_pair(cards: list[Card], expected: bool) -> None:
|
||||||
|
result = is_pair(Hand(cards=cards))
|
||||||
|
|
||||||
|
if expected:
|
||||||
|
assert result
|
||||||
|
else:
|
||||||
|
assert not result
|
Loading…
Reference in New Issue