0

I am currently working on a texas hold'em poker game project and got stuck with my new card dealing class:

import random

cardnames = ['2C', '2D', '2H', '2S', '3C', '3D', '3H', '3S',
             '4C', '4D', '4H', '4S', '5C', '5D', '5H', '5S',
             '6C', '6D', '6H', '6S', '7C', '7D', '7H', '7S',
             '8C', '8D', '8H', '8S', '9C', '9D', '9H', '9S',
             '10C', '10D', '10H', '10S', '11C', '11D', '11H', '11S',
             '12C', '12D', '12H', '12S', '13C', '13D', '13H', '13S',
             '14C', '14D', '14H', '14S']

class Card_Dealer():
    def __init__(self):
        self.cards = cardnames
        self.cards_available = cardnames.copy()

        self.dealing_to_hands()
        self.dealing_to_table()


    def dealing_to_hands(self):
        self.playercards = []
        self.p1_cards = []
        self.p2_cards = []
        self.p3_cards = []
        self.p4_cards = []
        hands = [self.playercards, self.p1_cards,
                      self.p2_cards, self.p3_cards,
                      self.p4_cards]

        for hand in hands:
            card = random.choice(self.cards_available)
            hand[0][1] = card
            hand[0][0] = self.cards.index(card)
            self.cards_available.remove(card)
            card = random.choice(self.cards_available)
            hand[1][1] = card
            hand[1][0] = self.cards.index(card)
            self.cards_available.remove(card)


    def dealing_to_table(self):
        self.cardsontable = {1: None, 2: None, 3: None, 4: None, 5: None}
        for a in self.cardsontable:
            card_name = random.choice(self.cards_available)
            card_ind = self.cards.index(card_name)
            card = (card_ind, card_name)
            self.cardsontable[a] = card
            self.cards_available.remove(card_name)

My first problem is, that when I try to call a players cards (e.g. p1_cards) I get this error message:

Traceback (most recent call last):
  File "C:/Users/Buza Dani/Documents/Programming/Projects/Texas_Holdem/poker_cards.py", line 229, in <module>
    print(Card_Dealer().p1_cards)
  File "C:/Users/Buza Dani/Documents/Programming/Projects/Texas_Holdem/poker_cards.py", line 195, in __init__
    self.dealing_to_hands()
  File "C:/Users/Buza Dani/Documents/Programming/Projects/Texas_Holdem/poker_cards.py", line 211, in dealing_to_hands
    hand[0][1] = card

IndexError: list index out of range

The second problem is, that if I will be able to fix the first problem, each time, I'll call a variable from the class, they will change because during the initialization the lists will grow and because of the random, the actual variables, from which I call one, will be different every time.

I need some help fixing these problems with some explanation, since I'm a very beginner.

danbuza
  • 144
  • 1
  • 1
  • 10
  • 2
    Consider _what_ `hand` is when you hit the line `hand[0][1] = card`. Then, what is `hand[0]`? what's `hand[0][1]` then? Now, what does the error message say, and how does it relate to that? – Green Cloak Guy Sep 02 '20 at 20:20
  • 2
    If you're trying to deal out a deck of cards, I would recommend: instead of _choosing a random element_, just _shuffle the list_ with `random.shuffle()` and then take the first or last element every time, with `.pop()`. Consider using `hand.append()` to then add the card, instead of trying to specify an index directly. – Green Cloak Guy Sep 02 '20 at 20:22

2 Answers2

0

Let's take a look at the method in question:

def dealing_to_hands(self):
    self.playercards = []
    self.p1_cards = []
    self.p2_cards = []
    self.p3_cards = []
    self.p4_cards = []
    hands = [self.playercards, self.p1_cards,
                  self.p2_cards, self.p3_cards,
                  self.p4_cards]

    for hand in hands:
        card = random.choice(self.cards_available)
        hand[0][1] = card

