Python Simple Card Game to Learn Classes - python

I am trying to create a simple card game to better understand OOP and classes.
The game is as follows: Two cards are dealt from a deck. Then a third card is dealt. If the third card is between the first two cards, then the player wins. If the third card is equal to either of the first two cards, or is outside of the set, then the player loses.
This is what I have so far:
class Deck(object):
def __init__(self):
self.deck = []
def PopulateDeck(self):
suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
for suit in suits:
for rank in range(2, 15):
if rank == 11:
value = "Jack"
elif rank == 12:
value = "Queen"
elif rank == 13:
value = "King"
elif rank == 14:
value = "Ace"
self.deck.append(str(value) + " of " + suit)
class Card(object):
def __init__(self, rank, value):
self.rank = rank
self.value = value
self.card = self.rank + self.value
I am having a difficult time with classes and OOP, and I'm not sure if this is a good start, or where I should go next. Much of this was created by reading other sources and examples. Can anyone please provide advice on what other classes I may want to make to run my game, and how those classes may interact with/inherit from each other? Thank you.

This is more of a code/approach review. A card game is a case for composition, not inheritance; the Deck contains Cards, but isn't in itself a type of Card, and vice versa.
I think you are duplicating information in the Card. Just store suit and rank, and use __str__ to create 'x of y'. You can also implement the rich comparison methods:
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
Now e.g. str(Card(13, 'Clubs')) == "King of Clubs". This way you don't duplicate the rank and value in card.
Next, I think the Deck should incorporate the population generation in __init__; you can provide optional arguments for a non-standard deck. I have included two implementations; the commented-out version is a list comprehension using itertools to do the same job in one line. I also provide a function to pick n different random cards from self.deck.
from itertools import product
import random
class Deck(object):
def __init__(self, ranks=None, suits=None):
if ranks is None:
ranks = range(2, 15)
if suits is None:
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
## self.deck = [Card(r, s) for r, s in product(ranks, suits)]
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)
Now the game is simple; you can deal three cards per hand, and compare the cards naturally (using e.g. <) because of the comparison methods.
deck = Deck()
hand = deck.deal(3)
print(" - ".join(map(str, hand)))
if min(hand[0], hand[1]) < hand[2] < max(hand[0], hand[1]):
print("Winner!")
else:
print("Loser.")

As #tobias_k have pointed out in the comments + some of my thoughts
class Deck(object):
def __init__(self):
self.deck = []
self.dealt = [] #Prevents from dealing the same card
def PopulateDeck(self):
suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
for suit in suits:
for rank in range(2, 15):
if rank == 11:
value = "Jack"
elif rank == 12:
value = "Queen"
elif rank == 13:
value = "King"
elif rank == 14:
value = "Ace"
else:
value = str(rank)
self.deck.append(Card(value, suit))
def deal(self):
#Randomly select card
remaining_cards = [card for card in self.deck if card not in self.dealt]
card_index = random.randrange(0, len(remaining_cards)-1)
card = remaining_cards[card_index]
self.dealt.append(card)
return card
def shuffle(self):
self.dealt = []
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
self.card = str(self.rank) + " " + str(self.suit)
def __eq__(self, other):
return self.rank == other.rank and self.suit == other.suit
def play():
deck = Deck()
card1 = deck.deal()
card2 = deck.deal()
card3 = deck.deal()
#And here you will compare the cards to see if the player wins or not. Not sure
#what exact criterion you're using.
deck.shuffle() #And leave your deck nicely shuffled for next game
play()
Random documentation
I have not run this code, and it likely contains errors. But it is an illustration of what you can do.

