How can I compare the values of two playing cards when they have a value and a suit? Python3

后端 未结 4 491
傲寒
傲寒 2020-12-07 05:42

I am trying to make a text based object oriented card game. Two players draw a card each from a deck of cards, and the player with the strongest card wins. I have four class

相关标签:
4条回答
  • 2020-12-07 06:22

    Here is a sketch of an approach. You can easily combine this with your own approach, the biggest change being for the Card class. Here, I've used namedtuple to make a Card class, but your current class can simply wrap a tuple value:

    import enum
    from functools import total_ordering
    from collections import namedtuple
    
    @total_ordering
    class OrderedEnum(enum.Enum):
        def __lt__(self, other):
            if isinstance(other, type(self)):
                return self.value < other.value
            return NotImplemented
    
    Rank = OrderedEnum('Rank', ['one', 'two', 'three', 'four', 'five', 'six',
                        'seven', 'eight', 'nine', 'jack', 'queen','king', 'ace'])
    
    Suit = OrderedEnum('Suit', ['clubs', 'diamonds', 'hearts', 'spades'])
    
    Card = namedtuple('Card', ['rank', 'suit'])
    
    c1 = Card(Rank.four, Suit.clubs)
    c2 = Card(Rank.four, Suit.spades)
    c3 = Card(Rank.ace, Suit.diamonds)
    

    Now, in action:

    >>> c1
    Card(rank=<Rank.four: 4>, suit=<Suit.clubs: 1>)
    >>> c2
    Card(rank=<Rank.four: 4>, suit=<Suit.spades: 4>)
    >>> c1 < c2
    True
    >>> c1 > c3
    False
    

    Tuple sorting is lexicographic! Nice!

    >>> hand = [c2, c1, c3]
    >>> hand
    [Card(rank=<Rank.four: 4>, suit=<Suit.spades: 4>), Card(rank=<Rank.four: 4>, suit=<Suit.clubs: 1>), Card(rank=<Rank.ace: 13>, suit=<Suit.diamonds: 2>)]
    >>> sorted(hand)
    [Card(rank=<Rank.four: 4>, suit=<Suit.clubs: 1>), Card(rank=<Rank.four: 4>, suit=<Suit.spades: 4>), Card(rank=<Rank.ace: 13>, suit=<Suit.diamonds: 2>)]
    >>>
    

    Note, I've used the total_ordering decorator, which is simply a shortcut, and indeed, I think it might be better to simply do the whole class by hand. Here's a recipe.

    EDIT So, to elaborate, here is how I would implement your Card and Deck classes. Notice how much more readable your code becomes when you use the enum and namedtuple.

    import enum
    from functools import total_ordering
    from collections import namedtuple
    from random import shuffle 
    
    @total_ordering
    class OrderedEnum(enum.Enum):
        def __lt__(self, other):
            if isinstance(other, type(self)):
                return self.value < other.value
            return NotImplemented
    
    Rank = OrderedEnum('Rank', ['one', 'two', 'three', 'four', 'five', 'six',
                        'seven', 'eight', 'nine', 'jack', 'queen','king', 'ace'])
    Suit = OrderedEnum('Suit', ['clubs', 'diamonds', 'hearts', 'spades'])
    CardValue = namedtuple('CardValue', ['rank', 'suit'])
    
    @total_ordering
    class Card(object):
        def __init__(self, rank, suit):
            self.value = CardValue(rank, suit)
        def __repr__(self):
            return "Card({:s}, {:s})".format(self.value.rank, self.value.suit)
        def __lt__(self, other):
            if isinstance(other, __class__):
                return self.value < other.value
            return NotImplemented
        def __eq__(self, other):
            if isinstance(other, __class__):
                return self.value == other.value
            return NotImplemented
    
    class Deck(object):
        def __init__(self):
            self.cards = []
            for rank in Rank:
                for suit in Suit:
                    self.cards.append(Card(rank, suit))
            shuffle(self.cards)
    

    Now, in action:

    >>> deck = Deck()
    >>> c1 = deck.cards.pop()
    >>> c2 = deck.cards.pop()
    >>> c1
    Card(Rank.queen, Suit.hearts)
    >>> c2
    Card(Rank.king, Suit.clubs)
    >>> c1 == c2
    False
    >>> c1 > c2
    False
    >>> c1 < c2
    True
    >>> c1.value
    CardValue(rank=<Rank.queen: 11>, suit=<Suit.hearts: 3>)
    >>> c2.value
    CardValue(rank=<Rank.king: 12>, suit=<Suit.clubs: 1>)
    

    Also, notice that __repr__ should try to represent the object, if you want a pretty message, use __str__. See this question

    0 讨论(0)
  • 2020-12-07 06:24

    An enumeration (https://docs.python.org/3.5/library/enum.html) would be appropriate. For rich comparisons (and ordering) you should also consider implementing some or all of the __eq__, __ne__, __lt__, __le__, __gt__, __ge__ methods (from https://docs.python.org/3/reference/datamodel.html) on the Card class.

    0 讨论(0)
  • 2020-12-07 06:37

    You could implement operators for your Card class __gt__(), __lt__(), etc ...

    than you can use a number of standard library functions like max() to determine the higher value card or deck and could even use sort() to simply sort a 'hand' for example a list [Card, Card, ...].

    0 讨论(0)
  • 2020-12-07 06:37

    I would recommend you store the value of each card as an int, so you can compare them, and not to use strings such as "King" or "Ace". You can do this and change repr() to print a human readable version using those strings.

    The Card class could look like this:

    class Card(object):
         suits = ["Clubs", "Diamonds", "Hearts", "Spades"] #list of suits
         values = [None, "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"] #list of card values
    
         def __init__(self, suit= 0, value= 2):
             """
             Initializes card
             :param suit: Suit of card int value 0-3
             :param value: Value of card int value 0-13
             """
             self.suit = suit
             self.value = value
    
         def __str__(self):
             """
             Returns a readable format of the card
             """
             return "%s of %s" %(Card.values[self.value],
                                 Card.suits[self.suit])
    

    Notice how the value of the card is stored as an int all the time.

    In the Game class you could have a function that compares two cards, I'm not sure how you want to do this but it could look something like this:

    def compare(card1, card2):
         """
         Compares the value of two cards and returns the greater of the two
         :param card1: A card object
         :param card2: A second card object
         :return: The higher value card, if tie returns 0
         """
         if card1.value > card2.value:
             return card1
         elif card2.value == card1.value:
             return 0
         else:
             return card2
    
    0 讨论(0)
提交回复
热议问题