Creating a card game with Python classes - python

I am trying to practice programming classes in Python by creating a card game. Right now what I want to achieve is to get a player to draw a card from the deck. I have the code as follows:
class Deck():
def __init__(self):
#create the deck
self.deck = []
self.discard_pile = []
def create_deck(self):
#assign the number of cards for each type to a card (dict)
deck_stats = {"A":4, "B":6, "C":5, "D":5, "E":5, "F":5, "G":5, "H":5, "I":5, 'J':5}
for card in deck_stats.keys():
for i in range(0,deck_stats[card]):
self.deck.append(card)
return self.deck
def shuffle(self):
#randomise the deck or for when the shuffle card is played
random.shuffle(self.deck)
return self.deck
def pickup(self):
#picks up the first card on the draw pile
picked_up = self.deck.pop(0)
print(picked_up)
return picked_up
And the player class:
class Player(Deck):
def __init__(self):
self.player_hand = ["defuse"]
for i in range(6):
self.draw_card()
def draw_card(self):
#draw pile reduces by one
deck = Deck()
deck.create_deck()
deck.shuffle()
self.player_hand.append(deck.pickup())
return self.player_hand
In the draw_card()method from the Player class I've called the pickup method from the Deck class. Which I believe is the wrong thing to do but I'm not sure how else to pickup a card from the Deck object.
Also, the draw_card method obviously doesn't work the way it's supposed to as it is creating a new deck every time and then picking up from the new deck (at least that's what I think it is doing right now). This brings me back to my original question, how do I get a player to pickup a card from the same Deck such that I don't need to create a new Deck every time?

Try something like
class Deck():
def __init__(self):
# create the deck
self.discard_pile = []
self.deck = self.create_deck()
self.shuffle()
def create_deck(self):
deck = []
# assign the number of cards for each type to a card (dict)
deck_stats = {"A": 4, "B": 6, "C": 5, "D": 5, "E": 5, "F": 5, "G": 5, "H": 5, "I": 5, 'J': 5}
for card in deck_stats.keys():
for i in range(0, deck_stats[card]):
deck.append(card)
return deck
def shuffle(self):
# randomise the deck or for when the shuffle card is played
random.shuffle(self.deck)
return self.deck
def pickup(self):
# picks up the first card on the draw pile
picked_up = self.deck.pop(0)
print(picked_up)
return picked_up
class Player:
def __init__(self):
self.player_hand = ["defuse"]
self.deck = Deck()
for i in range(6):
self.draw_card()
def draw_card(self):
# draw pile reduces by one
self.player_hand.append(deck.pickup())
return self.player_hand

Related

Why is my nested loop only picking up some items?

I'm trying to get the loop to go through all suits then all of the card numbers to create a deck of cards but it seems to be pulling alternating values. I have the range set at the bottom as 26 because I was getting out of range errors going any higher.
import random
# actual deck creation using Card class
class Deck:
def __init__(self):
suits = ["Spades", "Clubs", "Hearts", "Diamonds"]
numbers = {"two": ["Two", 2], "three": ["Three", 3], "four": ["Four", 4], "five": ["Five", 5],
"six": ["Six", 6],
"seven": ["Seven", 7], "eight": ["Eight", 8], "nine": ["Nine", 9], "ten": ["Ten", 10],
"jack": ["Jack", 10], "queen": ["Queen", 10], "king": ["King", 10], "ace": ["Ace", 1]}
self.cards = []
for suit in suits:
for number in numbers.values():
self.cards.append(Card(suit, number))
def shuffledeck(self):
random.shuffle(self.cards)
return self.cards
def cleardeck(self):
self.cards = []
return self.cards
class Card:
def __init__(self, suit, value):
self.suit = suit
self.value = value
# card print statement
def __str__(self):
return self.value + " of " + self.suit
class Game:
def __init__(self):
self.deck = []
def play(self):
self.deck = Deck()
#self.deck.shuffledeck()
game = Game()
game.play()
#testing area
blah = []
for i in range(26):
card = (game.deck.cards.pop(i))
print(card.suit)
blah.append(card.value)
print(str(blah))
You are popping an item at a specific index, while increasing the index; this will not exhaust all the items in game.deck.cards. Try instead, for example,
for i in range(52):
card = (game.deck.cards.pop())
Or pop(0) if you want to preserve the order, although this is said to be slower than pop(). If you really don't need to empty game.deck.cards, then the following might be simpler:
for card in game.deck.cards:
print(card.suit)
blah.append(card.value)

Popping and appending values from one class object to another

I'm trying to create the card game War, where two players draw a card, and the player with the higher card rank "appends" the card from the opponent. Here is my code so far:
import random
class Card(object):
def __init__(self, val):
self.value = val
def __str__(self):
return("{}".format(self.value))
def show(self):
print("{}".format(self.value))
class Deck:
def __init__(self):
self.cards = []
self.build()
def build(self):
for v in {'2' : 0, '3' : 1, '4' : 2, '5' : 3, '6' : 4, '7' : 5, '8' : 6, '9' : 7 , '10' : 8, 'Jack' : 9, 'Queen' : 10, 'King' : 11, 'Ace' : 12}:
for i in range(0,2): #suits removed; only prints half of a deck
self.cards.append(Card(v))
def divide(self):
global half
half = len(self.cards)//2
def show(self):
for c in self.cards:
c.show()
def shuffle(self):
for i in range(len(self.cards)-1, 0, -1):
r = random.randint(0, i)
self.cards[i], self.cards[r] = self.cards[r], self.cards[i]
def drawCard(self):
return self.cards.pop()
class Player:
def __init__(self, name):
self.name = name
self.hand = []
def draw(self,deck):
self.hand.append(deck.drawCard())
return self
def showHand(self):
for card in self.hand:
print(f"{self.name} drew a {card}!")
def discard(self, opponent):
print("Card discarded!")
self.hand.pop()
self.hand.appendleft(opponent)
deck1 = Deck()
deck2 = Deck()
deck1.shuffle()
deck2.shuffle()
deck1.show()
print()
deck2.show()
print(input("This is a card game known as War"))
print(input("It is a luck-based game where you have to annihilate your opponent's deck"))
print(input("Each player draws a card, and whoever gets the higher value gets the card"))
print(input("Otherwise both players simultaneously draw four extra cards, where the final card determines the higher value, otherwise keep drawing four cards until a higher value is chosen"))
print(input("The winner is the one who gets all of their opponent's cards. Ready, begin!"))
player1 = Player(input('Player 1, pick a name: '))
player2 = Player(input('Player 2, Pick a name: '))
player1.draw(deck1)
player2.draw(deck2)
player1.showHand()
player2.showHand()
player1.discard(player2)
So I want to create the method within the Player class by utilizing stacks and queues. However, my issue is that I am not sure how appending and popping list indices would work between two objects (player1 and player2) and every method I tried so far resulted in an attribute error. Any ways to fix that?

Python random shuffle not working in my even though it should

Hello guys first time posting here.
I have a problem i cant explain. I am trying to replicate poker but my deck shuffle function isnt working
First my code
class Start(object):
openCards = []
def __init__(self, numofplayer):
print("started")
finished = False
self.deck = Deck()
self.deck.shuffle()
for card in self.deck:
card.isshowing = True
print(card)
self.players = []
i = 0
while i < numofplayer:
player = Player()
self.players.append(player)
i = i + 1
class Deck(list):
def __init__(self):
self.cards = []
suits = ["Herz", "Piek", "Karo", "Kreuz"]
values = {
"zwei": 2,
"drei": 3,
"vier": 4,
"fünf": 5,
"sechs": 6,
"sieben": 7,
"acht": 8,
"neun": 9,
"zehn": 10,
"bube": 11,
"dame": 12,
"könig": 13,
"ass": 14
}
for value in values:
for suit in suits:
self.cards.append(Card(value, suit))
def shuffle(self):
random.shuffle(self)
print("Karten gemischt")
def __repr__(self):
cardsleft = len(self.cards)
return "Es sind noch {0} karten übrig".format(cardsleft)
def deal(self):
return self.cards.pop(0)
Ok so my problem at self.deck.shuffle() as i think this should randomize the order of my deck but if i take a look at the variable in debug mode its still in ist standard order. why?
As everyone is saying, you probably don't want to inherit from list if your cards are defined as a list within your class.
import random
class Card:
def __init__(self, value, suit):
self.suit = suit
self.value = value
def __repr__(self):
return f'{self.value} von {self.suit}'
class Start:
openCards = []
def __init__(self, numofplayer):
self.deck = Deck()
self.deck.shuffle()
# here we iterate through the cards rather than the deck object
for card in self.deck.cards:
card.isshowing = True
print(card)
self.players = []
class Deck():
def __init__(self):
self.cards = []
suits = ["Herz", "Piek", "Karo", "Kreuz"]
values = {
"zwei": 2,
"drei": 3,
"vier": 4,
"fünf": 5,
"sechs": 6,
"sieben": 7,
"acht": 8,
"neun": 9,
"zehn": 10,
"bube": 11,
"dame": 12,
"könig": 13,
"ass": 14
}
for value in values:
for suit in suits:
self.cards.append(Card(value, suit))
def shuffle(self):
# we also shuffle the cards sins that's what they're assigned to
random.shuffle(self.cards)
print("Karten gemischt")
def __repr__(self):
# we also show the length of the cards
cardsleft = len(self.cards)
return "Es sind noch {0} karten übrig".format(cardsleft)
Start(5)
If you really want to inherit from list you can do it like so.
import random
class Card:
def __init__(self, value, suit):
self.suit = suit
self.value = value
def __repr__(self):
return f'{self.value} von {self.suit}'
class Start:
openCards = []
def __init__(self, numofplayer):
self.deck = Deck()
self.deck.shuffle()
# inheriting from list allows you to iterate through the deck object
# since it inherits a __next__ method from list
for card in self.deck:
card.isshowing = True
print(card)
self.players = []
class Deck(list):
def __init__(self):
# we send a super call to lists constructor
super().__init__()
suits = ["Herz", "Piek", "Karo", "Kreuz"]
values = {
"zwei": 2,
"drei": 3,
"vier": 4,
"fünf": 5,
"sechs": 6,
"sieben": 7,
"acht": 8,
"neun": 9,
"zehn": 10,
"bube": 11,
"dame": 12,
"könig": 13,
"ass": 14
}
for value in values:
for suit in suits:
# here we simply use the append method we inherited from list
self.append(Card(value, suit))
def shuffle(self):
# doing it this way allows us to shuffle self
random.shuffle(self)
print("Karten gemischt")
def __repr__(self):
# we also need to show the length of self
cardsleft = len(self)
return "Es sind noch {0} karten übrig".format(cardsleft)
Start(5)

Selection sort in Card Hand

I'm currently trying to implement selection sort into a hand of cards when cards are added. This is my current code. I understand how selection sort is supposed to work and was just looking for advice on how to start to implement it into my add function.
Hand class
class newHand(object):
def __init__(self, label = ""):
"""
create an empty collection of cards with the given label
"""
self.label = label
self.cards = []
def add(self, card):
"""
# Implements selection sort
pre: card is a Card
post: appended the card to the hand and sorts the deck
"""
self.cards.append(card)
def sort(self):
"""
post: arrange the cards in decending order
"""
cards0 = self.cards
cards1 = []
while len(cards0) > 0:
card = max(cards0)
cards0.remove(card)
cards1.append(card)
self.cards = cards1
def dump(self):
"""
post: outputs the contents in the hand
"""
print(self.label + "'s Cards: ")
for card in self.cards:
print("\t", card)

passing multiple instances of the same class through objects

class Card:
allRanks=(2,3,4,5,6,7,8,9,10,11,12,13,14)
allSuits=('Spades','Hearts','Diamonds','Clubs')
def __init__(self, rank, suit):
self.rank=rank
self.suit=suit.capitalize()
def getRank(self):
if self.rank in Card.allRanks:
return self.rank
else:
print 'Please enter a number from 2 to 14\n14 => ACE\n11 => JACK\n12 => QUEEN\n13 => KING\n'
exit()
def getSuit(self):
if self.suit in Card.allSuits:
return self.suit
else:
print 'There are only 4 suits in a pack!'
exit()
def __str__(self):
translate={11:'Jack',12:'Queen',13:'King',14:'Ace'}
r = self.rank
if r in range(11,15):
myrank=translate[r]
elif r in range(2,11):
myrank=str(r)
else:
print "Sorry wrong card"
exit()
return myrank+' of '+self.suit
def __lt__(self,other):
return (self.rank > other.getRank())
#c=Card(1,'spades')
class Deck:
def __init__(self):
self.deck=[Card(i,j) for i in Card.allRanks for j in Card.allSuits]
#for i in Card.allRanks:
# for j in Card.allSuits:
# self.deck.append(Card(i,j))
def shuffle(self):
from random import shuffle
class Dealer(object):
def __init__(self, deck, cards, num_players):
self.deck=deck
self.num_players=num_players
self.cards=cards
def deal(self):
self.deck.shuffle()
deal_list=[[] for i in range(self.num_players)] #returns a list of lists
#say you have 4 players you will have [[hand1],[hand2],[hand3],[hand4]] where hand1=[5 cards]
#now you pass this to the hand variable
for i in range(self.cards):
for j in range(self.num_players):
deal_list[j].append(self.deck.topCard())
return deal_list
class Dealer_better(object):
def __init__(self, deck, cards, *args):
self.deck=deck
self.cards=cards
def deal(self):
self.deck.shuffle()
deal_list=[[] for i in range(len(*args))] #returns a list of lists
#say you have 4 players you will have [[hand1],[hand2],[hand3],[hand4]] where hand1=[5 cards]
#now you pass this to the hand variable
for i in range(self.cards):
for j in (*args):
j.append(self.deck.topCard())
class Player(object):
def __init__(self, hand=[]):
self.hand=hand
Hi I have classes Dealer and Dealer_better. It accepts objects from Deck() and has to deal cards to n number of players.
In class Dealer I pass the number of players that I need to deal the cards to directly as a variable and generate a list of lists where the hand of each player is a list.
I would like to make this better in Deal_better and pass the player objects directly to Deal_better after initializing multiple instances of Class Player. Is there a way to pass *player_objects similar to *args.
So that I can get the following functionality,
p1=Player()
p2=Player()
p3=Player()
p4=Player()
new_d=Dealer_better(Deck(),p1.hand,p2.hand,p3.hand,p4.hand)
new_d.deal()
print p1.hand should give me player one's hand, or atleast the objects in it.
I could write an add_card method in player to append to self.hand if need be.
pasting the classes in question below for clarity.
class Dealer(object):
def __init__(self, deck, cards, num_players):
self.deck=deck
self.num_players=num_players
self.cards=cards
def deal(self):
self.deck.shuffle()
deal_list=[[] for i in range(self.num_players)] #returns a list of lists
#say you have 4 players you will have [[hand1],[hand2],[hand3],[hand4]] where hand1=[5 cards]
#now you pass this to the hand variable
for i in range(self.cards):
for j in range(self.num_players):
deal_list[j].append(self.deck.topCard())
return deal_list
versus
class Dealer_better(object):
def __init__(self, deck,cards, *players):
self.deck=deck
self.cards=cards
self.players = players
def deal(self):
self.deck.shuffle()
for i in range(self.cards):
for p in range(len(self.players)):
self.players[p].addCard(self.deck.topCard())
print self.players[p].name,len(self.players[p].hand)
class Player(object):
def __init__(self, name, hand=[]):
self.hand=hand
self.name=name
def addCard(self,card):
self.hand.append(card)
Returns
p1=Player('Bob')
p2=Player('Lola')
p3=Player('Luigi')
p4=Player('Mario')
new_d=Dealer_better(Deck(),5,p1,p2,p3,p4)
new_d.deal()
print len(p1.hand)
Returns 20
How about something along the lines of:
class player(object):
def __init__(self):
self.d = []
class dealer(object):
def __init__(self, *decks): #feel free to add more stuff this is just an example :)
self.decks = [d for d in decks]
def deal(self):
for d in self.decks:
d.append(1) # just an example
d.append(2)
p1 = player()
p2 = player()
p3 = player()
d = dealer(p1.d,p2.d,p3.d)
d.deal()
print p1.d # was changed by deal
(demo)

Categories

Resources