Python Simple Card Game to Learn Classes

23,016

This is more of a code/approach review. A card game is a case for composition, not inheritance; the Deck contains Cards, but isn't in itself a type of Card, and vice versa.

I think you are duplicating information in the Card. Just store suit and rank, and use __str__ to create 'x of y'. You can also implement the rich comparison methods:

class Card(object):

    FACES = {11: 'Jack', 12: 'Queen', 13: 'King', 14: 'Ace'}

    def __init__(self, rank, suit):
        self.suit = suit
        self.rank = rank

    def __str__(self):
        value = self.FACES.get(self.rank, self.rank)
        return "{0} of {1}".format(value, self.suit)

    def __lt__(self, other):
        return self.rank < other.rank

Now e.g. str(Card(13, 'Clubs')) == "King of Clubs". This way you don't duplicate the rank and value in card.

Next, I think the Deck should incorporate the population generation in __init__; you can provide optional arguments for a non-standard deck. I have included two implementations; the commented-out version is a list comprehension using itertools to do the same job in one line. I also provide a function to pick n different random cards from self.deck.

from itertools import product 
import random

class Deck(object):

    def __init__(self, ranks=None, suits=None):
        if ranks is None:
            ranks = range(2, 15)
        if suits is None:
            suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
##        self.deck = [Card(r, s) for r, s in product(ranks, suits)]
        self.deck = []
        for r in ranks:
            for s in suits:
                self.deck.append(Card(r, s))

    def deal(self, n):
        return random.sample(self.deck, n)

Now the game is simple; you can deal three cards per hand, and compare the cards naturally (using e.g. <) because of the comparison methods.

deck = Deck()
hand = deck.deal(3)
print(" - ".join(map(str, hand)))
if min(hand[0], hand[1]) < hand[2] < max(hand[0], hand[1]):
    print("Winner!")
else:
    print("Loser.")
Share:
23,016
pez
Author by

pez

Updated on July 11, 2021

Comments

  • pez
    pez almost 3 years

    I am trying to create a simple card game to better understand OOP and classes.

    The game is as follows: Two cards are dealt from a deck. Then a third card is dealt. If the third card is between the first two cards, then the player wins. If the third card is equal to either of the first two cards, or is outside of the set, then the player loses.

    This is what I have so far:

    class Deck(object):
        def __init__(self):
            self.deck = []
    
        def PopulateDeck(self):
            suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
            for suit in suits:
                for rank in range(2, 15):
                    if rank == 11:
                        value = "Jack"
                    elif rank == 12:
                        value = "Queen"
                    elif rank == 13:
                        value = "King"
                    elif rank == 14:
                        value = "Ace"
    
                    self.deck.append(str(value) + " of " + suit)
    
    class Card(object):
        def __init__(self, rank, value):
            self.rank = rank
            self.value = value
            self.card = self.rank + self.value
    

    I am having a difficult time with classes and OOP, and I'm not sure if this is a good start, or where I should go next. Much of this was created by reading other sources and examples. Can anyone please provide advice on what other classes I may want to make to run my game, and how those classes may interact with/inherit from each other? Thank you.

  • Anton
    Anton about 10 years
    I think this answer might be a little bit too complex for somebody who doesn't yet have a solid grasp of how to finish his/her game.
  • jonrsharpe
    jonrsharpe about 10 years
    You have a TypeError in Card.__init__ and a NameError in Card.deal. Also, there are only 13 cards per suit (2-10, JQKA).
  • Farmer Joe
    Farmer Joe about 10 years
    You should really take a little more care with the code you provide, I know you made a cautionary note, but it won't be very helpful if the code you provide does not work. I made several edits to fix some outstanding errors.
  • jonrsharpe
    jonrsharpe about 10 years
    @farmerjoe I think card = random.choice(remaining_cards) saves you some lines and makes the process clearer.
  • Farmer Joe
    Farmer Joe about 10 years
    @jonrsharpe agreed, I think that is a better call, but I was more playing damage control on this answer than providing my own methodology, I felt my edits reflected the intent Andrey had
  • jonrsharpe
    jonrsharpe about 10 years
    @Andrey this is fairly straightforward; if the OP wants to learn Python OOP, getting to grips with magic methods is crucial.
  • jonrsharpe
    jonrsharpe about 10 years
    @farmerjoe ah, I see. Note that Card.__eq__ needs to be implemented for card in self.dealt to work, though, and that randint(a, b) includes b
  • Farmer Joe
    Farmer Joe about 10 years
    @jonrsharpe Indeed! Good catch! Serves me right for being so nit picky in the first place haha
  • jonrsharpe
    jonrsharpe about 10 years
    @farmerjoe classic application of Muphry's law!