I am also learning opps using card games as i find that is most easy way to understand the concepts. I have come accross this code sometime back, but not sure about the link. I am pesting this code with few usefull comments. This may be helpful.
from random import shuffle
class Card:
suits = ['spades', 'hearts', 'diamonds', 'clubs']
values = [None, None, '2','3','4','5','6','7','8','9','10','Jack', 'Queen', 'King', 'Ace']
def __init__(self, v, s):
'''suits + values are ints'''
self.value = v
self.suit = s
def __lt__(self, c2): # allows to compare two objects cards in this case
if self.value < c2.value:
return True
if self.value == c2.value: # value is what user is putting while creating object of class
if self.suit < c2.suit: # c2 is with what we are comapring
return True
else:
return False
return False
def __gt__(self, c2): # allows to compare two objects cards in this case,
if self.value > c2.value: # c2 is with what we are comapring
return True
if self.value == c2.value:
if self.suit > c2.suit: # suits comes into picture if values are same suit number is given importance
return True
else:
return False
return False
def __repr__(self):
v = self.values[self.value] + ' of ' + self.suits[self.suit]
return v
# defining the class which represent the deck of card
class Deck:
def __init__(self):
self.cards = []
for i in range(2,15):
for j in range(4):
self.cards.append(Card(i,j))
shuffle(self.cards)
def rm_card(self):
if len(self.cards) == 0:
return # if block it return to None object when condition is satisfied
return self.cards.pop()
class Player:
def __init__(self, name):
self.name = name # name of the player
self.card = None # current card player holding
self.wins = 0
class Game:
def __init__(self):
name1 = input('p1 name ')
name2 = input('p2 name ')
self.deck = Deck()
self.p1 = Player(name1)
self.p2 = Player(name2)
def wins(self, winner):
w = "{} wins this round"
w = w.format(winner)
print(w)
def draw(self, p1n, p1c, p2n, p2c):
d = "{} drew {} {} drew {}"
d = d.format(p1n, p1c, p2n, p2c)
print(d)
def play_game(self):
cards = self.deck.cards # self.deck = Deck() and cards = self.deck.cards is list of card
print('Beginning War!!!')
while len(cards) > 2:
m = 'q to quit. Any' + ' key to play'
response = input(m)
if response == 'q':
break
p1c = self.deck.rm_card() # removing first card from deck by player 1
p2c = self.deck.rm_card() # removing second card from deck by player 2
p1n = self.p1.name
p2n = self.p2.name
self.draw(p1n, p1c, p2n, p2c)
if p1c > p2c:
self.p1.wins +=1
self.wins(self.p1.name)
else:
self.p2.wins += 1
self.wins(self.p2.name)
win = self.winner(self.p1, self.p2)
print("War is over. {} wins".format(win))
def winner(self, p1, p2):
if p1.wins > p2.wins:
return p1.name
if p1.wins < p2.wins:
return p2.name
return 'Its Tie!'
game = Game()
game.play_game()
Source: https://thecleverprogrammer.com/2020/10/04/card-game-with-python/

Related

Why am I getting a "pop from empty list" error?

