mirror of
https://github.com/TheAlgorithms/Python.git
synced 2025-01-18 16:27:02 +00:00
Create problem_54 in project Euler (#2451)
* Add solution and test files for project euler 54 * Update sol1.py * updating DIRECTORY.md * Fix: use proper path to open files * Commit suggestions: - Use list comprehension instead of map - Sort imports using isort * Changes made as suggested (simplified a lot): - List and set comprehension instead of itemgetter - Using enumerate as it's easy to read - Divided into list of card values and set of card suit as set will remove all the duplicate values. So, no need for double indexing. - Add test for testing multiple calls to five_high_straight function * Add suggestions and simplified: - Split generate_random_hands function into two: - First will generate a random hand - Second, which will be called, will return a generator object Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
This commit is contained in:
parent
9b73884def
commit
ea0759dbaa
|
@ -86,6 +86,7 @@
|
|||
|
||||
## Conversions
|
||||
* [Binary To Decimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/binary_to_decimal.py)
|
||||
* [Binary To Octal](https://github.com/TheAlgorithms/Python/blob/master/conversions/binary_to_octal.py)
|
||||
* [Decimal To Any](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_any.py)
|
||||
* [Decimal To Binary](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_binary.py)
|
||||
* [Decimal To Hexadecimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_hexadecimal.py)
|
||||
|
@ -628,6 +629,9 @@
|
|||
* [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_52/sol1.py)
|
||||
* Problem 53
|
||||
* [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_53/sol1.py)
|
||||
* Problem 54
|
||||
* [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_54/sol1.py)
|
||||
* [Test Poker Hand](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_54/test_poker_hand.py)
|
||||
* Problem 55
|
||||
* [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_55/sol1.py)
|
||||
* Problem 551
|
||||
|
|
0
project_euler/problem_54/__init__.py
Normal file
0
project_euler/problem_54/__init__.py
Normal file
1000
project_euler/problem_54/poker_hands.txt
Normal file
1000
project_euler/problem_54/poker_hands.txt
Normal file
File diff suppressed because it is too large
Load Diff
358
project_euler/problem_54/sol1.py
Normal file
358
project_euler/problem_54/sol1.py
Normal file
|
@ -0,0 +1,358 @@
|
|||
"""
|
||||
Problem: https://projecteuler.net/problem=54
|
||||
|
||||
In the card game poker, a hand consists of five cards and are ranked,
|
||||
from lowest to highest, in the following way:
|
||||
|
||||
High Card: Highest value card.
|
||||
One Pair: Two cards of the same value.
|
||||
Two Pairs: Two different pairs.
|
||||
Three of a Kind: Three cards of the same value.
|
||||
Straight: All cards are consecutive values.
|
||||
Flush: All cards of the same suit.
|
||||
Full House: Three of a kind and a pair.
|
||||
Four of a Kind: Four cards of the same value.
|
||||
Straight Flush: All cards are consecutive values of same suit.
|
||||
Royal Flush: Ten, Jack, Queen, King, Ace, in same suit.
|
||||
|
||||
The cards are valued in the order:
|
||||
2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King, Ace.
|
||||
|
||||
If two players have the same ranked hands then the rank made up of the highest
|
||||
value wins; for example, a pair of eights beats a pair of fives.
|
||||
But if two ranks tie, for example, both players have a pair of queens, then highest
|
||||
cards in each hand are compared; if the highest cards tie then the next highest
|
||||
cards are compared, and so on.
|
||||
|
||||
The file, poker.txt, contains one-thousand random hands dealt to two players.
|
||||
Each line of the file contains ten cards (separated by a single space): the
|
||||
first five are Player 1's cards and the last five are Player 2's cards.
|
||||
You can assume that all hands are valid (no invalid characters or repeated cards),
|
||||
each player's hand is in no specific order, and in each hand there is a clear winner.
|
||||
|
||||
How many hands does Player 1 win?
|
||||
|
||||
Resources used:
|
||||
https://en.wikipedia.org/wiki/Texas_hold_%27em
|
||||
https://en.wikipedia.org/wiki/List_of_poker_hands
|
||||
|
||||
Similar problem on codewars:
|
||||
https://www.codewars.com/kata/ranking-poker-hands
|
||||
https://www.codewars.com/kata/sortable-poker-hands
|
||||
"""
|
||||
from typing import List, Set, Tuple
|
||||
|
||||
|
||||
class PokerHand(object):
|
||||
"""Create an object representing a Poker Hand based on an input of a
|
||||
string which represents the best 5 card combination from the player's hand
|
||||
and board cards.
|
||||
|
||||
Attributes: (read-only)
|
||||
hand: string representing the hand consisting of five cards
|
||||
|
||||
Methods:
|
||||
compare_with(opponent): takes in player's hand (self) and
|
||||
opponent's hand (opponent) and compares both hands according to
|
||||
the rules of Texas Hold'em.
|
||||
Returns one of 3 strings (Win, Loss, Tie) based on whether
|
||||
player's hand is better than opponent's hand.
|
||||
|
||||
hand_name(): Returns a string made up of two parts: hand name
|
||||
and high card.
|
||||
|
||||
Supported operators:
|
||||
Rich comparison operators: <, >, <=, >=, ==, !=
|
||||
|
||||
Supported builtin methods and functions:
|
||||
list.sort(), sorted()
|
||||
"""
|
||||
|
||||
_HAND_NAME = [
|
||||
"High card",
|
||||
"One pair",
|
||||
"Two pairs",
|
||||
"Three of a kind",
|
||||
"Straight",
|
||||
"Flush",
|
||||
"Full house",
|
||||
"Four of a kind",
|
||||
"Straight flush",
|
||||
"Royal flush",
|
||||
]
|
||||
|
||||
_CARD_NAME = [
|
||||
"", # placeholder as lists are zero indexed
|
||||
"One",
|
||||
"Two",
|
||||
"Three",
|
||||
"Four",
|
||||
"Five",
|
||||
"Six",
|
||||
"Seven",
|
||||
"Eight",
|
||||
"Nine",
|
||||
"Ten",
|
||||
"Jack",
|
||||
"Queen",
|
||||
"King",
|
||||
"Ace",
|
||||
]
|
||||
|
||||
def __init__(self, hand: str) -> None:
|
||||
"""
|
||||
Initialize hand.
|
||||
Hand should of type str and should contain only five cards each
|
||||
separated by a space.
|
||||
|
||||
The cards should be of the following format:
|
||||
[card value][card suit]
|
||||
|
||||
The first character is the value of the card:
|
||||
2, 3, 4, 5, 6, 7, 8, 9, T(en), J(ack), Q(ueen), K(ing), A(ce)
|
||||
|
||||
The second character represents the suit:
|
||||
S(pades), H(earts), D(iamonds), C(lubs)
|
||||
|
||||
For example: "6S 4C KC AS TH"
|
||||
"""
|
||||
if not isinstance(hand, str):
|
||||
raise TypeError(f"Hand should be of type 'str': {hand!r}")
|
||||
# split removes duplicate whitespaces so no need of strip
|
||||
if len(hand.split(" ")) != 5:
|
||||
raise ValueError(f"Hand should contain only 5 cards: {hand!r}")
|
||||
self._hand = hand
|
||||
self._first_pair = 0
|
||||
self._second_pair = 0
|
||||
self._card_values, self._card_suit = self._internal_state()
|
||||
self._hand_type = self._get_hand_type()
|
||||
self._high_card = self._card_values[0]
|
||||
|
||||
@property
|
||||
def hand(self):
|
||||
"""Returns the self hand"""
|
||||
return self._hand
|
||||
|
||||
def compare_with(self, other: "PokerHand") -> str:
|
||||
"""
|
||||
Determines the outcome of comparing self hand with other hand.
|
||||
Returns the output as 'Win', 'Loss', 'Tie' according to the rules of
|
||||
Texas Hold'em.
|
||||
|
||||
Here are some examples:
|
||||
>>> player = PokerHand("2H 3H 4H 5H 6H") # Stright flush
|
||||
>>> opponent = PokerHand("KS AS TS QS JS") # Royal flush
|
||||
>>> player.compare_with(opponent)
|
||||
'Loss'
|
||||
|
||||
>>> player = PokerHand("2S AH 2H AS AC") # Full house
|
||||
>>> opponent = PokerHand("2H 3H 5H 6H 7H") # Flush
|
||||
>>> player.compare_with(opponent)
|
||||
'Win'
|
||||
|
||||
>>> player = PokerHand("2S AH 4H 5S 6C") # High card
|
||||
>>> opponent = PokerHand("AD 4C 5H 6H 2C") # High card
|
||||
>>> player.compare_with(opponent)
|
||||
'Tie'
|
||||
"""
|
||||
# Breaking the tie works on the following order of precedence:
|
||||
# 1. First pair (default 0)
|
||||
# 2. Second pair (default 0)
|
||||
# 3. Compare all cards in reverse order because they are sorted.
|
||||
|
||||
# First pair and second pair will only be a non-zero value if the card
|
||||
# type is either from the following:
|
||||
# 21: Four of a kind
|
||||
# 20: Full house
|
||||
# 17: Three of a kind
|
||||
# 16: Two pairs
|
||||
# 15: One pair
|
||||
if self._hand_type > other._hand_type:
|
||||
return "Win"
|
||||
elif self._hand_type < other._hand_type:
|
||||
return "Loss"
|
||||
elif self._first_pair == other._first_pair:
|
||||
if self._second_pair == other._second_pair:
|
||||
return self._compare_cards(other)
|
||||
else:
|
||||
return "Win" if self._second_pair > other._second_pair else "Loss"
|
||||
return "Win" if self._first_pair > other._first_pair else "Loss"
|
||||
|
||||
# This function is not part of the problem, I did it just for fun
|
||||
def hand_name(self) -> str:
|
||||
"""
|
||||
Return the name of the hand in the following format:
|
||||
'hand name, high card'
|
||||
|
||||
Here are some examples:
|
||||
>>> PokerHand("KS AS TS QS JS").hand_name()
|
||||
'Royal flush'
|
||||
|
||||
>>> PokerHand("2D 6D 3D 4D 5D").hand_name()
|
||||
'Straight flush, Six-high'
|
||||
|
||||
>>> PokerHand("JC 6H JS JD JH").hand_name()
|
||||
'Four of a kind, Jacks'
|
||||
|
||||
>>> PokerHand("3D 2H 3H 2C 2D").hand_name()
|
||||
'Full house, Twos over Threes'
|
||||
|
||||
>>> PokerHand("2H 4D 3C AS 5S").hand_name() # Low ace
|
||||
'Straight, Five-high'
|
||||
|
||||
Source: https://en.wikipedia.org/wiki/List_of_poker_hands
|
||||
"""
|
||||
name = PokerHand._HAND_NAME[self._hand_type - 14]
|
||||
high = PokerHand._CARD_NAME[self._high_card]
|
||||
pair1 = PokerHand._CARD_NAME[self._first_pair]
|
||||
pair2 = PokerHand._CARD_NAME[self._second_pair]
|
||||
if self._hand_type in [22, 19, 18]:
|
||||
return name + f", {high}-high"
|
||||
elif self._hand_type in [21, 17, 15]:
|
||||
return name + f", {pair1}s"
|
||||
elif self._hand_type in [20, 16]:
|
||||
join = "over" if self._hand_type == 20 else "and"
|
||||
return name + f", {pair1}s {join} {pair2}s"
|
||||
elif self._hand_type == 23:
|
||||
return name
|
||||
else:
|
||||
return name + f", {high}"
|
||||
|
||||
def _compare_cards(self, other: "PokerHand") -> str:
|
||||
# Enumerate gives us the index as well as the element of a list
|
||||
for index, card_value in enumerate(self._card_values):
|
||||
if card_value != other._card_values[index]:
|
||||
return "Win" if card_value > other._card_values[index] else "Loss"
|
||||
return "Tie"
|
||||
|
||||
def _get_hand_type(self) -> int:
|
||||
# Number representing the type of hand internally:
|
||||
# 23: Royal flush
|
||||
# 22: Straight flush
|
||||
# 21: Four of a kind
|
||||
# 20: Full house
|
||||
# 19: Flush
|
||||
# 18: Straight
|
||||
# 17: Three of a kind
|
||||
# 16: Two pairs
|
||||
# 15: One pair
|
||||
# 14: High card
|
||||
if self._is_flush():
|
||||
if self._is_five_high_straight() or self._is_straight():
|
||||
return 23 if sum(self._card_values) == 60 else 22
|
||||
return 19
|
||||
elif self._is_five_high_straight() or self._is_straight():
|
||||
return 18
|
||||
return 14 + self._is_same_kind()
|
||||
|
||||
def _is_flush(self) -> bool:
|
||||
return len(self._card_suit) == 1
|
||||
|
||||
def _is_five_high_straight(self) -> bool:
|
||||
# If a card is a five high straight (low ace) change the location of
|
||||
# ace from the start of the list to the end. Check whether the first
|
||||
# element is ace or not. (Don't want to change again)
|
||||
# Five high straight (low ace): AH 2H 3S 4C 5D
|
||||
# Why use sorted here? One call to this function will mutate the list to
|
||||
# [5, 4, 3, 2, 14] and so for subsequent calls (which will be rare) we
|
||||
# need to compare the sorted version.
|
||||
# Refer test_multiple_calls_five_high_straight in test_poker_hand.py
|
||||
if sorted(self._card_values) == [2, 3, 4, 5, 14]:
|
||||
if self._card_values[0] == 14:
|
||||
# Remember, our list is sorted in reverse order
|
||||
ace_card = self._card_values.pop(0)
|
||||
self._card_values.append(ace_card)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_straight(self) -> bool:
|
||||
for i in range(4):
|
||||
if self._card_values[i] - self._card_values[i + 1] != 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _is_same_kind(self) -> int:
|
||||
# Kind Values for internal use:
|
||||
# 7: Four of a kind
|
||||
# 6: Full house
|
||||
# 3: Three of a kind
|
||||
# 2: Two pairs
|
||||
# 1: One pair
|
||||
# 0: False
|
||||
kind = val1 = val2 = 0
|
||||
for i in range(4):
|
||||
# Compare two cards at a time, if they are same increase 'kind',
|
||||
# add the value of the card to val1, if it is repeating again we
|
||||
# will add 2 to 'kind' as there are now 3 cards with same value.
|
||||
# If we get card of different value than val1, we will do the same
|
||||
# thing with val2
|
||||
if self._card_values[i] == self._card_values[i + 1]:
|
||||
if not val1:
|
||||
val1 = self._card_values[i]
|
||||
kind += 1
|
||||
elif val1 == self._card_values[i]:
|
||||
kind += 2
|
||||
elif not val2:
|
||||
val2 = self._card_values[i]
|
||||
kind += 1
|
||||
elif val2 == self._card_values[i]:
|
||||
kind += 2
|
||||
# For consistency in hand type (look at note in _get_hand_type function)
|
||||
kind = kind + 2 if kind in [4, 5] else kind
|
||||
# first meaning first pair to compare in 'compare_with'
|
||||
first = max(val1, val2)
|
||||
second = min(val1, val2)
|
||||
# If it's full house (three count pair + two count pair), make sure
|
||||
# first pair is three count and if not then switch them both.
|
||||
if kind == 6 and self._card_values.count(first) != 3:
|
||||
first, second = second, first
|
||||
self._first_pair = first
|
||||
self._second_pair = second
|
||||
return kind
|
||||
|
||||
def _internal_state(self) -> Tuple[List[int], Set[str]]:
|
||||
# Internal representation of hand as a list of card values and
|
||||
# a set of card suit
|
||||
trans: dict = {"T": "10", "J": "11", "Q": "12", "K": "13", "A": "14"}
|
||||
new_hand = self._hand.translate(str.maketrans(trans)).split()
|
||||
card_values = [int(card[:-1]) for card in new_hand]
|
||||
card_suit = {card[-1] for card in new_hand}
|
||||
return sorted(card_values, reverse=True), card_suit
|
||||
|
||||
def __repr__(self):
|
||||
return f'{self.__class__}("{self._hand}")'
|
||||
|
||||
def __str__(self):
|
||||
return self._hand
|
||||
|
||||
# Rich comparison operators (used in list.sort() and sorted() builtin functions)
|
||||
# Note that this is not part of the problem but another extra feature where
|
||||
# if you have a list of PokerHand objects, you can sort them just through
|
||||
# the builtin functions.
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, PokerHand):
|
||||
return self.compare_with(other) == "Tie"
|
||||
return NotImplemented
|
||||
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, PokerHand):
|
||||
return self.compare_with(other) == "Loss"
|
||||
return NotImplemented
|
||||
|
||||
def __le__(self, other):
|
||||
if isinstance(other, PokerHand):
|
||||
return self < other or self == other
|
||||
return NotImplemented
|
||||
|
||||
def __gt__(self, other):
|
||||
if isinstance(other, PokerHand):
|
||||
return not self < other and self != other
|
||||
return NotImplemented
|
||||
|
||||
def __ge__(self, other):
|
||||
if isinstance(other, PokerHand):
|
||||
return not self < other
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self):
|
||||
return object.__hash__(self)
|
228
project_euler/problem_54/test_poker_hand.py
Normal file
228
project_euler/problem_54/test_poker_hand.py
Normal file
|
@ -0,0 +1,228 @@
|
|||
import os
|
||||
from itertools import chain
|
||||
from random import randrange, shuffle
|
||||
|
||||
import pytest
|
||||
|
||||
from .sol1 import PokerHand
|
||||
|
||||
SORTED_HANDS = (
|
||||
"4S 3H 2C 7S 5H",
|
||||
"9D 8H 2C 6S 7H",
|
||||
"2D 6D 9D TH 7D",
|
||||
"TC 8C 2S JH 6C",
|
||||
"JH 8S TH AH QH",
|
||||
"TS KS 5S 9S AC",
|
||||
"KD 6S 9D TH AD",
|
||||
"KS 8D 4D 9S 4S", # pair
|
||||
"8C 4S KH JS 4D", # pair
|
||||
"QH 8H KD JH 8S", # pair
|
||||
"KC 4H KS 2H 8D", # pair
|
||||
"KD 4S KC 3H 8S", # pair
|
||||
"AH 8S AS KC JH", # pair
|
||||
"3H 4C 4H 3S 2H", # 2 pairs
|
||||
"5S 5D 2C KH KH", # 2 pairs
|
||||
"3C KH 5D 5S KH", # 2 pairs
|
||||
"AS 3C KH AD KH", # 2 pairs
|
||||
"7C 7S 3S 7H 5S", # 3 of a kind
|
||||
"7C 7S KH 2H 7H", # 3 of a kind
|
||||
"AC KH QH AH AS", # 3 of a kind
|
||||
"2H 4D 3C AS 5S", # straight (low ace)
|
||||
"3C 5C 4C 2C 6H", # straight
|
||||
"6S 8S 7S 5H 9H", # straight
|
||||
"JS QS 9H TS KH", # straight
|
||||
"QC KH TS JS AH", # straight (high ace)
|
||||
"8C 9C 5C 3C TC", # flush
|
||||
"3S 8S 9S 5S KS", # flush
|
||||
"4C 5C 9C 8C KC", # flush
|
||||
"JH 8H AH KH QH", # flush
|
||||
"3D 2H 3H 2C 2D", # full house
|
||||
"2H 2C 3S 3H 3D", # full house
|
||||
"KH KC 3S 3H 3D", # full house
|
||||
"JC 6H JS JD JH", # 4 of a kind
|
||||
"JC 7H JS JD JH", # 4 of a kind
|
||||
"JC KH JS JD JH", # 4 of a kind
|
||||
"2S AS 4S 5S 3S", # straight flush (low ace)
|
||||
"2D 6D 3D 4D 5D", # straight flush
|
||||
"5C 6C 3C 7C 4C", # straight flush
|
||||
"JH 9H TH KH QH", # straight flush
|
||||
"JH AH TH KH QH", # royal flush (high ace straight flush)
|
||||
)
|
||||
|
||||
TEST_COMPARE = (
|
||||
("2H 3H 4H 5H 6H", "KS AS TS QS JS", "Loss"),
|
||||
("2H 3H 4H 5H 6H", "AS AD AC AH JD", "Win"),
|
||||
("AS AH 2H AD AC", "JS JD JC JH 3D", "Win"),
|
||||
("2S AH 2H AS AC", "JS JD JC JH AD", "Loss"),
|
||||
("2S AH 2H AS AC", "2H 3H 5H 6H 7H", "Win"),
|
||||
("AS 3S 4S 8S 2S", "2H 3H 5H 6H 7H", "Win"),
|
||||
("2H 3H 5H 6H 7H", "2S 3H 4H 5S 6C", "Win"),
|
||||
("2S 3H 4H 5S 6C", "3D 4C 5H 6H 2S", "Tie"),
|
||||
("2S 3H 4H 5S 6C", "AH AC 5H 6H AS", "Win"),
|
||||
("2S 2H 4H 5S 4C", "AH AC 5H 6H AS", "Loss"),
|
||||
("2S 2H 4H 5S 4C", "AH AC 5H 6H 7S", "Win"),
|
||||
("6S AD 7H 4S AS", "AH AC 5H 6H 7S", "Loss"),
|
||||
("2S AH 4H 5S KC", "AH AC 5H 6H 7S", "Loss"),
|
||||
("2S 3H 6H 7S 9C", "7H 3C TH 6H 9S", "Loss"),
|
||||
("4S 5H 6H TS AC", "3S 5H 6H TS AC", "Win"),
|
||||
("2S AH 4H 5S 6C", "AD 4C 5H 6H 2C", "Tie"),
|
||||
("AS AH 3H AD AC", "AS AH 2H AD AC", "Win"),
|
||||
("AH AC 5H 5C QS", "AH AC 5H 5C KS", "Loss"),
|
||||
("AH AC 5H 5C QS", "KH KC 5H 5C QS", "Win"),
|
||||
("7C 7S KH 2H 7H", "3C 3S AH 2H 3H", "Win"),
|
||||
("3C 3S AH 2H 3H", "7C 7S KH 2H 7H", "Loss"),
|
||||
("6H 5H 4H 3H 2H", "5H 4H 3H 2H AH", "Win"),
|
||||
("5H 4H 3H 2H AH", "5H 4H 3H 2H AH", "Tie"),
|
||||
("5H 4H 3H 2H AH", "6H 5H 4H 3H 2H", "Loss"),
|
||||
("AH AD KS KC AC", "AH KD KH AC KC", "Win"),
|
||||
("2H 4D 3C AS 5S", "2H 4D 3C 6S 5S", "Loss"),
|
||||
("2H 3S 3C 3H 2S", "3S 3C 2S 2H 2D", "Win"),
|
||||
("4D 6D 5D 2D JH", "3S 8S 3H TC KH", "Loss"),
|
||||
("4S 6C 8S 3S 7S", "AD KS 2D 7D 7C", "Loss"),
|
||||
("6S 4C 7H 8C 3H", "5H JC AH 9D 9C", "Loss"),
|
||||
("9D 9H JH TC QH", "3C 2S JS 5C 7H", "Win"),
|
||||
("2H TC 8S AD 9S", "4H TS 7H 2C 5C", "Win"),
|
||||
("9D 3S 2C 7S 7C", "JC TD 3C TC 9H", "Loss"),
|
||||
)
|
||||
|
||||
TEST_FLUSH = (
|
||||
("2H 3H 4H 5H 6H", True),
|
||||
("AS AH 2H AD AC", False),
|
||||
("2H 3H 5H 6H 7H", True),
|
||||
("KS AS TS QS JS", True),
|
||||
("8H 9H QS JS TH", False),
|
||||
("AS 3S 4S 8S 2S", True),
|
||||
)
|
||||
|
||||
TEST_STRAIGHT = (
|
||||
("2H 3H 4H 5H 6H", True),
|
||||
("AS AH 2H AD AC", False),
|
||||
("2H 3H 5H 6H 7H", False),
|
||||
("KS AS TS QS JS", True),
|
||||
("8H 9H QS JS TH", True),
|
||||
)
|
||||
|
||||
TEST_FIVE_HIGH_STRAIGHT = (
|
||||
("2H 4D 3C AS 5S", True, [5, 4, 3, 2, 14]),
|
||||
("2H 5D 3C AS 5S", False, [14, 5, 5, 3, 2]),
|
||||
("JH QD KC AS TS", False, [14, 13, 12, 11, 10]),
|
||||
("9D 3S 2C 7S 7C", False, [9, 7, 7, 3, 2]),
|
||||
)
|
||||
|
||||
TEST_KIND = (
|
||||
("JH AH TH KH QH", 0),
|
||||
("JH 9H TH KH QH", 0),
|
||||
("JC KH JS JD JH", 7),
|
||||
("KH KC 3S 3H 3D", 6),
|
||||
("8C 9C 5C 3C TC", 0),
|
||||
("JS QS 9H TS KH", 0),
|
||||
("7C 7S KH 2H 7H", 3),
|
||||
("3C KH 5D 5S KH", 2),
|
||||
("QH 8H KD JH 8S", 1),
|
||||
("2D 6D 9D TH 7D", 0),
|
||||
)
|
||||
|
||||
TEST_TYPES = (
|
||||
("JH AH TH KH QH", 23),
|
||||
("JH 9H TH KH QH", 22),
|
||||
("JC KH JS JD JH", 21),
|
||||
("KH KC 3S 3H 3D", 20),
|
||||
("8C 9C 5C 3C TC", 19),
|
||||
("JS QS 9H TS KH", 18),
|
||||
("7C 7S KH 2H 7H", 17),
|
||||
("3C KH 5D 5S KH", 16),
|
||||
("QH 8H KD JH 8S", 15),
|
||||
("2D 6D 9D TH 7D", 14),
|
||||
)
|
||||
|
||||
|
||||
def generate_random_hand():
|
||||
play, oppo = randrange(len(SORTED_HANDS)), randrange(len(SORTED_HANDS))
|
||||
expected = ["Loss", "Tie", "Win"][(play >= oppo) + (play > oppo)]
|
||||
hand, other = SORTED_HANDS[play], SORTED_HANDS[oppo]
|
||||
return hand, other, expected
|
||||
|
||||
|
||||
def generate_random_hands(number_of_hands: int = 100):
|
||||
return (generate_random_hand() for _ in range(number_of_hands))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hand, expected", TEST_FLUSH)
|
||||
def test_hand_is_flush(hand, expected):
|
||||
assert PokerHand(hand)._is_flush() == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hand, expected", TEST_STRAIGHT)
|
||||
def test_hand_is_straight(hand, expected):
|
||||
assert PokerHand(hand)._is_straight() == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hand, expected, card_values", TEST_FIVE_HIGH_STRAIGHT)
|
||||
def test_hand_is_five_high_straight(hand, expected, card_values):
|
||||
player = PokerHand(hand)
|
||||
assert player._is_five_high_straight() == expected
|
||||
assert player._card_values == card_values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hand, expected", TEST_KIND)
|
||||
def test_hand_is_same_kind(hand, expected):
|
||||
assert PokerHand(hand)._is_same_kind() == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hand, expected", TEST_TYPES)
|
||||
def test_hand_values(hand, expected):
|
||||
assert PokerHand(hand)._hand_type == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hand, other, expected", TEST_COMPARE)
|
||||
def test_compare_simple(hand, other, expected):
|
||||
assert PokerHand(hand).compare_with(PokerHand(other)) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hand, other, expected", generate_random_hands())
|
||||
def test_compare_random(hand, other, expected):
|
||||
assert PokerHand(hand).compare_with(PokerHand(other)) == expected
|
||||
|
||||
|
||||
def test_hand_sorted():
|
||||
POKER_HANDS = [PokerHand(hand) for hand in SORTED_HANDS]
|
||||
list_copy = POKER_HANDS.copy()
|
||||
shuffle(list_copy)
|
||||
user_sorted = chain(sorted(list_copy))
|
||||
for index, hand in enumerate(user_sorted):
|
||||
assert hand == POKER_HANDS[index]
|
||||
|
||||
|
||||
def test_custom_sort_five_high_straight():
|
||||
# Test that five high straights are compared correctly.
|
||||
pokerhands = [PokerHand("2D AC 3H 4H 5S"), PokerHand("2S 3H 4H 5S 6C")]
|
||||
pokerhands.sort(reverse=True)
|
||||
assert pokerhands[0].__str__() == "2S 3H 4H 5S 6C"
|
||||
|
||||
|
||||
def test_multiple_calls_five_high_straight():
|
||||
# Multiple calls to five_high_straight function should still return True
|
||||
# and shouldn't mutate the list in every call other than the first.
|
||||
pokerhand = PokerHand("2C 4S AS 3D 5C")
|
||||
expected = True
|
||||
expected_card_values = [5, 4, 3, 2, 14]
|
||||
for _ in range(10):
|
||||
assert pokerhand._is_five_high_straight() == expected
|
||||
assert pokerhand._card_values == expected_card_values
|
||||
|
||||
|
||||
def test_euler_project():
|
||||
# Problem number 54 from Project Euler
|
||||
# Testing from poker_hands.txt file
|
||||
answer = 0
|
||||
script_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
poker_hands = os.path.join(script_dir, "poker_hands.txt")
|
||||
with open(poker_hands, "r") as file_hand:
|
||||
for line in file_hand:
|
||||
player_hand = line[:14].strip()
|
||||
opponent_hand = line[15:].strip()
|
||||
player, opponent = PokerHand(player_hand), PokerHand(opponent_hand)
|
||||
output = player.compare_with(opponent)
|
||||
if output == "Win":
|
||||
answer += 1
|
||||
assert answer == 376
|
Loading…
Reference in New Issue
Block a user