For my class i have to make a working Go fish game. i got this far in class but I'm stuck i cant figure out how send player back to Hand so i can make a check statement to see if he has any four of a kind. Any suggestions?
class Card(object):
""" A playing card. """
RANKS = ["A", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "J", "Q", "K"]
SUITS = ["c", "d", "h", "s"]
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __str__(self):
rep = self.rank + self.suit
return rep
class Hand(object):
""" A hand of playing cards. """
def __init__(self):
self.cards = []
def __str__(self):
if self.cards:
rep = ""
for card in self.cards:
rep += str(card) + " "
else:
rep = "<empty>"
return rep
def clear(self):
self.cards = []
def add(self, card):
self.cards.append(card)
def give(self, card, other_hand):
self.cards.remove(card)
other_hand.add(card)
def check(self,player):
print(player1)
for self.cards in rep:
print("i")
#####################
class Deck(Hand):
""" A deck of playing cards. """
def populate(self):
for suit in Card.SUITS:
for rank in Card.RANKS:
self.add(Card(rank, suit))
def shuffle(self):
import random
random.shuffle(self.cards)
def deal(self, hands, per_hand = 1):
for rounds in range(per_hand):
for hand in hands:
if self.cards:
top_card = self.cards[0]
self.give(top_card, hand)
else:
print ("Out of cards!")
def main():
deck1 = Deck()
deck1.populate()
deck1.shuffle()
player1 = Hand()
player2 = Hand()
hands = [player1, player2]
deck1.deal(hands, per_hand = 5)
print(player1)
player1.check(player1)
main()
What you said doesn't really make sense . Hand is a class; player1 is an instance of that class. Translating into personal terms, this is parallel to asking how to send Brandon back to human so you can count his teeth. You don't: you simply use the tooth-counting procedure that works on all humans.
Looking specifically at your code, I think this deals with the line
player1.check(player1)
All it should need is
player1.check()
player1 is a Hand; this call goes to Hand's check method. Here, the object that invoked the method automatically shows up as the first argument, self. Get rid of the second one and just use
def check(self):
Does that clarify the point of confusion?
Check this code out, which is a working version of Go Fish executable and playable from the console. See how they use:
def makeTurn(self):
print '%s\'s hand: %s' % (self.name,self.displayHand())
chooseCard = raw_input('What card do you ask for? ').strip()
if chooseCard == 'quit':
sys.exit(0)
if chooseCard not in self.hand:
print 'You don\'t have that card. Try again! (or enter quit to exit)'
chooseCard = self.makeTurn()
return chooseCard
Here is the complete Python Go Fish game.
import random
import sys
from collections import defaultdict
class HumanPlayer(object):
def __init__(self,deck):
self.hand = defaultdict(int)
self.book = []
self.deck = deck #making a copy of deck, all changes within
#this class should affect the global deck
self.score = 0
self.name = raw_input('Name yourself: ')
def Draw(self): #assuming that deck is a global
cardDrawn = self.deck.pop() #removes the last card from deck
self.hand[cardDrawn] += 1 #adds card to hand
print '%s drew %s.' % (self.name,cardDrawn)
self.checkForBooks()
def checkForBooks(self):
# Removes all items of which are 4.
for key,val in self.hand.items(): #can't use iteritems() because we are modifying hand in loop
if val == 4: #completed a book
self.book.append(key)
print '%s completed the book of %s\'s.' % (self.name,key)
self.score += 1
del self.hand[key]
self.emptyCheck()
def emptyCheck(self):
if len(self.deck)!=0 and len(self.hand)==0: #checks if deck/hand is empty
self.Draw()
def displayHand(self): #Displays current hand, cards separated by spaces
return ' '.join(key for key,val in self.hand.iteritems()
for i in range(val)) #meh, make it prettier
def makeTurn(self):
print '%s\'s hand: %s' % (self.name,self.displayHand())
chooseCard = raw_input('What card do you ask for? ').strip()
if chooseCard == 'quit':
sys.exit(0)
if chooseCard not in self.hand:
print 'You don\'t have that card. Try again! (or enter quit to exit)'
chooseCard = self.makeTurn()
return chooseCard
def fishFor(self,card):
if card in self.hand: # if card in hand, returns count and removes the card from hand
val = self.hand.pop(card)
self.emptyCheck()
return val
else:
return False
def gotCard(self,card,amount):
self.hand[card] += amount
self.checkForBooks()
class Computer(HumanPlayer):
def __init__(self,deck):
self.name = 'Computer'
self.hand = defaultdict(int)
self.book = []
self.deck = deck
self.opponentHas = set()
self.score = 0
def Draw(self): #assuming that deck is a global
cardDrawn = self.deck.pop() #removes the last card from deck
self.hand[cardDrawn] += 1 #adds card to hand
print '%s drew a card.' % (self.name)
self.checkForBooks()
##AI: guesses cards that knows you have, then tries cards he has at random.
##Improvements: remember if the card was rejected before, guess probabilities
def makeTurn(self):
# print self.displayHand(),self.opponentHas
candidates = list(self.opponentHas & set(self.hand.keys())) #checks for cards in hand that computer knows you have
if not candidates:
candidates = self.hand.keys() #if no intersection between those two, random guess
move = random.choice(candidates)
print '%s fishes for %s.' % (self.name,move)
return move
def fishFor(self,card): #Same as for humans players, but adds the card fished for to opponentHas list.
self.opponentHas.add(card)
if card in self.hand: # if card in hand, returns count and removes the card from hand
val = self.hand.pop(card)
self.emptyCheck()
return val
else:
return False
def gotCard(self,card,amount):
self.hand[card] += amount
self.opponentHas.discard(card)
self.checkForBooks()
class PlayGoFish(object):
def __init__(self):
self.deck = ('2 3 4 5 6 7 8 9 10 J Q K A '*4).split(' ')
self.deck.remove('')
self.player = [HumanPlayer(self.deck),Computer(self.deck)] #makes counting turns easier
def endOfPlayCheck(self):#checks if hands/decks are empty using the any method
return self.deck or self.player[0].hand or self.player[1].hand
def play(self):
random.shuffle(self.deck)
for i in xrange(9): # Deal the first cards
self.player[0].Draw()
self.player[1].Draw()
turn = 0
while self.endOfPlayCheck():
print '\nTurn %d (%s:%d %s:%d) %d cards remaining.' % (turn,self.player[0].name,
self.player[0].score,self.player[1].name,self.player[1].score,len(self.deck))
whoseTurn = turn%2
otherPlayer = (turn+1)%2
while True: #loop until player finishes turn
cardFished = self.player[whoseTurn].makeTurn()
result = self.player[otherPlayer].fishFor(cardFished)
if not result: #Draws and ends turn
self.player[whoseTurn].Draw()
break
print '%s got %d more %s.' % (self.player[whoseTurn].name,result, cardFished)
self.player[whoseTurn].gotCard(cardFished,result)
if not self.endOfPlayCheck(): break
turn+=1
print '\nScores: \n%s: %d\n%s: %d\n' % (self.player[0].name,self.player[0].score,
self.player[1].name,self.player[1].score)
if self.player[0].score>self.player[1].score:
print self.player[0].name,'won!'
elif self.player[0].score==self.player[1].score:
print 'Draw!'
else:
print self.player[1].name,'won!'
if __name__=="__main__":
game = PlayGoFish()
game.play()
Related
Ok, so I'm experiencing a weird error, and unfortunately, I don't know that I can show both examples here.
Take a look at the following code (apologies for the length):
import random
suits = ('Hearts', 'Diamonds', 'Spades', 'Clubs')
ranks = ('Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King', 'Ace')
values = {'Two':2, 'Three':3, 'Four':4, 'Five':5, 'Six':6, 'Seven':7, 'Eight':8,
'Nine':9, 'Ten':10, 'Jack':10, 'Queen':10, 'King':10, 'Ace':11}
class Card():
def __init__(self,suit,rank):
self.suit = suit
self.rank = rank
## Attributes listed in the __init__ statement do not have to
## correlate to called objects (ie: suit,rank)
self.value = values[rank]
## Values represents the integer value of each card as compared
## to it's string (word) based rank
def __str__(self):
return self.rank + " of " + self.suit
## Allows for printing in the form of X of Y, or Two of Hearts as an example
# Deck will be a compilation of all card objects to a single list
# that can then be shuffled, dealt from, etc.
class Deck():
def __init__(self):
## All cards starts off with a blank list
self.all_cards = []
## For each suit in suits
for suit in suits:
### For each rank in ranks
for rank in ranks:
#### Create the Card object equal to the suit and rank of the card
#### ie: Two of spades, three of clubs, etc.
created_card = Card(suit,rank)
#### Then append each card created to the self.all_cards list.
self.all_cards.append(created_card)
def shuffle(self):
## Method call to randomize the self.all_cards list internally
random.shuffle(self.all_cards)
def deal_one(self):
return self.all_cards.pop()
game_deck = Deck()
game_deck.shuffle()
## Checking to make sure the deck is full before continuing
print(len(game_deck.all_cards))
## Attempt without watching tutorial
class Player():
def __init__(self,name):
self.name = name
self.player_hand = []
self.balance = 100
self.bet = 0
def deposit(self,dep_amt):
self.balance += dep_amt
print(f'{dep_amt} added to balance. New balance is: ${self.balance}')
def withdraw(self,wit_amt):
self.balance -= wit_amt
print(f'{wit_amt} subtracted from balance. New balance is: ${self.balance}')
def player_bet(self,bet_amt = 0):
while True:
bet_amt = int(input("How much would you like to bet? \n"))
if bet_amt > self.balance:
print("Sorry, you don't have enough money, try again.")
elif bet_amt < 1:
print('Sorry, you must bet at least $1')
else:
print(f'Thanks, your bet is ${bet_amt}')
self.balance -= bet_amt
print(f'Your remaining balance is ${self.balance}')
break
## May need to be adjusted to clear the hand entirely, unsure
def remove_one(self):
return self.player_hand.pop(0)
# NOTE: The most cards a player can hold without busting is 11
def add_cards(self,new_cards):
### Single card object
self.player_hand.append(new_cards)
def __str__(self):
return f'{self.name} has a balance of ${self.balance}.'
## Attempt without watching tutorial
class Dealer():
def __init__(self,name):
self.name = name
self.player_hand = []
self.balance = 500
def deposit(self,dep_amt):
self.balance += dep_amt
print(f'{dep_amt} added to balance. New balance is: ${self.balance}')
def withdraw(self,wit_amt):
self.balance -= wit_amt
print(f'{wit_amt} subtracted from balance. New balance is: ${self.balance}')
def remove_one(self):
return self.player_hand.pop(0)
def add_cards(self,new_cards):
### Single card object
self.player_hand.append(new_cards)
def __str__(self):
return f'{self.name} has a balance of ${self.balance}.'
## Full Game Code
game_on = True
playing = False
while game_on == True:
game_check = input('Would you like to play Black Jack, Y/N? ').lower()
if game_check == 'y':
round_counter = 0
dealer_name = 'Klaus the Dealer'
dealer = Dealer(dealer_name)
player_name = input("Player One, what is your name? ")
player_one = Player(player_name)
print(f"Nice to meet you {player_name}, you'll be playing against {dealer_name}\n")
print(player_one)
print(dealer)
print("Let's play Black Jack!\n")
playing = True
else:
print('Ok, have a nice day')
game_on = False
break
while playing == True:
round_counter +=1
for x in range(2):
player_one.add_cards(game_deck.deal_one())
dealer.add_cards(game_deck.deal_one())
So when I run this code all at once, at the very last 2 lines, I get an index error when the code hits the player_one.add_cards(game_deck.deal_one()) stating IndexError: pop from empty list
The thing is, if I run this code in separate lines in a Jupiter notebook, and separate out the class calls from the rest of the game code, the player_one.add_cards(game_deck.deal_one()) works just fine.
Can anyone tell me what is causing this error?
You are running out of cards... :)
the line you reference is inside of an infinite loop.
while playing == True:
round_counter +=1
for x in range(2):
player_one.add_cards(game_deck.deal_one())
dealer.add_cards(game_deck.deal_one())
suggestion: while you are troubleshooting, just pop a little print statement in the deal_one() function to see what is being dealt and then delete it later.
Ok, so thanks to Jeff H for pointing out that the call for the card deal was inside an infinite loop, even though I couldn't fix the problem by breaking the loop itself, I did figure out how to start off the initial deal by absorbing it into the game_check statement above, like so:
while game_on == True:
game_check = input('Would you like to play Black Jack, Y/N? ').lower()
if game_check == 'y':
round_counter = 0
dealer_name = 'Klaus the Dealer'
dealer = Dealer(dealer_name)
player_name = input("Player One, what is your name? ")
player_one = Player(player_name)
print(f"Nice to meet you {player_name}, you'll be playing against {dealer_name}\n")
print(player_one)
print(dealer)
print("Let's play Black Jack!\n")
playing = True
for x in range(2):
player_one.add_cards(game_deck.deal_one())
dealer.add_cards(game_deck.deal_one())
This won't account for having to deal cards into the hand later while in another while loop, but it will fix the first issue.
declared playing in my hit_or_stay function as a global variable but keep getting an error when i run my program saying "name error: playing is not defined" How do i fix this?
I used global playing in hit_or_stand() to control the gameflow depending on the player's input. When I run the code I get error "NameError: name playing is not defined." I tried to move "global playing" outside of hit_or_stay() but nothing seems to work.
import random
suits = ('Hearts', 'Diamonds', 'Spades', 'Clubs')
ranks = ('Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King', 'Ace')
values = {'Two':2, 'Three':3, 'Four':4, 'Five':5, 'Six':6, 'Seven':7, 'Eight':8, 'Nine':9, 'Ten':10, 'Jack':10,
'Queen':10, 'King':10, 'Ace':11}
class Deck:
'''
CREATES DECK, SHUFFLES DECK, DEALS CARDS.
'''
#CREATES DECK BY ADDING EACH SUIT TO EACH RANK AND STORING IN LIST SELF.DECK
def __init__(self):
self.deck = []
for s in suits:
for r in ranks:
self.deck.append(r + ' of ' + s)
#SHUFFLE DECK CREATED IN __init__()
def shuffle(self):
random.shuffle(self.deck)
#DEALS CARD FROM SELF.DECK AT GAME START AND IF PLAYER CHOOSES TO HIT
def deal(self):
single_card = self.deck.pop()
return single_card
#PRINTS CARDS IN SELF.DECK (FOR TROUBLESHOOTING)
def __str__(self):
for card in self.deck:
return str(self.deck)
#print('\n')
##create an instance of Deck class and print the deck
#test_deck = Deck()
#print(test_deck)
#print('\n')
##shuffle and print the deck
#test_deck.shuffle()
#print(test_deck)
#print('\n')
class Card:
'''
CREATES CLASS FOR INDIVIDUAL CARDS, PRINTS INIDIVIDUAL CARDS "SUIT OF RANK"
'''
#CREATES CHARACTERISITCS OF class Card; self.suit and self.rank
def __init__(self,suit,rank):
self.suit = suit
self.rank = rank
class Hand:
'''
HOLDS CARD OBJECT FROM self.deck USING Deck.deal() method
CALCULATES THE VALUE OF THE CARDS IN HAND
ADJUST FOR ACES WHEN APPROPRIATE
'''
#CREATE CHARATERISTICS FOR CARDS IN HAND; self.card = cards in hand, self.value = value of cards in hand, self.aces = counts aces in hand
def __init__(self):
self.cards = []
self.value = 0
self.aces = 0
#ADDS CARDS TO HANDS ONCE DEALT
def add_card(self,card):
#ADDS CARD TO HAND
self.cards.append(card)
#HOLDS THE VALUE OF THE HAND
key = ''
key = card.split()
self.value += values[key[0]]
#ACCOUNT FOR ACE
if key[0] == 'Ace':
self.aces += 1
#KEEPS TRACK OF ACES; ADJUSTS VALUE FOR ACE WHEN APPROPARIATE
def adjust_for_ace(self,card):
if self.value > 21 and self.aces:
self.value -= 10
self.aces -= 1
class Chips:
def __init__(self):
self.total = 100
self.bet = 0
#ADD CHIPS TO TOTAL IF WIN
def win_bet(self):
self.total += self.bet
#TAKE AWAY CHIPS FORM TOTAL IF LOSE
def lose_bet(self):
self.total -= self.bet
#TAKE BET FROM USER
def take_bet(Chips):
while True:
try:
Chips.bet = int(input("How many chips do you want to bet?: "))
except:
print("There was an error! Enter an integer!\n")
continue
else:
if Chips.bet > Chips.total:
print("You don't have enough chips!\n")
else:
print("Your bet is {} chips\n".format(Chips.bet))
break
#IF PLAYER CHOOSES TO HIT
def hit(deck,hand):
#USE IN hit_or_stay function; ADDS CARD DEALT FROM DECK TO THE HAND and ADJUSTS FOR ACES
Hand.add_card(deck.deal())
Hand.adjust_for_ace()
#DETERMINE WHETHER PLAYER WANTS TO HIT OR STAY
def hit_or_stay(deck,hand):
global playing #controls upcoming loop
while True:
h_or_s = raw_input("Would you like to hit or stay? Enter 'hit' or 'stay': ")
if h_or_s[0].lower() == "h":
hit(deck,hand)
elif h_or_s[0].lower() == "s":
playing = False
else:
print("\nThere was an error. Try again.\n")
continue
break
def show_some(player,dealer):
print("Dealer's Hand:")
print(dealer.cards[0])
print("< card hidden >\n")
print("\nPlayer's hand value: {}".format(player.value))
print("Player's Hand:", *player.cards, sep='\n')
def show_all(player,dealer):
print("\nDealer's hand value: {}".format(dealer.value))
print("\nDealer's Hand:", *dealer.cards, sep='\n')
print("\nPlayer's hand value: {}\n".format(player.value))
print("Player's Hand:", *player.cards, sep='\n')
#GAME ENDING FUNCTIONS
def player_busts(player,dealer,Chips):
#PRINT PLAYER BUSTS; TAKE AWAY CHIPS BET FROM TOTAL CHIPS
print("Dealer's hand value: {}\n Player's hand value: {}\n".format(dealer_hand.value,player_hand.value))
print("Player busts! Dealer Wins\n")
Chips.lose_bet()
print("You lost {} chips!\n You have {} chips remaining.".format(Chips.bet,Chips.total))
def player_wins(player,dealer,Chips):
print("Dealer's hand value: {}\n Player's hand value: {}\n".format(dealer_hand.value,player_hand.value))
print("Player wins!\n")
Chips.win_bet()
print("You won {} chips!\n You have {} chips remaining.".format(Chips.bet,Chips.total))
def dealer_busts(player,dealer,Chips):
print("Dealer's hand value: {}\n Player's hand value: {}\n".format(dealer_hand.value,player_hand.value))
print("Dealer busts! Player wins!\n")
Chips.lose_bet()
print("You won {} chips!\n You have {} chips remaining.".format(Chips.bet,Chips.total))
def dealer_wins(player,dealer,Chips):
print("Dealer's hand value: {}\n Player's hand value: {}\n".format(dealer_hand.value,player_hand.value))
print("Dealer wins!\n")
Chips.lose_bet()
print("You lost {} chips!\n You have {} chips remaining.".format(Chips.bet,Chips.total))
def push(player,dealer,Chips):
print("Player and Dealer tie, its a push!\n")
#GAMEPLAY
while True:
print("Welcome to Black Jack. Get as close to 21 as you can without going over 21.\n")
print("Dealer hits until he reaches 17. Aces count as 1 or 11\n")
#create an instance of Deck class and print the deck
game_deck = Deck()
#shuffle and print the deck
game_deck.shuffle()
#CREATE PLAYER HAND AND DEAL TWO CARDS
player_hand = Hand()
player_hand.add_card(game_deck.deal())
player_hand.add_card(game_deck.deal())
#CREATE DEALER HAND AND DEAL TWO CARDS
dealer_hand = Hand()
dealer_hand.add_card(game_deck.deal())
dealer_hand.add_card(game_deck.deal())
#TAKE BET FROM PLAYER
#player_chips = take_bet(Chips())
#SHOW ONE OF DEALER'S CARDS AND ALL OF PLAYER'S CARDS
show_some(player_hand,dealer_hand)
while playing: #THIS IS WHERE THE NAME ERROR OCCURS!!
if player_hand.value < 21:
#PLAYER CHOOSES HIT OR STAY
hit_or_stay(game_deck(),player_hand)
#SHOW PLAYERS CARDS AND KEEP ONE DEALER CARD HIDDEN
show_some(player_hand,dealer_hand)
NameError: name 'playing' is not defined
global in Python doesn't create a variable, it is defining the existing variable as global. So you are trying to state that a some variable is global (but it is still not exists) and then call it. So you get an error.
Please, never use global in Python! In 99.999% cases you can easily replace your code without any global variables! For example, add playing = True after this part of your code:
#GAMEPLAY
while True:
print("Welcome to Black Jack. Get as close to 21 as you can without going over 21.\n")
print("Dealer hits until he reaches 17. Aces count as 1 or 11\n")
#create an instance of Deck class and print the deck
game_deck = Deck()
#shuffle and print the deck
game_deck.shuffle()
The problem is that playing is defined in hit_or_stay, which isn't called until after the while loop begins.
Define playing as True to start, or include an explicit break condition
while True:
print("Welcome to Black Jack. Get as close to 21 as you can without going over 21.\n")
print("Dealer hits until he reaches 17. Aces count as 1 or 11\n")
# ~snip~
# define here
playing = True
while playing:
# rest of your code
Furthermore, it looks like hit_or_stay also has a while loop in it. I'd suggest taking a look at how you've structured your code so far and maybe refactoring it. Instead of just break in hit_or_stay, maybe you could return playing:
def hit_or_stay(deck,hand):
# no need for global playing anymore, as you return the proper bool
h_or_s = raw_input("Would you like to hit or stay? Enter 'hit' or 'stay': ")
if h_or_s[0].lower() == "h":
hit(deck,hand)
return True
elif h_or_s[0].lower() == "s":
return False
else:
raise ValueError("\nThere was an error. Try again.\n")
playing = True
while playing:
try:
playing = hit_or_stay(deck, hand)
# Catch the error for unexpected input and retry
except ValueError as e:
print(e)
continue
I think I need help with my code, I can't tell what the error is or where to fix it. The following are a number of classes that handle a basic deck of playing cards and what it's suppose to do is randomly pick cards from the deck object and put them in the hand object, and then delete the objects from the hand object, so that the next hand I create won't pick up duplicates. Maybe this is an obtuse way of thinking about it, but it seemed to be the best way of managing a deck of cards as it would be in the real world.
The problem is either in the logging method, or in the create_hand() method. When I look at my hand after I run my code, I see the cards in my hand are still in the deck, if I go to the report in the log. Instead, there are different cards that have been removed. It reports the right number of cards taken out of the deck, but it also only reports that hearts are taken out in the tallies that count the number of cards according to suit. However, my hand has a random assortment of unique cards, with unique values and suits. So that part works.
class Suit(Enum):
HEARTS = "Hearts"
CLUBS = "Clubs"
DIAMONDS = "Diamonds"
SPADES = "Spades"
class Card:
def __init__(self, suit, value):
self.suit = suit
self.value = value
if self.suit == 1:
self.suit = Suit.HEARTS
elif self.suit == 2:
self.suit = Suit.CLUBS
elif self.suit == 3:
self.suit = Suit.DIAMONDS
else:
self.suit = Suit.SPADES
#and a host of magic methods that are irrelevant
class Deck:
def __init__(self, deck_size):
self.deck_size = deck_size
self.deck = []
card_suit = 1
card_value = 2
while len(self.deck) < self.deck_size:
self.deck.append(Card(suit=card_suit, value=card_value))
card_value += 1
if card_value > 14:
card_suit += 1
card_value = 2
def log_deck(self):
logging.info("Deck cards: {}".format(len(self.deck)))
hearts = clubs = diamonds = spades = 0
for card in self.deck:
if card.suit == Suit.HEARTS:
hearts += 1
elif card.suit == Suit.CLUBS:
clubs += 1
elif card.suit == Suit.DIAMONDS:
diamonds += 1
else:
spades += 1
logging.info("{} |".format(str(card)))
logging.info("Hearts: {}, Clubs: {}, Diamonds: {}, Spades: {}".format(hearts, clubs, diamonds, spades))
logging.info("*"*20)
class Hand:
def __init__(self, size=2):
self.size = size
self.cards = []
def create_hand(self, deck):
self.cards = []
while len(self.cards) < self.size:
card = random.sample(deck.deck_cards, k=1)
card_value = card[0].value
card_suit = card[0].suit
if card not in self.cards:
self.cards.append(card)
for card in deck.deck_cards:
if card.value == card_value and card.suit == card_suit:
deck.deck_cards.remove(card)
deck.log_deck()
return self.cards
And here's a sample of what the log looks like. For conciseness I cut out most of the cards from the listing:
INFO:root:Deck cards: 47
INFO:root:|2 of Hearts| |
INFO:root:|3 of Hearts| |
INFO:root:|5 of Hearts| |
INFO:root:|6 of Hearts| |
....
INFO:root:|King of Spades| |
INFO:root:|Ace of Spades| |
INFO:root:Hearts: 8, Clubs: 13, Diamonds: 13, Spades: 13
INFO:root:********************
The tallies at the bottom seem to always show that there are only eight hearts after a hand was generated, even if my hand doesn't have hearts, and even if the cards that are missing aren't hearts.
Essentially, I can't figure out if my log is logging things incorrectly, or if I'm not removing cards correctly. Or both. Some testing with creating two hands using the same deck is showing duplicate cards, which is what I'm trying to avoid.
Thanks
Your outer variable card is a list rather than a Card instance.
You append the list containing one item (your sampled card) to your self.cards list, rather than the item itself. I believe that is causing some disruption trying to remove the card in your last line.
I recommend changing card to card[0] in
if card not in self.cards
and
self.cards.append(card)
to see if that fixes the issue.
Moreover, you seem to be sampling from deck.deck_cards; I assume deck is a Deck object, but your given Deck implementation does not seem to have a deck_cards attribute. I would imagine that would throw an AttributeError, though.
Also, to save yourself (and anyone reading your code) some undue confusion, maybe you would want to not overwrite the card variable name in your inner loop.
I got some help from /u/Zigity_Zagity at /r/learnpython at reddit. A slight rewrite of my code, thanks to him:
def create_hand(self, deck):
self.cards = random.sample(deck.deck_cards, k=self.size)
deck.deck_cards = [card for card in deck.deck_cards if card not in self.cards]
deck.log_deck()
return self.cards
And fixing the Card object's __eq__ method:
def __eq__(self, other):
return self.value == other and self.suit.value == other
I am making a simple card game that deals the player two cards and if the cards are the same suit, the same rank (value), or a run the player wins. If the player's hand has none of these properties the player loses. The code I am using is as follows:
from itertools import product
import random
class Card(object):
FACES = {11: 'Jack', 12: 'Queen', 13: 'King', 14: 'Ace'}
def __init__(self, rank, suit):
self.suit = suit
self.rank = rank
def __str__(self):
value = self.FACES.get(self.rank, self.rank)
return "{0} of {1}".format(value, self.suit)
def __lt__(self, other):
return self.rank < other.rank
class Deck(object):
def __init__(self, ranks=None, suits=None):
if ranks is None:
ranks = xrange(2, 15)
if suits is None:
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
self.deck = []
for r in ranks:
for s in suits:
self.deck.append(Card(r, s))
def deal(self, n):
return random.sample(self.deck, n)
ranks = xrange(2, 15)
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
deck = Deck()
hand = deck.deal(2)
print "Your hand is"
print " - ".join(map(str, hand))
suits_in_hand = []
for card in hand:
suits_in_hand.append(card.suit)
if all(suits_in_hand == suit for suit in suits):
print "\nAll cards are of the same suit"
print "You Win!"
elif all(suits_in_hand == rank for rank in ranks):
print "\nAll cards are of the same rank"
print "You Win!"
# I don't know how to test if the cards in the player's hand are a run
else:
print "\nYou Lose."
However, even if the cards are both the same suit (e.g. 'two of Spades' and 'three of Spades') or the same rank (e.g. 'two of Hearts' and 'two of Clubs') it outputs You Lose. What can I do to fix this and how can I add the elif statement to test whether or not the cards in the player's hand are a run.
To check if all elements in a collection are the same, a short and elegant solution is to use a set :
suits_in_hand = set(card.suit for card in hand)
if len(suits_in_hand) == 1:
print "\nAll cards are of the same suit"
print "You Win!"
Same for ranks.
For runs, you could compare the set of ranks to a set from range :
ranks_in_hand = set(card.rank for card in hand)
min_rank == min(ranks_in_hand)
if set(ranks_in_hand) == set(range(min_rank, min_rank + 2)):
print "\nGot a run !"
Your main issue is you're trying to compare a single entity to an array, which you can't do. Instead of
if all(suits_in_hand == suit for suit in suits):
print "\nAll cards are of the same suit"
print "You Win!"
do something like
if suit in suits_in_hand for suit in suits:
print "\nAll cards are of the same suit"
print "You Win!"
For the ranks, you'll need to do something similar, but build a ranks_in_hand object.
You want to compare the elements in suits_in_hand against each other. Comparing them to suits is comparing your hand to all possible suits, which isn't right. Similarly, you need a ranks_in_hand and compare the elements in it to each other.
To check for a run, just check if the difference between the card ranks in your hand is 1.
For checking if all of the suits in the hand are the same, you can check to see if the length of the set created from suits_in_hand is one.
if len(set(suits_in_hand)) == 1:
# do something
A similar thing could be done for checking if the hand contains all of the same card.
To check for a run, you could try sorting the list and checking that each element is one greater than the next (simplified, just check the difference if you only need to support a hand size of two).
Here's the current code for my Black Jack game:
import random
def showMenu():
userInput = raw_input("Welcome to the game of Black Jack! Please choose an option from the following: \n - Start Game \n - Rules \n - Exit")
if userInput == "Start Game":
return maingame()
def maingame():
done = False
return cardGenerator()
print "{0:>4} {01:>18} {02:>20} {03:>18}".format("Player Money", (cards[0], cards[1]), "CPU Cards", "CPU Money")
def getInitialMoney():
initialdough = 5000
def cardGenerator():
#Assign a random suit
suit_card = ["Hearts", "Spades", "Clubs", "Diamond"]
from random import choice
#Assign a random number between 1-13 (Ace to King)
number_card = random.randrange(1,14)
cards = choice(suit_card), (number_card)
def getDecision():
getDecision = raw_input("What will you do? \n - Hit \n - Stand")
if getDecision == "Hit":
return hit()
elif getDecision == "Stand":
return stand()
def hit():
return cardGenerator()
def stand():
return opponentphase()
def raise_bet():
raise_amount = input("How much will you bet?")
total_pot = 0 + raise_amount
def main():
maingame()
main()
The problem is in maingame(). is return cardGenerator effectively calling the function? Not sure why I can't index a value from a list in the string formatting, when I run as of now it doesn't return anything.
Sorry if things are unclear, I'm really bad at articulating what I'm trying to explain
In the method maingame(), you are returning the result of cardGenerator()(which is None because it is not returning anything), so your method maingame() never reaches the print statement. I think you could do these modifications:
def maingame():
done = False
cards = cardGenerator()
print "{0:>4} {01:>18} {02:>20} {03:>18}".format("Player Money", (cards[0], cards[1]), "CPU Cards", "CPU Money")
and return cards in cardGenerator(), so you can use it in maingame()
def cardGenerator():
#Assign a random suit
suit_card = ["Hearts", "Spades", "Clubs", "Diamond"]
from random import choice
#Assign a random number between 1-13 (Ace to King)
number_card = random.randrange(1, 14)
cards = choice(suit_card), (number_card)
return cards
Note that you have never declared opponentphase() method (at least it is not in the code you posted).