I'm trying to pass a class object as an argument to a global function.
Here's the function:
def CreatePlayers(p1_Name, p2_Name, cardDeck):
#Function to Create Players
#Takes 3 variables: Names of player 1 and player 2 and the card deck
#Returns a List of players [p1,p2]
print("Creating Players... \n")
print(f"Dealing a deck of ", len(cardDeck), " among 2 players")
player1 = Player(p1_Name)
player2 = Player(p2_Name)
#Share cards between players
for i in range(25):
player1.addCard(cardDeck.dealOne())
player2.addCard(cardDeck.dealOne())
print("Verify... player creation\n")
print(player1)
print(player2)
return [player1, player2]
The class object here's "cardDeck", and the class object is initialized before making the function call with the variable name, of course
And here's the class definition:
class Deck:
'''A Python Deck class. Holds a list of Card objects
Possesses the following
* Attributes: - myDeck (A list of cards. Expected = 50 cards in deck)
* Methods: - Constructor (creates list)
- Shuffle
- Deal a card
- return number of cards
'''
def __init__(self):
'''Method to initialize a deck'''
self.myDeck = []
#initialize cards
for rank in Ranks:
for order in Orders:
self.myDeck.append( Card(order, rank) )
##other functions....
def __len__(self):
'''Return the size of the card deck'''
return len(self.myDeck)
This is where I call my createPlayer() function:
myDeck = Deck().shuffle()
#Create my players
players = CreatePlayers("Adam", "Bob", myDeck)
And finally here's the error that I keep getting while running the 'createPlayer' function
File "/home/CardGame.py", line 32, in CreatePlayers
print(f"Dealing a deck of ", len(cardDeck), " among 2 players")
TypeError: object of type 'NoneType' has no len()
Deck().shuffle() doesn't return the deck
you can do this to solve it :
myDeck = Deck();
myDeck.shuffle();
players = CreatePlayers("Adam", "Bob", myDeck)
an other alternative is to change shuffle to be a class method :
class Deck:
def __init__(self):
...
#classmethod
def shuffle(cls):
new_deck = cls()
# Do shuffle here for new_deck
return new_deck
and use it that way :
myDeck = Deck.shuffle();
players = CreatePlayers("Adam", "Bob", myDeck)
but that way you can't shuffle an existing deck so it depend on what you want to do.
Related
Question: I am attempting to make a blackjack game. I am at the part where I am trying to make a player class with a hand size which contains an empty list. My intuition tells me that I need to be targeting the deck where the cards are and to use the append and the pop methods. I have already written a function in another class called the deal cards method. Am I able to use functions from other classes inside a class? Or do I have to come up with some way of my class targeting the deck and getting the cards?
# Work on the player class give them the ability to have a hand and to deal the
# cards into that hand
from random import shuffle
class Card:
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __str__(self):
return str(self.rank) + " of " + self.suit
class Deck:
deck = []
def __init__(self):
suits = "Hearts", "Diamonds", "Clubs", "Spades"
ranks = 2,3,4,5,6,7,8,9,10,"J","Q","K","A"
for suit in suits:
for rank in ranks:
card= Card(rank,suit)
self.deck.append(card)
# need to be able to deal cards
def deal_card(self):
dealt_card = self.deck.pop()
return dealt_card # return gives back the value to whomever called it.
# needs to be able to shuffle
def shuffle(self):
shuffle(self.deck)
# display the deck
def display(self):
for card in self.deck:
print(card)
class Player:
def __init__(self, name, isdealer):
self.name = name
self.hand = []
self.isdealer = isdealer
def draw_cards(self):
player_cards = dealt_card();
#scratchpad thinking area
# self.hand is an empty list. I want to get cards from the deck and put them in that list! SO I need to target
# the deck somehow.
# maybe with pop?
# I would use append to add the result to my hand.
# Work on the player class give them the ability to have a hand and to deal the
# cards into that hand
def main(): # main can be called blackjack or gameplay
# Welcome the player and explain the rules
print("""Welcome to Blackjack! Here are the Rules
Try to get as close to 21 without going over.
Kings, Queens, and Jacks are worth 10 points.
Aces are worth 1 or 11 points.
Cards 2 through 10 are worth their face value.
(H)it to take another card.
(S)tand to stop taking cards.
The dealer stops hitting at 17""")
# Run a game of blackjack
# create a deck of cards outside of the main.
deck = Deck()
deck.shuffle()
deck.display()
# Make player 1 and the dealer
# while True:
# return cards to the deck
# Shuffle the deck of cards close to the start to start a new game.
# Deal 2 cards to the players
# Loop: display hands and scores
# Ask them to hit or stand.
# Determine Winner
# If the program is run (instead of imported), run the game:
if __name__ == '__main__':
main()
I have tried using the function from the deck class but I am unsure I can use methods from other classes. I know that append can be used to add to strings. I know I need to target the deck somehow but it is in another class.
Am I able to use functions from other classes inside a class? Or do I have to come up with some way of my class targeting the deck and getting the cards?
There are several ways to approach this dilemma:
You could have a method in the Deck class that takes a player instance as argument. It then has access to both the deck and the player's hand
You could have a method in the Player class that takes the deck instance as argument. This is similar, but approaching it from the opposite side
You could create yet another class, like BlackJack that would have attributes for the deck and the players, and then its methods can access both deck and player without issue. This looks like a nicer solution, but it seems less compatible with the main function you have. It would mean that all of the logic in main would move into that new class.
In below code I have taken the first approach in the deal method.
I also took some other decisions:
Make Deck a subclass of list, because a deck is mostly like a list with maybe just one or two extra methods. This means a deck instance no longer needs a deck attribute, but can apply pop, append, ..etc directly to self.
Make Player a subclass of Deck, because a player is mostly like a deck (a "hand") with a name and a score. This means there is no more hand attribute, as self is that hand.
Add a value attribute to the Card class, which would calculate the value of the card (1 for Ace, ...etc) at construction.
Add a score method to the Player class which sums up the card values, adding 10 if the hand includes at least one ace and that would not bust the score.
I also implemented the game loop with hitting/standing/quitting, and reporting the winner
Maybe some of this might not be acceptable for the code challenge you might be taking, but I thought these were the right thing to do:
from random import shuffle
class Card:
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
# Add a value to use in the blackjack scoring
self.value = rank if isinstance(rank, int) else (1 if rank == "A" else 10)
def __str__(self):
return f"{self.rank} of {self.suit}"
class Deck(list): # Inherit from list as a deck is mostly a list
def __init__(self):
suits = "Hearts", "Diamonds", "Clubs", "Spades"
ranks = 2,3,4,5,6,7,8,9,10,"J","Q","K","A"
for suit in suits:
for rank in ranks:
card = Card(rank, suit)
self.append(card)
def deal(self, player): # Pass player as argument so to act on deck & player
player.append(self.pop())
return player[-1]
def collect(self, player):
self.extend(player)
player.clear()
def __str__(self):
return ", ".join(map(str, self))
class Player(Deck): # Inherit from Deck, as a player really is a deck with a name
def __init__(self, name, isdealer):
self.name = name
self.isdealer = isdealer
def score(self):
total = 0
hasAce = False
for card in self:
total += card.value
hasAce = hasAce or card.value == 1
return total + 10 if hasAce and total < 12 else total
def __str__(self):
return f"{self.name} has {self.score()} ({super().__str__()})"
def main():
# Welcome the player and explain the rules
print("""Welcome to Blackjack! Here are the Rules
Try to get as close to 21 without going over.
Kings, Queens, and Jacks are worth 10 points.
Aces are worth 1 or 11 points.
Cards 2 through 10 are worth their face value.
(H)it to take another card.
(S)tand to stop taking cards.
The dealer stops hitting at 17
""")
deck = Deck()
# Make player 1 and the dealer
player = Player(input("What's your name? "), False)
dealer = Player("Dealer", True)
while True:
# return cards to the deck
deck.collect(player)
deck.collect(dealer)
shuffle(deck)
# Deal 2 cards to the players
deck.deal(player)
deck.deal(player)
deck.deal(dealer)
deck.deal(dealer)
# Loop: display hands and scores
print()
print("New game:")
print(dealer)
print(player)
# Ask them to hit or stand.
while player.score() <= 21:
choice = input(f"{player.name}, do you want to [h]it, [s]tand or [q]uit? ").upper()
if choice == "Q":
return
if choice == "S":
break
if choice == "H":
print(f"{player.name}, you receive {deck.deal(player)}")
print(player)
while dealer.score() <= 16:
print(f"{dealer.name} receives {deck.deal(dealer)}")
print(dealer)
# Determine Winner
if player.score() > 21 or player.score() <= dealer.score() <= 21:
print(f"{dealer.name} won this round")
else:
print(f"{player.name}, you won this round!")
if __name__ == '__main__':
main()
I am working on a texas hold-em game in python, and am looking to traverse an array containing a complete hand of 7 cards (2 in the hole + 5 on the board). The array contains elements of class Cards, where the Card class constructor is
class Card:
def __init__(self, suit, val):
self.suit = suit
self.value = val
So, I have a "hand" array within a "Player" class of 7 random cards, where the suit is one of 4 strings (spade, club, heart, diamond) and the value is one of 9 numbers (2-10) or 4 strings (jack-ace). I want to traverse the array to check if the list contains any of the hands in poker, but I'm failing to figure out how I can "extract" the suit/value of the cards from my array. I've started a method within my "Player" class to check for a suit, here that suit is a spade.
class Player:
def __init__(self, name):
self.name = name
self.pocket = []
self.hand = []
def spadeChecker(self):
card = Card("Blank", 0)
for i in self.hand:
card = self.hand[i]
if(card.suit == "Spade"):
print("Hi! you have a spade!")
else:
pass
When running the program from my terminal I receive a TypeError message:
in spadeChecker card = self.hand[i] TypeError: list indices must be integers or slices, not Card
I know my method is pretty bad but I'm very new to this and just can't figure out how to get it to work. Any advice?
Thanks
Here is the rewritten method, including an example with a 3 card hand.
class Card:
def __init__(self, suit, val):
self.suit = suit
self.value = val
class Player:
def __init__(self, name):
self.name = name
self.pocket = []
self.hand = [Card("Heart", 10), Card("Spade", 10), Card("Diamond", 10)]
def spadeChecker(self):
for card in self.hand:
if(card.suit == "Spade"):
print("Hi! you have a spade!")
#return True
else:
#return False
pass
p = Player("Bob")
p.spadeChecker()
Output is:
Hi! you have a spade!
In your code, your iterator is i and your iterable is self.hand, which is a list of Card objects. Thus, i will be a Card object each time the loop iterates. If you want to be able to get the index of the Card object as well as the Card object itself, I recommend using the enumerate() function. However, since you only reference the Card objects, you can just get the suit attribute directly.
class Player:
def __init__(self, name):
self.name = name
self.pocket = []
self.hand = []
def spadeChecker(self):
card = Card("Blank", 0)
for card_obj in self.hand:
if(card_obj.suit == "Spade"):
print("Hi! you have a spade!")
else:
pass
There's a Python library for generating, comparing & evaluating poker hands called tilted which can be found here: https://github.com/MaxAtkinson/tilted
I've used it myself before and it's really easy. This may allow you to avoid having to implement it yourself.
I am trying to add card c to deck d but I can't print the deck instead I keep getting <__main__.card at >
class Card():
def __init__(self,theName,theMoves):
self.name = theName
self.moves = list(theMoves)
class Deck():
theCards = []
def __init__(self):
pass
def addCard(self,theCard):
Deck.theCards.append(theCard)
print(Deck.theCards)
#adding card to my deck
c = Card("sh",["fire",258])
d = Deck()
d.addCard(c)
You're not referring to the specific class instance in your class functions. Change your Deck class definition as follows:
class Deck():
def __init__(self):
self.theCards = []
def addCard(self, theCard):
self.theCards.append(theCard)
print(self.theCards)
You should iterate over your cards and call vars() on them, because printing a list of objects just shows you only certain memory addresses but no properties. Instead of:
print(Deck.theCards)
use a loop similar to this one:
for card in Deck.theCards:
print(vars(card))
which prints all variables and their corresponding values of an object. I also assume you want to have different decks of different players, but your code currently saves the list of cards within the class definition. Thusly all objects of type Deck you create with the Deck() call are sharing the same list of cards. Lets say you add another deck:
#adding card to my deck
print("player 1")
c = Card("sh",["fire",258])
d = Deck()
d.addCard(c)
print("player 2")
c2 = Card("it",["water",258])
d2 = Deck()
d2.addCard(c2)
The deck of player 2 also includes the cards of player 1 and vice versa. The printed output of the suggested code extension:
player 1
{'name': 'sh', 'moves': ['fire', 258]}
player 2
{'name': 'sh', 'moves': ['fire', 258]}
{'name': 'it', 'moves': ['water', 258]}
The user 'insert random' already offered a solution before. To get two different decks with two different lists of cards you have to store the card list(s) within the class instance, not the class definition. Here the whole updated code:
class Card():
def __init__(self,theName,theMoves):
self.name = theName
self.moves = list(theMoves)
class Deck():
def __init__(self):
self.theCards = []
def addCard(self,theCard):
self.theCards.append(theCard)
for card in self.theCards:
print(vars(card))
#adding card to my deck
print("player 1")
c = Card("sh",["fire",258])
d = Deck()
d.addCard(c)
print("player 2")
c2 = Card("it",["water",258])
d2 = Deck()
d2.addCard(c2)
Note that self is used instead of Deck to store a different card list for every new deck created with the Deck() call. This list is created by the initial class constructor function not by the class definition itself.
Console output:
player 1
{'name': 'sh', 'moves': ['fire', 258]}
player 2
{'name': 'it', 'moves': ['water', 258]}
Which is I guess what you want.
Version with dictionaries:
#adding card to my deck
print("player 1")
c = {'name': 'sh', 'moves':["fire",258] }
d = { 'theCards': [c] }
print(list(card for card in d['theCards']))
print("player 2")
c2 = {'name': 'it', 'moves':["water",258] }
d2 = { 'theCards': [c2] }
print(list(card for card in d2['theCards']))
By default, when Python prints a list it prints the repr of the list's elements. For many objects, including custom classes, the default repr looks like
<__main__.MyClass object at 0x7f00ff75fd00>
You can override this behaviour by defining a __repr__ method on your classes. Conventionally, the output of __repr__ should be a string that would produce your object if it were passed to eval.
For the card class, it would look like this:
class Card:
def __init__(self, theName, theMoves):
self.name = theName
self.moves = list(theMoves)
def __repr__(self):
return "Card('{}', {})".format(self.name, self.moves)
Giving this output to your script (once you have fixed the indentation):
[Card('sh', ['fire', 258])]
You can also define a __str__ method to customise how your object appears when str(myobject) is called. If __str__ is not defined Python will use __repr__ instead.
You seem to have an indentation error. Your declaration of c, d and d.addCard(c) should be spaced indented back once. The same needs to go for everything inside your Card and Deck class.
class Card():
def __init__(self,theName,theMoves):
self.name = theName
self.moves = list(theMoves)
class Deck():
theCards = []
def __init__(self):
pass
def addCard(self,theCard):
Deck.theCards.append(theCard)
print(Deck.theCards)
#adding card to my deck
c = Card("sh",["fire",258])
d = Deck()
d.addCard(c)
Originally I had 2 files, one named "cards" and one named "decks". The cards file contain the definition of the cards class and was imported into the "decks" file. In the decks files was the definition of the deck object. After defining the deck object, I would test the class in the lines below by typing something like "deck = deck()" and everything would work.
After verifying everything, I wanted to move the deck definition into the "cards" file, to create a library that would contain both the "card" and "deck" class definitions. However after doing this, running "deck = deck()" failed, giving the following error. This happens even if I run the "deck = deck()" line in the bottom of the cards file, or if I import cards an run in a separate file.
"card = card(name = name_of_card,suit = card_suit,value = 0)
UnboundLocalError: local variable 'card' referenced before assignment"
Below is the cards file code:
import random
class card:
def __init__(self, name, suit, value):
self.name = name
self.suit = suit
self.value = value
class deck:
def __init__(self, contents = [], num_cards = 0):
self.contents = contents
self.num_cards = num_cards
self.generate()
def generate(self):
card_values = ['Ace', *range(2,11,1),'Jack', 'Queen', 'King']
suits = ['Hearts','Clubs','Diamonds','Spades']
for i in suits:
card_suit = str(i)
for n in card_values:
name_of_card = str(n) + " of " + card_suit
card = card(name = name_of_card,suit = card_suit,value = 0)
self.contents.append(card)
self.num_cards += 1
def shuffle(self):
random.shuffle(self.contents)
def print_contents(self):
for i in self.contents:
print(i.name)
def draw_card(self):
card_drawn = self.contents[0]
self.contents.pop(0)
return card_drawn
def reveal(self,num_to_reveal = 1,from_top = True):
for i in range(0,num_to_reveal,1):
if from_top == True:
print(self.contents[i].name)
else:
print(self.contents[-1-i].name)
def return_to_deck(self,card, position = 0):
self.contents.insert(position,card)
deck = deck()
You're using the same name for the class card and for the object you create for it. Your code will work if you use different names. Typically classes have names starting with a capitol letter, so I'd suggest class Card and then later, card = Card(name=name_of_card,suit=card_suit,value = 0).
Create a class Deck that represents a deck of cards. Your class should have the following methods:
constructor: creates a new deck of 52 cards in standard order.
shuffle: randomnizes the order of the cards.
dealCard: returns a single card from the top of the deck and removes the card from the deck
cardsLeft: returns the number of cards remaining in the deck.
Test your program by having it deal out a sequence of n cards from a shuffle deck where n is the user input.
class Deck:
def __init__(self):
self.cardList=[]
for suit in ["d","c","h","s"]:
for rank in range(1,14):
card=PlayingCard(suit, rank)
self.cardList.append(card)
def shuffle(self):
#I DON'T KNOW HOW TO SHUFFLE MY CARDS PLEASE HELP.
#self.cardList[pos1] = self.cardList[pos2]
#self.cardList[pos2] = self.cardList[pos1]
#these two lines above are not working
def dealCard(self):
return self.cardList.pop()
def cardsLeft(self):
return len(self.cardList)
Read the docs on random.shuffle. It should help you greatly! :)
from collections import namedtuple
from random import shuffle
PlayingCard = namedtuple('PlayingCard', "suit rank")
class Deck:
def __init__(self):
self.cardList = [PlayingCard(suit, rank) for suit in"dchs" for rank in range(1,14)]
def shuffle(self):
shuffle(self.cardList)
def dealCard(self):
return self.cardList.pop()
def cardsLeft(self):
return len(self.cardList)
d = Deck()
d.shuffle()
print [d.dealCard() for _ in range(5)]