I'm trying to make a simplified version of the card game War. In this game, there are two players. Each starts with half of a deck. The players each deal the top card from their decks and whoever has the higher card wins the other player's cards and adds them to the bottom of his deck. If there is a tie, the two cards are eliminated from play. The game ends when one player runs out of cards.
However, I'm having an issue with the pop argument, in that it gives me an error with "pop from empty list".
How can I fix that?
import random
class Card:
def __init__(self, value, suit):
self.value = value
self.suit = suit
def __str__(self):
names = ['Jack', 'Queen', 'King', 'Ace']
if self.value <= 10:
return '{} of {}'.format(self.value, self.suit)
else:
return '{} of {}'.format(names[self.value-11], self.suit)
class CardGroup:
def __init__(self, cards = []):
self.cards = cards
def shuffle(self):
random.shuffle(self.cards)
class StandardDeck(CardGroup):
def __init__(self):
self.cards = []
for s in ['Hearts', 'Diamonds', 'Clubs', 'Spades']:
for v in range(2,15):
self.cards.append(Card(v, s))
def deal_out(self, num_cards, num_players):
deal = [[0 for x in range(num_cards)] for y in range(num_players)]
for i in range(num_cards):
for k in range(num_players):
deal[k][i] = self.cards.pop()
self.deal = deal
deck = StandardDeck()
deck.shuffle()
print("\n===== shuffled deck =====\n")
player1_list = []
player2_list = []
for i in range(26):
p1_temp = deck.deal_out(26, 2)
player1_list.append(p1_temp)
p2_temp = deck.deal_out(26, 2)
if (p2_temp.__init__() == 1):
player1_list.append(p2_temp)
player2_list.append(player1_list.pop(0))
else:
player2_list.append(p2_temp)
# Card dealt to Player #1
player1_card = player1_list.pop(0)
print("===== player #1 =====")
print("Card dealt to player #1: \n", player1_card)
print(player1_list)
#Card dealt to Player #2
player2_card = player2_list.pop(0)
print("\n===== player #2 =====")
print("Card dealt to player #2: \n", player2_card)
print(player2_list)
# Compare the two cards using overloaded operators
if player1_card == player2_card:
print("Tie: ", player1_card, "and", player2_card,\
"are of equal rank")
elif player1_card > player2_card:
print("Player #1 wins: ", player1_card, \
"is of higher rank than", player2_card)
else:
print("Player #2 wins: ", player2_card, \
"is of higher rank than", player1_card)
print()
Rather than self.deal = deal in deal_out() you likely want to return the deal.
If you do, some code related to for i in range(26): can be removed as your player hands get populated from deal_out(). You also have some issues of comparing cards when you want to compare card values.
Here are some slight modifications to get you going again:
import random
class Card:
def __init__(self, value, suit):
self.value = value
self.suit = suit
def __str__(self):
names = ['Jack', 'Queen', 'King', 'Ace']
if self.value <= 10:
return '{} of {}'.format(self.value, self.suit)
else:
return '{} of {}'.format(names[self.value-11], self.suit)
class CardGroup:
def __init__(self, cards = []):
self.cards = cards
def shuffle(self):
random.shuffle(self.cards)
class StandardDeck(CardGroup):
def __init__(self):
self.cards = []
for s in ['Hearts', 'Diamonds', 'Clubs', 'Spades']:
for v in range(2,15):
self.cards.append(Card(v, s))
def deal_out(self, num_cards, num_players):
deal = [[0 for x in range(num_cards)] for y in range(num_players)]
for i in range(num_cards):
for k in range(num_players):
deal[k][i] = self.cards.pop()
## self.deal = deal
return deal
deck = StandardDeck()
deck.shuffle()
print("\n===== shuffled deck =====\n")
'''
player1_list = []
player2_list = []
for i in range(26):
p1_temp = deck.deal_out(26, 2)
player1_list.append(p1_temp)
p2_temp = deck.deal_out(26, 2)
if (p2_temp.__init__() == 1):
player1_list.append(p2_temp)
player2_list.append(player1_list.pop(0))
else:
player2_list.append(p2_temp)
'''
player1_list, player2_list = deck.deal_out(26, 2)
# Card dealt to Player #1
player1_card = player1_list.pop(0)
print("===== player #1 =====")
print("Card dealt to player #1: \n", player1_card)
#print(player1_list)
#Card dealt to Player #2
player2_card = player2_list.pop(0)
print("\n===== player #2 =====")
print("Card dealt to player #2: \n", player2_card)
#print(player2_list)
# Compare the two cards using overloaded operators
if player1_card.value == player2_card.value:
print("Tie: ", player1_card, "and", player2_card,\
"are of equal rank")
elif player1_card.value > player2_card.value:
print("Player #1 wins: ", player1_card, \
"is of higher rank than", player2_card)
else:
print("Player #2 wins: ", player2_card, \
"is of higher rank than", player1_card)
print()

Why is it creating a bound method and how to fix it?

When I manually call deal method it works but when under while playing it creates a bound method.
Any ideas to fix it.
import random
#Raw values
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}
playing = True
Classes are defined here
#Define classes
class Card:
def __init__(self,suit,rank):
self.suit = suit
self.rank = rank
def __str__(self):
return self.rank + " of " + self.suit
class Deck:
def __init__(self):
self.deck = []
for suit in suits:
for rank in ranks:
self.deck.append(Card(suit,rank))
def __str__(self):
deck_comp = ""
for card in self.deck:
deck_comp += " \n " + card.__str__()
return "The deck has" + deck_comp
def shuffle(self):
random.shuffle(self.deck)
def deal(self):
single_card = self.deck.pop()
return single_card
Hand class for the creating the hands for players
class Hand:
def __init__(self):
self.cards = []
self.value = 0
self.ace = 0
def add_card(self,card):
self.cards.append(card)
self.value += values[card.rank]
if card.rank == "Ace":
self.ace += 1
def adjust_for_ace(self):
while self.value >21 and self.ace>0:
self.value -= 10
self.ace -= 1
Functions are made here
#Functions to use during play
def Take_bet(chips):
while True:
try:
chips.bet = int(input("Enter your bet:"))
except ValueError:
print("Bet must be in integer form")
else:
if chips.bet>chips.chip:
print("Bet is outside ",chips.chip)
else:
break
Hit function is mainly creating the bound method problem
def Hit(deck,hand):
hand.add_card(deck.deal)
hand.adjust_for_ace()
def Hit_or_stand(deck,hand):
global playing
while True:
x = input("Would you like hit or stand(enter 'h' or 's'):")
if x[0].lower()=='h':
Hit(deck,hand)
elif x[0].lower()=='s':
playing = False
else:
print("Please try again.")
continue
break
while True:
print("Welcome to Blackjack! Get as close to 21 as possible without going over\n The dealer hits until it reaches 17.Aces count as 1 or 11")
deck = Deck()
deck.shuffle()
Manually calling the deal method successfully creates an instance of card class
player_hand = Hand()
player_hand.add_card(deck.deal())
player_hand.add_card(deck.deal())
dealer_hand = Hand()
dealer_hand.add_card(deck.deal())
dealer_hand.add_card(deck.deal())
player_chips = Chips()
Take_bet(player_chips)
show_some(player_hand,dealer_hand)
while playing:
'''
card instance creates a bound method
'''
Hit_or_stand(deck,player_hand)
show_some(player_hand,dealer_hand)
The issue is in the Hit function, you are trying to add a card to a hand but are passing the method deck.deal rather than the result of a call to deck.deal()
def Hit(deck,hand):
hand.add_card(deck.deal()) # <- here
hand.adjust_for_ace()

