patternpythonMinor
Text-based Blackjack game in Python
Viewed 0 times
textgamepythonbasedblackjack
Problem
I am new to programming in Python and wrote a simple Blackjack project. I am looking for experienced peers to provide a high level code review about the overall design patterns and proper usages. The code performs as expected and passes all my unit tests so I am mainly looking for feedback on how to make the code more clean, modular, and conforming to some of the most common best practices. I am especially nervous about all the variables I am passing and the use (or misuse) of global variables.
P.S. On a side note, for the blackjack players out there I did not implement the "double down" feature yet.
```
import sys
import os
import random
deck = None
stats = {'player':{'win':0,'lose':0,'tie':0,'blackjack':0},'dealer'{'win':0,'lose':0,'tie':0,'blackjack':0}}
history = {'bets':[]} #use later to log playing history
def NewBlackjackGame():
player = Player("Player 1")
dealer = Player("Dealer")
game = Game(player, dealer,[])
round_number = 1
while player.credits > 1:
print('### Round ' + str(round_number) + ' ###')
game.play_round()
round_number = round_number + 1
if player.credits ') #don't show the card in the hole
if self.owner == "Dealer" and dealer_turn==1:
print(self.owner + ' current hand: ' + str(self.cards) + ' for a total of: ' + str(self.get_total()))
def draw_card(self):
global deck
new_card = deck.draw()
self.cards.append(new_card)
self.values.append(deck.values_lookup[new_card])
#automatically take care of the "soft" "hard" business
if "A" in self.cards:
self.adjust_ace_value()
self.total = self.get_total()
def adjust_ace_value(self):
global deck
total_of_non_ace_cards = sum(deck.values_lookup[i] for i in self.cards if i != 'A')
if total_of_non_ace_cards ')
if choice == "s":
return 0
else:
return 1
def increment_stats(self,player,cat):
P.S. On a side note, for the blackjack players out there I did not implement the "double down" feature yet.
```
import sys
import os
import random
deck = None
stats = {'player':{'win':0,'lose':0,'tie':0,'blackjack':0},'dealer'{'win':0,'lose':0,'tie':0,'blackjack':0}}
history = {'bets':[]} #use later to log playing history
def NewBlackjackGame():
player = Player("Player 1")
dealer = Player("Dealer")
game = Game(player, dealer,[])
round_number = 1
while player.credits > 1:
print('### Round ' + str(round_number) + ' ###')
game.play_round()
round_number = round_number + 1
if player.credits ') #don't show the card in the hole
if self.owner == "Dealer" and dealer_turn==1:
print(self.owner + ' current hand: ' + str(self.cards) + ' for a total of: ' + str(self.get_total()))
def draw_card(self):
global deck
new_card = deck.draw()
self.cards.append(new_card)
self.values.append(deck.values_lookup[new_card])
#automatically take care of the "soft" "hard" business
if "A" in self.cards:
self.adjust_ace_value()
self.total = self.get_total()
def adjust_ace_value(self):
global deck
total_of_non_ace_cards = sum(deck.values_lookup[i] for i in self.cards if i != 'A')
if total_of_non_ace_cards ')
if choice == "s":
return 0
else:
return 1
def increment_stats(self,player,cat):
Solution
You should follow the style guide. I was thinking that
There is no point having a
You only need to hold the values for face cards in the
Note the use of
As already pointed out, don't hard-code the name of the
Then play like:
I think the main issue is having logic in odd places. I would suggest a structure like the following:
Even within the existing classes, your logic is all over the place. Consider this simplified implementation of
Now there is only one attribute the
Here is a rough UML diagram showing what I mean:
NewBlackjackGame was an odd name for a class, but it turns out it's a function; it should therefore be new_blackjack_game. (That being said, I think all of the logic in that function should really be in Game, see below.)There is no point having a
main that just calls one other function. Either rename that function to main, or call the function directly if __name__ == "__main__":.You only need to hold the values for face cards in the
values_lookup, which should be a class attribute (it's the same for all cards/decks). The card's value can just be an integer for non-face cards, then you can dict.get either the face card value or use it directly. For example:class Card():
FACES = {'Ace': 1, 'Jack': 10, 'Queen': 10, 'King': 10}
def __init__(self, face, suit):
self.face = face
self.suit = suit
def __str__(self):
return "{0.face} of {0.suit}".format(self)
@property
def value(self):
return self.FACES.get(self.face, self.face)Note the use of
@property, which is a Pythonic way to implement the getters and setters used in other languages. In use:>>> c = Card("Ace", "spades")
>>> str(c)
'Ace of spades'
>>> c.value
1
>>> c = Card(9, "diamonds")
>>> str(c)
'9 of diamonds'
>>> c.value
9As already pointed out, don't hard-code the name of the
Players. If you keep the Dealer separate, you can have as many others as you like:class Game():
def __init__(self, *players, start_credit=100):
self.dealer = Dealer()
self.deck = Deck()
self.players = [Player(player, start_credit) for player in players]
...Then play like:
game = Game("Anna", "Bob", "Chris")
game.play() # replaces `NewBlackjackGame`I think the main issue is having logic in odd places. I would suggest a structure like the following:
Card- holds the value and suit of an individual card;
Deck- holds the cards and the logic for shuffling and dealing;
Hand- another collection of cards, with the logic for adding up scores. The hand doesn't need to know itsowner, the logic for whichCards in theHandto show should be in thePlayer. It shouldn't access a globaldeck- implement e.g.def draw_from(self, deck):;
Player- a player has aHandofCards, the input logic (hit_or_stand, which should incorporate validation), the rules for showing the cards, theircreditandstats, etc.;
Dealer- a subclass ofPlayerholding the dealer's playing logic and the different rules for showing itshand;
Game- holds the deck and the players and the logic for progressing through the game, including e.g.history, which should beGameinstance attributes rather thanglobalvariables.
Even within the existing classes, your logic is all over the place. Consider this simplified implementation of
Hand:class Hand():
def __init__(self):
# initialise the empty list of cards
def draw_from(self, deck):
# draw a card from the deck and add to list
def return_to(self, deck):
# empty list of cards and return to deck
@property
def value(self):
# all the value logic goes hereNow there is only one attribute the
Hand instance needs - the cards it holds. You don't have to do everything in value (you could have "private" _helper_functions), but you don't need to recalculate a total or hold a separate list of values.Here is a rough UML diagram showing what I mean:
Code Snippets
class Card():
FACES = {'Ace': 1, 'Jack': 10, 'Queen': 10, 'King': 10}
def __init__(self, face, suit):
self.face = face
self.suit = suit
def __str__(self):
return "{0.face} of {0.suit}".format(self)
@property
def value(self):
return self.FACES.get(self.face, self.face)>>> c = Card("Ace", "spades")
>>> str(c)
'Ace of spades'
>>> c.value
1
>>> c = Card(9, "diamonds")
>>> str(c)
'9 of diamonds'
>>> c.value
9class Game():
def __init__(self, *players, start_credit=100):
self.dealer = Dealer()
self.deck = Deck()
self.players = [Player(player, start_credit) for player in players]
...game = Game("Anna", "Bob", "Chris")
game.play() # replaces `NewBlackjackGame`class Hand():
def __init__(self):
# initialise the empty list of cards
def draw_from(self, deck):
# draw a card from the deck and add to list
def return_to(self, deck):
# empty list of cards and return to deck
@property
def value(self):
# all the value logic goes hereContext
StackExchange Code Review Q#56695, answer score: 9
Revisions (0)
No revisions yet.