You start with 5 empty lists, then you place them in a list called hands. Then you iterate over that mega-list. But those individual lists are still empty. Each hand is an empty list so hand[0] fails and hand[0][1] also fails (you can't access the first entry of an empty list). Instead, use .append() to append to each list. Something like:

for hand in hands:
    card = random.choice(self.cards_available)
    hand.append(card)

I think that I ma ynot understand your second question. Don't you want the lists to be filled with random cards? Each time that you call dealing_to_hands() and dealing_to_table() you're setting various variables to some original state. It does look like cards_available needs to be refreshed from time to time, but you can do that with a new method that always refreshes the available list of cards prior to dealing:

def deal():
    self.cards_available = cardnames.copy()
    self.dealing_to_hands()
    self.dealing_to_table()
Michael
  • 2,344
  • 6
  • 12
0

I made some edits with a lot of comments to your snippet:

# so-63712958.py

import random
    
class Card_Dealer():
    # You may define either card names as a class constant or
    # you pass it as an argument to your __init__ function
    CARDNAMES  = ['2C', '2D', '2H', '2S', '3C', '3D', '3H', '3S',
             '4C', '4D', '4H', '4S', '5C', '5D', '5H', '5S',
             '6C', '6D', '6H', '6S', '7C', '7D', '7H', '7S',
             '8C', '8D', '8H', '8S', '9C', '9D', '9H', '9S',
             '10C', '10D', '10H', '10S', '11C', '11D', '11H', '11S',
             '12C', '12D', '12H', '12S', '13C', '13D', '13H', '13S',
             '14C', '14D', '14H', '14S']

    def __init__(self):
        self.cards = cardnames
        self.cards_available = self.CARDNAMES.copy()

        # You may set the initial value of these instance
        # variables into the __init__ function.
        # Your previous implementation won't allow you
        # to deal two hands

        self.playercards = []
        self.p1_cards = []
        self.p2_cards = []
        self.p3_cards = []
        self.p4_cards = []
        self.hands = [self.playercards, self.p1_cards,
                      self.p2_cards, self.p3_cards,
                      self.p4_cards]

        # You could rethink your game with a variable number of players.
        # In that case, you can create a dict with n keys and default
        # empty list. Your players dict will correspond to the current
        # self.hands
        # You will later add another function to change cards which will
        # inspect for content for player at index n and replace in the
        # list the card(s)

        # You may shuffle cards in the __init__ since the dealer
        # is not shuffling them every time he/she puts them on
        # the table or give one to a player

        # https://stackoverflow.com/questions/10048069
        random.shuffle(self.cards_available)
        self.dealing_to_hands()

        # dealing_to_table() happens only once at __init__
        # you may not need to define it into a separate function
        # since the implementation would be quite trivial

        # I am not sure to understand why you wanted cardsontable to be a dict
        self.cardsontable = []

        for _ in range(5):
            self.cardsontable.append(self.cards_available.pop())

    def dealing_to_hands(self):
        for hand in self.hands:
            # Deal two cards to each player
            for _ in range(2):
                 hand.append(self.cards_available.pop())

if __name__ == "__main__":
    cd = Card_Dealer()
    print("Cards on the table: ", cd.cardsontable)
    print("Player 1 cards: ", cd.p1_cards)

This is the output of the suggestion:

$ python3 so-63712958.py 
Cards on the table:  ['5C', '12S', '7H', '7C', '2S']
Player 1 cards:  ['2C', '13C']

I would choose a dictionary data structure to store a variable number of players and store the cards on the table in a list (the opposite of what you did actually). Your functions would look like

def __init__(self, n_players):
    self.cards = cardnames
    self.cards_available = self.CARDNAMES.copy()

    # Initialize an dict with a given number of players
    # and an empty list as default value
    self.players = dict.fromkeys(range(n_players))

    random.shuffle(self.cards_available)

    # Deal two cards to each player
    for p_index in self.players:
        self.players[p_index] = []
        for _ in range(2):
            self.players[p_index].append(self.cards_available.pop())

    self.cardsontable = []
    for _ in range(5):
        self.cardsontable.append(self.cards_available.pop())

def get_card_for_player(self, p_index):
    return self.players[p_index]

def change_card_for_player(self, p_index, c_index):
    # where c_index is 0, 1
    self.players[p_index].pop(c_index)
    self.players[p_index].append(self.cards_available.pop())

This simple program below will generate the following output

cd = Card_Dealer(4) # 4 players game
print("Cards on the table: ", cd.cardsontable)
print("Players cards: ", cd.players)
cd.change_card_for_player(0, 0) # first player change first card
print("Player 1 cards: ", cd.get_card_for_player(0))

Output:

Cards on the table:  ['2S', '8S', '13C', '13H', '5C']
Players cards:  {0: ['4C', '9D'], 1: ['4D', '14H'], 2: ['11H', '10D'], 3: ['10S', '9H']}
Player 1 cards:  ['9D', '12C']

Is this close to what you are expecting to use for the next steps your exercise?

mabe02
  • 2,676
  • 2
  • 20
  • 35
  • @danbuza please don't hesitate to ask for clarifications if it is not clear. – mabe02 Sep 02 '20 at 21:16
  • the only thing that isn't clear by now is if there's a way for the variables in the __init__ method (e.g. p1_cards) to stay the same every time I call the class? – danbuza Sep 06 '20 at 09:51