How to use have only one instance of a class in python

I can already tell that this question will be disliked and probably answered really quickly. Id like to preface this by letting you know that i have research this, but cant comprehend what to do.
So i have a python script that creates a game of cards. The card game in mind is 3's. A game that only (to my knowledge) my family knows how to play.
My script thus far is:
import math
import random
from itertools import product
def start_game():
print ("Game started")
deck = Deck()
deck.current_turn = random.randint(1,2)
print ("Player " + str(deck.current_turn) + " will go first.")
Round_Start()
def Round_Start():
deck = Deck()
p1down = Player1Down()
p2down = Player2Down()
p1up = Player1Up()
p2up = Player2Up()
if p1down.hidden == True:
print("P1: " + " - ".join(map(str,p1up.cards)))
print("P1: #/# - #/# - #/#")
else:
print("P1: " + " - ".join(map(str,p1up.cards)))
print("P1: " + " - ".join(map(str,p1down.cards)))
if p2down.hidden == True:
print("P2: " + " - ".join(map(str,p2up.cards)))
print("P2: #/# - #/# - #/#")
else:
print("P2: " + " - ".join(map(str,p2up.cards)))
print("P2: " + " - ".join(map(str,p2down.cards)))
Give_Turn()
def P1Turn():
print("It is now Player 1's turn.")
def P2Turn():
print("It is now Player 2's turn.")
def Give_Turn():
deck = Deck()
print(deck.current_turn)
if deck.current_turn == 2:
print("It is now Player 1's turn.")
P1Turn()
elif deck.current_turn == 1:
print("It is now Player 2's turn.")
P2Turn()
class Player1Down(object):
def __init__(self):
deck = Deck()
self.cards = deck.Deal(3)
self.hidden = True
class Player2Down(object):
def __init__(self):
deck = Deck()
self.cards = deck.Deal(3)
self.hidden = True
class Player1Up(object):
def __init__(self):
deck = Deck()
self.cards = deck.Deal(3)
class Player2Up(object):
def __init__(self):
deck = Deck()
self.cards = deck.Deal(3)
class Deck(object):
current_turn = 0
def __init__(self, ranks=None, suits=None):
if ranks is None:
ranks = range(2,15)
if suits is None:
suits = ["H","D","C","S"]
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)
class Card(object):
FACES = {11: 'J', 12: 'Q', 13: 'K', 14: 'A'}
def __init__(self, rank, suit):
self.suit = suit
self.rank = rank
def __str__(self):
value = self.FACES.get(self.rank, self.rank)
return "{0}/{1}".format(value, self.suit)
def __lt__(self, other):
return self.rank < other.rank
if __name__ == '__main__':
start_game()
Now some of the script is a direct copy paste from another users work, it was the only way i could get things to work up until this point.
My problem is that the
deck.current_turn
keeps resetting to 0. I figure this is because i have multiple instances of the Deck() class opened. But i do not know how to fix this.
My output from the current script is:
Game started
Player 2 will go first.
P1: 7/H - 9/H - J/H
P1: #/# - #/# - #/#
P2: 5/H - 3/S - 10/H
P2: #/# - #/# - #/#
0
This is my first Stack Exchange post, and I'm sorry if this is a dumb question.
The solutions proposed in comments are much better, but the quick and dirty approaches are Singleton and Borg.
The Singleton way:
class Deck(object):
_deck = None
def __new__(cls, *a, **k):
if not cls._deck:
cls._deck = object.__new__(cls, *a, **k)
return cls._deck
# and the rest as you have it above
The Borg way:
class Deck(object):
_dict = {}
def __init__(self, ranks=None, suits=None):
self.__dict__ = self._dict
# and the rest as you have it, inc. the rest of __init__
They both work. As the original inventor of Borg, I have a soft spot for it, of course, and you can still read my ancient essay about it at http://www.aleax.it/Python/5ep.html .

