Search code examples
pythonfunctionclassmethodsblackjack

Python Blackjack Project Type Error: Chips.place_bet() missing 1 required positional argument


I am trying to write my first python project blackjack. I was advised to write the program with each class and its methods in a different file so that the code would be more manageable. I divided my code up into smaller files and used import statements.

When I run my code I get the following error message:

Chips.place_bet() missing 1 required positional argument. self.

I thought self was self explanatory. I do not need to put a value for self. I am wondering if this is an import issue.

I am trying to get my place_bet function to activate so that I can ask the user to input the number of chips for a bet. Can anyone advise me on how to do this?

I feel this question is different from

My code is spread over these files:

blackjack.py

import random

from player import Player
from hand import Hand
from chips import Chips
from deck import Deck
from card import Card
# import helper_functions

# Blackjack/
# ├── app/
# │   ├── card.py
# │   ├── chips.py
# │   ├── deck.py
# │   ├── hand.py
# │   ├── player.py
# │   ├── main.py
# │   ├── helper_functions.py

# Gameplay and Outline of Project

# Welcome message and rules
print("Welcome to Blackjack! The rules for blackjack can be found here. https://www.youtube.com/watch?v=PljDuynF-j0")
print("The object of this blackjack game is to make it to 100 chips. You will start with 25 chips.")

# Establish player name
print("Please insert your player name: ")
player_name = input()
print('Please insert your player name ' + player_name)
print("Lets get started" + player_name + "please place your bet!")
# Place the bets BEFORE the cards are dealt. Pure risk.
place_bet()
# .  Deal 2 cards to user and dealer
# . print a message explaining the total hand value of the user. ask player to hit/stay?
# . If the player busts print player busts. if the player is under 21 ask them again to hit or stay.
# . If stay, move on to dealers turn.
# . If the hand is less than 17 have him hit, if it is over 17 have the dealer stay.
# . If the dealer stays print the dealers hand total. IF the dealers hand total is greater than the player the dealer wins.
# . if the dealer busts print the message he busts and the player wins and the player gets the chips.

# 1. Ask dealer to hit/stay?
# 2. If hit, deal 2 cards
# 3. If broke or blackjack deliver message
# 4. If stay, compare hands
# 5. If users hand is closer to 21, (user wins)
# 6. If dealers hand is closer to 21, (user loses)


def main():
    player = Player('strikeouts27', 500,)
    dealer = Player()

    player_chips = Chips()
    dealer_chips = Chips()

    cards = Card()
    deck = Deck()


if __name__ == '__main__':
    main()

card.py

class Card:
    """Card class"""

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

    def __repr__(self):
        return f'{self.__class__.__name__}(rank={self.rank}, suit={self.suit})'

class Chips:
    """Chip class manages chip balance"""

    def __init__(self, balance):
        self.balance = balance
        self.bet = 0

    def place_bet(self):
        while True:
            total = input(f'How much do you bet? (1-{self.balance}):\n> ')
            # return True if a digit string. false otherwise. hover over the method for visual studio code to show you.
            if not total.isdigit():
                continue
            total = int(total)
            if total > self.balance:
                print('You do not have enough')
                continue
            return total

    def add_value(self, value):
        """Add value to chip total"""
        self.balance += value

    def deduct_value(self, value):
        """Deduct value from chip total"""
        self.balance -= value

    def display_chips(player, dealer):
        print(f'player chips: ${player.chips.balance}')
        print(f'dealer chips: ${dealer.chips.balance}')
            # Displays player and dealer chip count

class.py

class Card:
    #"""Card class"""

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

    def __repr__(self):
        return f'{self.__class__.__name__}(rank={self.rank}, suit={self.suit})'

deck.py

class Deck:
    """Deck class, represents 52 cards"""

    def __init__(self):
        self.ranks = [str(n) for n in range(2, 11)] + list('jqka')
        self.suits = ["hearts", "diamonds", "clubs", "spades"]
        self.cards = [Card(rank, suit)
                      for suit in self.suits for rank in self.ranks]
        random.shuffle(self.cards)

    # dunder method rewrites what pythons default method to show better information about itself.
    def __repr__(self):
        return f'{self.__class__.__name__}({self.cards})'

    def __len__(self):
        return len(self.cards)

    def __getitem__(self, item):
        return self.cards[item]

    def shuffle(self):
        """shuffles deck of cards"""
        random.shuffle(self.cards)

hand.py

class Hand:
    """Hand class to hold the cards for the player and the dealer"""

    def __init__(self, cards=None):
        self.cards = [] if cards is None else cards
        self.value = 0
        self.aces = 0

    def add_to_hand(self, card):
        """Adds a cards to self.cards."""
        self.cards.append(card)

    def display_hand(self):
        hashtable = {'hearts': 9829, 'diamonds': 9830,
                     'spades': 9824, 'clubs': 9827}
        return [[i.rank, chr(hashtable.get(i.suit))] for i in self.cards]

    def display_hand_two(player, dealer):
        # Displays player and dealer hand
        print(
            f'players Hand: {player.hand.display_hand()} -->  total {player.total}')
        print(
            f'dealer Hand: {dealer.hand.display_hand()} -->  total {dealer.total}')

