Search code examples
pythonclassplaying-cards

Creating a card game on python by using Classes and Functions - EDIT


I'm learning Python programming and i'm trying to make a working popular card game.

I'm starting from the skeleton, so I wrote some code to create the deck and to deal cards and shuffle the deck. Now I want to create 2 players that "have" a hand, in which they can keep a maximum of 3 cards, and can "draw" cards from the deck. I thought about a player class, in which are defined the name and hand attributes for each different player, but when i tried to draw cards with player1 it also added the cards to player2's hand. How can i change it so it adds the cards to a a player and only to him, removing them from the deck? What is wrong with my solution?

Output:

['10 di Denari', '1 di Coppe']
['10 di Denari', '1 di Coppe', '6 di Denari', '5 di Denari']

Can you help me understanding how to get it to work?

EDIT: Yeah i pasted the wrong code, here is the one i need help in:

import random
semi = ['Bastoni','Spade','Coppe','Denari']
numeri = [1,2,3,4,5,6,7,8,9,10]
mazzo = []

for element in numeri:
    for seme in semi:
        carta = str(element) + ' di ' + seme
        mazzo.append(carta)
Rimanenti = len(mazzo)
def mischia():
    random.shuffle(mazzo)
class Giocatore:
    nome = None
    mano = []
    tola = []
    def __init__(self,nome):
        self.nome=nome

    def draw(q):
        for n in range(0, q):
            pesco = random.choice(mazzo)
             Giocatore.mano.append(pesco)
            mazzo.remove(pesco)

    def turno():
        Giocatore.draw('Toni',1)
        Giocatore.draw('Piero',1)
    def inizio():
        Giocatore.draw('Toni', 3)
        Giocatore.draw('Piero', 3)


class Piero(Giocatore):
    nome = 'Piero'

class Toni(Giocatore):
    nome = 'Toni'


Toni.draw(2)
print(Toni.mano)
Piero.draw(2)
print(Piero.mano)

ANOTHER EDIT:

Thank you all for your answers! Now I have a better understanding of the whole thing, I'm rewriting it differently!


Solution

  • I think the main problem is that you are misunderstanding the difference between class variables and instance variables. I'll use a simple example:

    class Player():
        hand = [] # hand is a class variable, as it is not tied to an instance
    
    p1 = Player() # p1 is an instance of Player
    p2 = Player() # p2 is a separate instance of Player()
    

    Now, if I add something to the hand of p1:

    p1.hand.append(1)
    
    p2.hand
    # [1]
    

    This is because I have defined hand at the class level, and so both p1.hand and p2.hand are really Player.hand. To change this, you will want hand to be attached to an instance, using self.hand and the __init__ dunder method, which acts like a constructor for class instances.

    class Player():
        def __init__(self): # self is always passed as the first argument
            self.hand = []
    
    p1 = Player() 
    p2 = Player()
    

    Now, p1.hand is a completely different object than p2.hand because they were created through each call to __init__. Now if I add something to p1.hand:

    p1.hand.append(1)
    p1.hand
    # [1]
    p2.hand
    # []
    

    They aren't both modified. Now, to touch on class functions. By default, functions in classes are instance level, which means that self, or the class instance, will be implicitly passed as the first argument. If you don't have a placeholder for self, you will get errors:

    class A():
        def a():
            print("did something")
    
    inst = A()
    a.a()
    # TypeError: a() takes 0 positional arguments but 1 was given
    
    # To fix this
    class A():
        def a(self):
            print("did something")
    
    inst = A()
    a.a()
    # did something
    

    The Program

    Fortunately, you've identified what you need for this program to function properly, two players, a deck of cards, and a mechanism to take turns. Because you are using random.choice to pick cards, I'd argue that you don't actually need a function to shuffle the deck. You can get the card by choosing a random card as you've done before:

    semi = ['Bastoni','Spade','Coppe','Denari']
    numeri = [1,2,3,4,5,6,7,8,9,10]
    mazzo = ['%d di %s' % (element, seme) for seme in semi for element in numeri]
    

    To draw a card, you can use the following:

    # Use random.choice as you've done before
    idx = random.coice(mazzo)
    mazzo.remove(drawn_card)
    

    The class method remove will remove a value from the list. Paired with your class.hand approach:

    Toni = Player()
    
    Toni.hand.append(drawn_card)
    

    Or, as a method in the Player class:

    class Player():
        def __init__(self):
            self.hand = []
    
        def draw(self):
            # Need to convert set to list to choose
            drawn_card = random.choice(mazzo)
            mazzo.remove(drawn_card)
            self.hand.append(drawn_card)
            print(self.hand) # If you want to see the hand
    
    toni = Player()
    
    toni.draw()
    # ['8 di Spade']
    

    Though you could view that as a bit verbose. If you wanted to keep your shuffle method, then you could shuffle the deck every time and use pop to grab a random card as @ibonyun suggested