How to make cards work in python [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
This is my code so far:
import sys
import os
import random
Question():
os.system('cls')
SQ=input('Do you want to play blackjack y/n')
if(SQ == y or SQ == Y):
StartGame()
if(SQ == n or SQ == N):
sys.exit()
if(SQ != n and SQ != N and SQ != y and SQ != Y):
print('You did answer the question with a y or a n which correspond to yes and no accordingly')
Question()
Question()
StartGame():
slot1=False
slot2=False
slot3=False
slot4=False
slot5=False
slot6=False
slot7=False
slot8=False
slot9=False
slot10=False
slot11=False
slot12=False
slot13=False
slot14=False
slot15=False
slot16=False
slot17=False
slot18=False
slot19=False
slot20=False
slot21=False
slot22=False
slot22=False
slot23=False
Slot24=False
slot25=False
slot26=False
slot27=False
Slot28=False
slot29=False
slot30=False
slot31=False
slot32=False
slot33=False
slot34=False
slot35=False
slot36=False
slot37=False
slot38=False
slot39=False
slot40=False
slot41=False
slot42=False
slot43=False
slot44=False
slot45=False
slot46=False
slot47=False
slot48=False
slot49=False
slot50=False
slot51=False
slot52=False
aceHEART = randrange(1, 52)
aceHEART
I don't understand the correct way to make the slots and a random number generator together to make a random shuffle. How can i make it so it does not try to put more than one card in one slot. I also do not know how to manage these cards in a more efficient way. I am making a blackjack game in python and i do not know the correct way to approach this. Please help me in the best way you can.
Not sure what you're trying to do, but here is a way to generate a shuffled deck of cards:
ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
suite = ['clubs', 'hearts', 'spades', 'diamonds']
deck = [r + ' ' + s for r in ranks for s in suite]
random.shuffle(deck)
Or with objects:
class Card(object):
def __init__(self, rank, suite):
self.rank = rank
self.suite = suite
deck = [Card(r,s) for r in ranks for s in suite]
random.shuffle(deck)
Learn to love lists, and use numbers to represent cards, not strings. Here's a simple card class that should work nicely:
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __str__(self):
return "23456789TJQKA"[self.rank] + "cdhs"[self.suit]
Then create decks and hands that are just lists of cards:
deck = [ Card[r,s] for r in range(13) for s in range(4) ]
random.shuffle(deck)
Why you want to use lists for hands and decks, for example, is that dealing a card is simply:
hand.append(deck.pop())
Why you want to use numbers for card ranks instead of strings is to make it possible to add and compare them. You could add a "value" member to Mihai's code above, that would help. WIth mine, you just have to adjust the numbers a bit:
def bjTotal(hand):
total = 0
hasAce, isSoft = False, False
for card in hand:
if card.rank == 12:
hasAce = True
total += 1
elif card.rank > 7:
total + 10
else:
total += card.rank + 2
if hasAce and total < 12:
isSoft = True
total += 10
return total, isSoft
classes do a good job of representing cards and games
import random
class Card:
def __init__(self,rank,suit):
self.rank = rank
self.suit = suit
def rankName(self):
return "A23456789TJQK"[self.rank]
def suitName(self):
return "HCDS"[self.suit]
def __int__(self):
if self.rank > 8: return 10
if self.rank == 0:return 11
return self.rank + 1
def __eq__(self,other):
try:
return self.rank == other.rank
except:
return self.rank == other
def __str__(self):
return self.rankName() +self.suitName()
def __repr__(self):
return "<Card: %s>"%self
class Deck:
def __init__(self,cards=None):
if cards is None:
cards = [Card(rank,suit) for rank in range(13) for suit in range(4)]
self.cards = cards
random.shuffle(self.cards)
def draw(self):
return self.cards.pop(0)
def __add__(self,other):
return Deck(self.cards+other.cards)
class Hand:
def __init__(self):
self.cards = []
def __int__(self):
total = sum(map(int,self.cards))
aces = self.cards.count(0)
while aces > 0 and total > 21:
aces -= 1
total -= 10
return total
def put(self,card):
self.cards.append(card)
def __str__(self):
return ", ".join(map(str,self.cards))
def __repr__(self):
return "<Hand %s>"%self.cards
once you have your classes you can now start constructing your game
class Game:
def __init__(self,n_players=1,use_dealer=True):
self.main_deck = Deck()+Deck() # 2 deck shoe
self.n_players = n_players
self.dealer = use_dealer
def play_hand(self,hand):
while int(hand) <= 21 and raw_input("%r\nHit?"%hand)[0].lower() == "y" :
hand.put(self.main_deck.draw())
if int(hand) > 21:
print "BUST!!!"
def play_game(self):
current_player = 0
hands = [Hand() for _ in range(self.n_players+self.dealer)]
for i in range(2):
for hand in hands:
hand.put(self.main_deck.draw())
while current_player < len(hands) - self.dealer:
self.play_hand(hands[current_player])
current_player += 1
if self.dealer:
while int(hands[-1]) < 17:
hands[-1].put(self.main_deck.draw())
print "DEALER HITS:",hands[-1]
print "FINAL SCORES:"
print "\n".join("%s. %r %d"%(i,h,h) for i,h in enumerate(hands))
game = Game()
game.play_game()
(something like that anyway)

Blackjack game reshuffling problem-edited

I am trying to make a blackjack game where before each new round, the program checks to make sure that the deck has 7 cards per player. And if it doesn't, the deck clears, repopulates, and reshuffles. I have most of the problem down, but for some reason at the start of every deal it reshuffles the deck more than once, and I can't figure out why. Help, please.
Here's what I have so far:
(P.S. the imported cards and games modules aren't part of the problem, I'm fairly sure my problem lies in the deal() function of my BJ_Deck class.)
import cards, games
class BJ_Card(cards.Card):
""" A Blackjack Card. """
ACE_VALUE = 1
def get_value(self):
if self.is_face_up:
value = BJ_Card.RANKS.index(self.rank) + 1
if value > 10:
value = 10
else:
value = None
return value
value = property(get_value)
class BJ_Deck(cards.Deck):
""" A Blackjack Deck. """
def populate(self):
for suit in BJ_Card.SUITS:
for rank in BJ_Card.RANKS:
self.cards.append(BJ_Card(rank, suit))
def deal(self, hands, per_hand=1):
for rounds in range(per_hand):
if len(self.cards)>=7*(len(hands)):
print "Reshuffling the deck."
self.cards=[]
self.populate()
self.shuffle()
for hand in hands:
top_card=self.cards[0]
self.give(top_card, hand)
class BJ_Hand(cards.Hand):
""" A Blackjack Hand. """
def __init__(self, name):
super(BJ_Hand, self).__init__()
self.name = name
def __str__(self):
rep = self.name + ":\t" + super(BJ_Hand, self).__str__()
if self.total:
rep += "(" + str(self.total) + ")"
return rep
def get_total(self):
# if a card in the hand has value of None, then total is None
for card in self.cards:
if not card.value:
return None
# add up card values, treat each Ace as 1
total = 0
for card in self.cards:
total += card.value
# determine if hand contains an Ace
contains_ace = False
for card in self.cards:
if card.value == BJ_Card.ACE_VALUE:
contains_ace = True
# if hand contains Ace and total is low enough, treat Ace as 11
if contains_ace and total <= 11:
# add only 10 since we've already added 1 for the Ace
total += 10
return total
total = property(get_total)
def is_busted(self):
return self.total > 21
class BJ_Player(BJ_Hand):
""" A Blackjack Player. """
def is_hitting(self):
response = games.ask_yes_no("\n" + self.name + ", do you want a hit? (Y/N): ")
return response == "y"
def bust(self):
print self.name, "busts."
self.lose()
def lose(self):
print self.name, "loses."
def win(self):
print self.name, "wins."
def push(self):
print self.name, "pushes."
class BJ_Dealer(BJ_Hand):
""" A Blackjack Dealer. """
def is_hitting(self):
return self.total < 17
def bust(self):
print self.name, "busts."
def flip_first_card(self):
first_card = self.cards[0]
first_card.flip()
class BJ_Game(object):
""" A Blackjack Game. """
def __init__(self, names):
self.players = []
for name in names:
player = BJ_Player(name)
self.players.append(player)
self.dealer = BJ_Dealer("Dealer")
self.deck = BJ_Deck()
self.deck.populate()
self.deck.shuffle()
def get_still_playing(self):
remaining = []
for player in self.players:
if not player.is_busted():
remaining.append(player)
return remaining
# list of players still playing (not busted) this round
still_playing = property(get_still_playing)
def __additional_cards(self, player):
while not player.is_busted() and player.is_hitting():
self.deck.deal([player])
print player
if player.is_busted():
player.bust()
def play(self):
# deal initial 2 cards to everyone
self.deck.deal(self.players + [self.dealer], per_hand = 2)
self.dealer.flip_first_card() # hide dealer's first card
for player in self.players:
print player
print self.dealer
# deal additional cards to players
for player in self.players:
self.__additional_cards(player)
self.dealer.flip_first_card() # reveal dealer's first
if not self.still_playing:
# since all players have busted, just show the dealer's hand
print self.dealer
else:
# deal additional cards to dealer
print self.dealer
self.__additional_cards(self.dealer)
if self.dealer.is_busted():
# everyone still playing wins
for player in self.still_playing:
player.win()
else:
# compare each player still playing to dealer
for player in self.still_playing:
if player.total > self.dealer.total:
player.win()
elif player.total < self.dealer.total:
player.lose()
else:
player.push()
# remove everyone's cards
for player in self.players:
player.clear()
self.dealer.clear()
def main():
print "\t\tWelcome to Blackjack!\n"
names = []
number = games.ask_number("How many players? (1 - 7): ", low = 1, high = 8)
for i in range(number):
name = raw_input("Enter player name: ")
names.append(name)
print
game = BJ_Game(names)
again = None
while again != "n":
game.play()
again = games.ask_yes_no("\nDo you want to play again?: ")
main()
raw_input("\n\nPress the enter key to exit.")
Since someone decided to call this 'psychic-debugging', I'll go ahead and tell you what the modules are then.
Here's the cards module:
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, face_up = True):
self.rank = rank
self.suit = suit
self.is_face_up = face_up
def __str__(self):
if self.is_face_up:
rep = self.rank + self.suit
else:
rep = "XX"
return rep
def flip(self):
self.is_face_up = not self.is_face_up
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) + "\t"
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)
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 "Can't continue deal. Out of cards!"
if name == "main":
print "This is a module with classes for playing cards."
raw_input("\n\nPress the enter key to exit.")
And here's the games module:
class Player(object):
""" A player for a game. """
def __init__(self, name, score = 0):
self.name = name
self.score = score
def __str__(self):
rep = self.name + ":\t" + str(self.score)
return rep
def ask_yes_no(question):
"""Ask a yes or no question."""
response = None
while response not in ("y", "n"):
response = raw_input(question).lower()
return response
def ask_number(question, low, high):
"""Ask for a number within a range."""
response = None
while response not in range(low, high):
response = int(raw_input(question))
return response
if name == "main":
print "You ran this module directly (and did not 'import' it)."
raw_input("\n\nPress the enter key to exit.")
You're checking it again and again, inside the loop, and while you distribute the cards, the deck is being reduced, I think (can't see the Deck.give method on your code to know for sure).
You probably want to check only once, move the check to outside the loop.
def deal(self, hands, per_hand=1):
for rounds in range(per_hand):
if len(self.cards) <= 7 * len(hands):
print "Reshuffling the deck."
self.cards = []
self.populate()
self.shuffle()
for hand in hands:
top_card=self.cards[0]
self.give(top_card, hand)
Nosklo pointed out one problem (checking it inside the loop) but there is a second problem.
the condition
if len(self.cards)>=7*(len(hands)):
is checking if the number of cards is greater than the number needed and if so,clears the deck, populates and shuffles.
When combined with the check inside the loop, it will repopulate and shuffle the deck every time it starts another round.
So you probably want something like:
if len(self.cards) <= 7*(len(hands)):
print "Reshuffling the deck."
self.cards=[]
self.populate()
self.shuffle()
for rounds in range(per_hand):
for hand in hands:
top_card=self.cards[0]
self.give(top_card, hand)
for hand in hands:
Do you really want to run that logic for each hand?

Categories

Resources