player.py

class Player:
    """Player class"""

    def __init__(self, name, chips, hand):
        self.name = name
        self.chips = chips
        self.hand = hand

    @property
    def total(self):
        """Returns the value of the cards. Face cards equal 10, aces can equal
        11 or 1, this function picks best case"""

        value = 0
        aces_count = 0
        # Each card is a tuple in a list:
        cards = [card for card in self.hand.cards]

        for card in cards:
            if card.rank == 'a':
                aces_count += 1
            elif card.rank in 'kqj':
                # Face cards are worth 10 points
                value += 10
            else:
                # Numbered cards are worth their value.
                value += int(card.rank)
        # Add value of aces - 1 per ace
        value += aces_count
        for i in range(aces_count):
            # If another 10 can be added,then do it!
            if value + 10 <= 21:
                value += 10

        return value

    @staticmethod
    def get_move():
        """returns player choice to hit or stand"""
        move = input('>>> (H)it, (S)tand ?:\n> ').lower()
        return move


def main():
    # Instantiate Deck
    deck = Deck()

    # shuffle deck
    deck.shuffle()

    # Instantiate player and dealer chips
    player_chips = Chips(500)
    dealer_chips = Chips(500)

    while True:
        # Instantiate player and dealer hand
        player_hand = Hand()
        dealer_hand = Hand()

        # Instantiate player and dealer
        player = Player('seraph', player_chips, player_hand)
        dealer = Player('codecademy', dealer_chips, dealer_hand)

        # Check if player has enough money
        if player_chips.balance <= 0:
            print('need more money')

        # Then place bet
        bet = player_chips.place_bet()

        # player draws 2 cards
        player.hand.add_to_hand(deck.cards.pop())
        player.hand.add_to_hand(deck.cards.pop())

        # Dealer draws cards
        dealer.hand.add_to_hand(deck.cards.pop())
        dealer.hand.add_to_hand(deck.cards.pop())

        # display player ann dealers hand
        display_hand(player, dealer)

        # Begin game
        while True:
            if player.total > 21:
                break

            #  Get the player's moves
            player_move = player.get_move()
            if player_move == 'h':
                new_card = deck.cards.pop()
                rank, suit = new_card.rank, new_card.suit
                player.hand.add_to_hand(new_card)
                print(f'You drew a {rank} of {suit}')
                display_hand(player, dealer)
                if player.total > 21:
                    print('You busted...')
                    # Busted
                    continue
            if player_move == 's':
                break

        # Get the dealer's moves
        if dealer.total <= 21 and not player.total > 21:
            while dealer.total <= 17:
                print('Dealer hits...')
                new_card = deck.cards.pop()
                dealer.hand.add_to_hand(new_card)
                if dealer.total > 21:
                    break

        if dealer.total > 21:
            print('Dealer Busted - You win!')
            display_hand(player, dealer)
            player.chips.add_value(bet)
            dealer.chips.deduct_value(bet)
            display_chips(player, dealer)

        elif player.total > 21 or player.total < dealer.total:
            print('You lost!')
            display_hand(player, dealer)
            player.chips.deduct_value(bet)
            dealer.chips.add_value(bet)
            display_chips(player, dealer)

        elif player.total == dealer.total:
            print("It's a tie! Money is returned")


if __name__ == '__main__':
    main()

I feel that this question is different than this referenced article because we are dealing with multiple files and the article that I am referencing here is only talking about one file.

referenced article: Why do I get "TypeError: Missing 1 required positional argument: 'self'" trying to call a method from the class directly?


Solution

  • A few issues:

    • The file class.py is redundant. It is not imported, and only defines a class that is already defined in card.py.

    • There are two main function definitions. One in blackjack.py and one in player.py. The first one defines a player 'strikeouts27', while the other defines 'seraph', 'codecademy'. The second one seems the more developed one, and uses more than what is in player.py, and so it should actually be moved into blackjack.py.

    • Related to the previous point: blackjack.py has code that executes outside of any function. This counters the pattern where you put the driver code in a function main. It is better to move all relevant driver code inside one main function that is placed in blackjack.py. But still, that code looks unfinished, and the code already present in main is much more developed. I think you only need the code in main (the larger one).

    • Related to the previous point: that code in blackhack.py calls a function place_bet which is not defined. There is a method with that name, but not a standalone function with that name. For calling the method you first need an instance of Chips, so like I mentioned in the previous bullet, I would suggest to just remove this code, including also the input that is made there. Maybe you just want to keep the introductory print statements that are made there, and move those into main.

    • random is used in deck.py, but it is imported in blackjack.py. It would make deck.py more self-contained, if you would move that import statement to the deck.py file.

    • display_hand_two should not be a method of the Hand class. It doesn't even expect a self argument of the type Hand. Instead this should be a standalone function in blackjack.py.

    • In main you have several calls like display_hand(player, dealer), but display_hand is not a standalone function, but a method. What you really wanted to do here, is call display_hand_two(player, dealer), and for that to work you must make display_hand_two a standalone function as mentioned in the previous bullet point.

    If you implement all those changes, your code will run.