I'm working right now on Project Euler problem 54, and I figured that this was the perfect opportunity to try to work with classes. This is my first time, so I'm sure that there are a lot of style, efficiency or functionality errors. I would like some insights for that.
The Class is initiated with a string consisting of the cards, i.e., '2C AC 8H 9D 9S', and the Hand inherits several variables that I'm interested in:
Hand.rank and Hand.rank_val # String with the rank name and numerical value of the rank Hand.cards and Hand.num_vals # Lists of cards and numerical value of the cards Hand.max_val # Value of the highest card in the rank Hand.rest_vals # Rest of values for the cards that are not the highest in the rank There are also the variables:
all_vals, order_vals, all_ranks and order_ranks that give the custom sort order for the cards and the ranks.
It works as I want it to, at least for all the tests that I've given it (which were all that I could think of). I haven't implemented a test to check if the hand is impossible (repeated or non existing cards), but I may do it in the future. The next step is to create a process that compares two hands and gives the winner. Should I do a function or a Class method?
Anyway, here is the code of my program. It's a bit long, so thank you to anyone who takes some time to read it:
from collections import Counter class Hand: '''Class representing the rank of a hand in poker.''' # Possible card values all_vals = [ '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A'] # Custom order for the values order_vals = dict(zip(all_vals, range(len(all_vals)))) # Possible ranks (including empty/unknown rank) all_ranks = ['', 'High Card', 'One Pair', 'Two Pairs', 'Three of a Kind', 'Straight', 'Flush', 'Full House', 'Four of a Kind', 'Straight Flush', 'Royal Flush'] # Custom order for the ranks order_ranks = dict(zip(all_ranks, range(len(all_ranks)))) def __init__(self, cards): self.cards = cards.split(' ') self.vals_and_suits() self.calculate_rank() def vals_and_suits(self): self.vals = [card[0] for card in self.cards] # Ordered values of the cards with custom sort self.vals.sort(key=lambda val: self.order_vals[val]) # Numeric values of the cards with custom sort self.num_vals = [self.order_vals[val] for val in self.vals] # Suits of the cards self.suits = [card[1] for card in self.cards] def calculate_rank(self): def flush_and_straight(): '''Checks all combinations of flush and straight.''' # Generate new list with range lowest and highest numeric values min_max = list(range(self.num_vals[0], self.num_vals[4] + 1)) # If the list hasn't changed, cards are consecutive consecutive = (self.num_vals == min_max) # And we also need to consider the case of a wheel straight (1*) # where the card values are 2, 3, 4, 5 and Ace wheel_num_vals = [0, 1, 2, 3, 12] wheel = (self.num_vals == wheel_num_vals) # Straight if the cards are consecutive or wheel if consecutive or wheel: self.rank = 'Straight' # If all cards from the same suit if (len(set(self.suits)) == 1): # Flush if not Straight if self.rank != 'Straight': self.rank = 'Flush' # Add cards different than max to compare between flushes self.rest_vals += self.num_vals[:-1] else: # Straight Flush if smallest card is not a 10 if self.num_vals[0] != 8: self.rank = 'Straight Flush' # Royal Flush otherwise else: self.rank = 'Royal Flush' # If any value has been specified for rank if self.rank != "": if wheel: # Value for the wheel self.max_val = 3 else: # Value for anything else self.max_val = self.num_vals[-1] # Value for the current rank self.rank_val = self.order_ranks[self.rank] def repeated_cards(): # Counter for the repeated values of the cards count_num_vals = Counter(self.num_vals) # Returns a list [(value, count),(value, count),...] order by count popular = count_num_vals.most_common() # Most common values most_popular = popular[0] # Only combinations for len == 2 are 4-1 and 3-2 if len(popular) == 2: # Four of a Kind if the most common count is equal to four if most_popular[1] == 4: self.rank = 'Four of a Kind' print(self.rest_vals, popular) self.rest_vals += [popular[1][0]] # Full House if it is equal to three else: self.rank = 'Full House' self.rest_vals += [popular[1][0]]*2 self.max_val = most_popular[0] self.rank_val = self.order_ranks[self.rank] # Only if the rank is less than Straight if self.rank_val <= 4: # Three of a Kind if most common is equal to three if most_popular[1] == 3: self.rank = 'Three of a Kind' self.max_val = most_popular[0] self.rest_vals += [popular[1][0]] + [popular[2][0]] # Two Pairs if first and second most common are equal to two elif popular[0][1] == 2 and popular[1][1] == 2: self.rank = 'Two Pairs' self.max_val = popular[1][0] # Add card not in pairs and lowest valued pair (that order) self.rest_vals += sorted([popular[2][0]]+[popular[0][0]]*2) # One Pair if previous test failed and most common is two elif popular[0][1] == 2: self.rank = 'One Pair' self.max_val = popular[0][0] # Add the remaining cards self.rest_vals += [popular[1][0]] \ + [popular[2][0]] + [popular[3][0]] # High Card if all tests until now have failed else: self.rank = 'High Card' self.max_val = self.num_vals[-1] # Add the remaining cards self.rest_vals += self.num_vals[:-1] self.rank_val = self.order_ranks[self.rank] # Init rank name, max_val, rank_val and rest_vals # Name of the rank self.rank = '' # Value of the rank self.rank_val = -1 # Should change eventually # Max card value for particular rank self.max_val = -1 # Should change eventually # Excess of card values not in particular rank self.rest_vals = [] flush_and_straight() # Not RF or SF if self.rank_val <= 8: repeated_cards() if __name__ == '__main__': # tests hand1 = Hand('5C 2H 2C AD AC') print("This hand is a", hand1.rank, "with rank value", hand1.rank_val) print("The cards are", hand1.cards, "and their values are", hand1.num_vals) print("The highest card value in the rank is", hand1.max_val) print("The remaining values in the hand are", hand1.rest_vals) ''' #-------# # Notes # #-------# (1*) If there is a wheel straight, self.vals are ['2', '3', '4', '5', 'A'] and self.num_vals are [0, 1, 2, 3, 4, 12] '''
if self.rank != ""be replaced byif self.rank? \$\endgroup\$