[Edited: added V2 link] BlackJack V2 - Github This is my first post, and I was actually going to post this on Stackoverflow when I was rather suggested posting the code here, as it may be better suited.
Here is the github repository, if that is allowed here, as it seems easy to receive suggestions.
I am looking for criticism as everything I have learned has been self taught, and mainly in this manner. Self-teach, practice, build, post, refine, move on.
The first bit of code would be the inherited code.
import time, itertools, random
def countyDots():
for i in range(5):
line = "." * i
print(line, end="")
print("\r", end="")
time.sleep(0.5)
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
self.valueChart = {
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"10": 10,
"J": 10,
"Q": 10,
"K": 10,
"A": 11,
}
def getValues(self):
cardValues = self.valueChart
returnedValue = 0
for rank, value in cardValues.items():
if self.rank == rank:
returnedValue = value
return returnedValue
class Player(object):
def __init__(self):
self.name = self.defineName()
#initialize player bank to 200ドル
self.balance = 200
print("Beginning Balance: 200ドル.00 USD\n")
##initialize player hand
self.hand = []
#initialize score and currentBet to 0
self.score = 0
self.bet = 0
def placeBet(self):
currentBet = input(f"BALANCE: ${self.balance}\nHow much would you like to bet? (1, 2, 5, 10, 25): ")
def is_number(n):
try:
int(n)# Type-casting the string to `float`.
# If string is not a valid `float`,
# it'll raise `ValueError` exception
except ValueError:
return False
return True
if is_number(currentBet):
currentBet = int(currentBet)
if currentBet in (1, 2, 5, 10, 25):
self.balance -= currentBet
self.bet = currentBet
print(f"\nCurrent Bet: {currentBet}\nBALANCE: {self.balance}\n")
else:
self.placeBet()
else:
self.placeBet()
def defineName(self):
#Ask for name from user
newName = input("What is your Name?: ")
#confirm new name with user
confirmation = input(f"Your name is {newName}, correct? (Y/N): ")
#convert confirmation to all lowercase and eliminate whitespace
confirmation = confirmation.lower().strip()
while confirmation != "y":
newName = input("Sorry. What is your Name?: ")
countyDots()
confirmation = input(f"So you prefer to be called {newName}? (Y/N): ")
confirmation = confirmation.lower().strip()
if confirmation == "y":
return newName
def showHand(self):
print(f"{self.name}'s HAND")
for card in self.hand:
rank = card[0]
suit = card[1]
print(f"{rank} of {suit}")
def getAction(self):
action = input("Would you like to HIT or STAND?: ")
action = action.lower().strip()
return action
def calculateScore(self):
self.score = 0
for card in self.hand:
rank = card[0]
suit = card[1]
card = Card(rank, suit)
value = card.getValues()
self.score += value
print(f"{self.name}'s SCORE: {self.score}\n")
return self.score
def showBalance(self):
print(f"BALANCE: {self.balance}")
class Dealer(object):
def __init__(self):
self.name = "Dealer"
#initialize a dealer score to 0
self.score = 0
#initialize a dealer hand
self.hand = []
#initialize a dealer bank of 1,000,000
self.bank = 1000000
def showHand(self):
print(f"{self.name}'s HAND")
for card in self.hand:
rank = card[0]
suit = card[1]
print(f"{rank} of {suit}")
def calculateScore(self):
self.score = 0
for card in self.hand:
rank = card[0]
suit = card[1]
card = Card(rank, suit)
value = card.getValues()
self.score += value
print(f"{self.name}'s SCORE: {self.score}\n")
return self.score
class Deck(object):
def __init__(self):
self.deck = []
self.ranks = (
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"J",
"Q",
"K",
"A",
)
def buildDeck(self):
suits = ("Spades ♠", "Clubs ♣", "Hearts ♥", "Diamonds ♦")
cards = list(itertools.product(self.ranks, suits))
random.shuffle(cards)
for card in cards:
self.deck.append(card)
return self.deck
class Shoe(Deck):
def __init__(self):
self.shoe = []
def buildShoe(self):
for i in range(5):
newDeck = Deck()
newDeck.buildDeck()
for card in newDeck.deck:
self.shoe.append(card)
def dealCard(self):
gameDeck = self.shoe
dealtCard = self.shoe[0]
self.shoe.pop(0)
return dealtCard
The Second bit of code is the gameLogic file which executes at the bottom.
class GameLogic(object):
def __init__(self, player, dealer, shoe):
self.player = player
self.dealer = dealer
self.gameShoe = shoe
def beginGame(self):
self.dealer.hand = []
self.player.hand = []
#initalize the players first bet
self.player.placeBet()
#initialize the shoe of cards (5 decks of cards)
self.gameShoe.buildShoe()
self.player.hand.append(self.gameShoe.dealCard())
self.dealer.hand.append(self.gameShoe.dealCard())
self.player.hand.append(self.gameShoe.dealCard())
self.dealer.hand.append(self.gameShoe.dealCard())
self.player.showHand()
self.player.calculateScore()
countyDots()
self.dealer.showHand() #These need to be changed so that they reflect one face up card
self.dealer.calculateScore() #and one face down card
countyDots()
self.playerAction(self.player.getAction())
def keepPlaying(self):
userResponse = input("\nPRESS ENTER FOR NEXT HAND\nType 'EXIT' to quit game\n")
userResponse = userResponse.lower().strip()
if userResponse == "":
self.beginGame()
elif userResponse == "exit":
exit()
else:
self.keepPlaying()
def playerAction(self, action):
if action == "hit":
print("You chose to Hit")
#player takes on an additional card
self.player.hand.append(self.gameShoe.dealCard())
self.player.showHand()
self.player.calculateScore()
if self.player.score <= 21:
newAction = self.player.getAction()
self.playerAction(newAction)
elif self.player.score > 21:
print("BUSTED!!! YOU LOSE")
#Proceed to
self.dealer.showHand()
#set currentbet to 0
self.player.bet = 0
self.keepPlaying()
#show dealers hand for good faith
#set player bet to 0
#set forth with "would you like to play another round"
#if player total score isnt higher than 21, we ask the same question
#if the player total score is higher than 21, we end the game immediately and proceed to ask endgame()
elif action == "stand":
print("You chose to STAND\n")
#if action is to stand then we need to proceed forward with the game logic for the dealer.
self.dealer.showHand()
self.dealer.calculateScore()
while self.dealer.score < 16:
countyDots()
self.dealer.hand.append(self.gameShoe.dealCard())
self.dealer.showHand()
self.dealer.calculateScore()
if self.dealer.score > 21:
print("The dealer has busted!")
#pay the player the player 2times their bet
payout = self.player.bet * 2
self.player.balance += payout
print(f"{self.player.name} WON {self.player.bet}")
self.player.showBalance()
#reset bet to 0 for the next round
self.player.bet = 0
self.keepPlaying()
elif self.dealer.score <= 21 and self.dealer.score >= 16:
if self.player.score > self.dealer.score:
print("The player WINS!")
#pay the player the player 2times their bet
payout = self.player.bet * 2
self.player.balance += payout
print(f"{self.player.name} WON {self.player.bet}")
self.player.showBalance()
#reset bet to 0 for the next round
self.player.bet = 0
self.keepPlaying()
elif self.player.score <= self.dealer.score:
print("The PLAYER LOSES!")
print(f"{self.player.name} LOST {self.player.bet}")
self.player.showBalance()
#reset bet to 0 for the next round
self.player.bet = 0
self.keepPlaying()
else:
print("Some Real Bad shit happened herrrrrr")
#initialize a dealer
dealer1 = Dealer()
#initialize a player
player1 = Player()
#initialize a game shoe
gameShoe = Shoe()
#Initialize GamePlay
game = GameLogic(player1, dealer1, gameShoe)
#Begin Game Play
game.beginGame()
-
3\$\begingroup\$ As the first review doesn't mention it: Follow the Style Guide for Python Code. \$\endgroup\$greybeard– greybeard2022年10月04日 07:28:08 +00:00Commented Oct 4, 2022 at 7:28
-
\$\begingroup\$ Followup question posted here \$\endgroup\$AJNeufeld– AJNeufeld2022年10月07日 16:11:46 +00:00Commented Oct 7, 2022 at 16:11
4 Answers 4
Looks like you are from different language background. To name methods, you should use underscores rather than camelcase. For example the first method should be named based on that
county_dots
Consider using typing hints, they will increase some basic readability of your code - for example it's not completely obvious, what date type is the card rank - if it is the number or string as per your
valueChart
variable.I expect the
Card
attributevalueChart
is basically constant. It doesn't have to be instance variable, move this outside of the class or declare it on the class level.I would rename
getValues
to justvalues
(Java background? :) )Rather to see comments, I'd sometimes just name the functions and parameters better so that the function call has the same information value as the comment and therefore you can remove the comment. For example:
#initialize the shoe of cards (5 decks of cards) self.gameShoe.buildShoe()
Perhaps name the function better and even pass decks=5
as named parameter to make it clear, it is about 5 decks of cards:
self.gameShoe.initialize(decks=5)
Another example, where I see the comment pointless even like this, just reducing readability. I would also consider creating "reset_bet" method instead so that your code shows actions rather than modifying data:
#set currentbet to 0
self.player.bet = 0
- Make smaller methods, extract for better readability and extensibility.
Example:
if self.player.score <= 21:
Can be improved to:
if self.player.still_in_play():
(name is questionable, maybe you will think of a better one).
I would rename all
showXX
methods toprint_XX
.I am not sure what is a type of architecture you are trying to do, but if it is OOP with DDD, etc., there is way too much code in your
GameLogic
class. Some of it should be closer the the classes themselves. Your approach seems a bit more functional, having classes mostly as data containers so maybe that's what you want. Then I would consider@dataclass
annotation and remove all logic from those completely (also some of my previous comments wouldn't make sense). But I don't think that is the way you probably wanted. Edit: Just noticed the title, ha! My bad :)
-
1\$\begingroup\$ My man, I appreciate this so much. I do not have to ability to have a formal mentor for coding. I sold my business and picked programming back up a year ago, beginning with Harvard's CS50 online course. Since then I have been in and out of speaking spaces (which usually become toxic or heavily filled with web security, so it still gets toxic.) My cousin is high up in SONY but I cant get 30 mins a week with my dude, so I am left to self teaching and hoping the 4k Im paying General assembly to teach me javascript is all worth it. The best advice I was given was just BUILD STUFF and learn. \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月04日 16:59:02 +00:00Commented Oct 4, 2022 at 16:59
-
1\$\begingroup\$ Cool! So far I found this community very welcoming and always getting good feedback :) \$\endgroup\$K.H.– K.H.2022年10月04日 18:10:41 +00:00Commented Oct 4, 2022 at 18:10
-
\$\begingroup\$ V2 added above! \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月07日 17:23:25 +00:00Commented Oct 7, 2022 at 17:23
Python 3 Classes
class Card(object):
is deprecated Python 2 syntax. With Python 3, the (object)
base class is implied and just unnecessary clutter. Just write class Card:
.
One Source of Truth
Each Card
has a dictionary converting the card rank to a value. With 52 cards in a deck, you'll have 52 identical dictionaries. With 5 decks in the shoe, you'll have 260 identical dictionaries!
Additionally, each Deck
has a tuple of ranks
. 5 decks yields 5 identical tuples of card ranks.
(Again, additionally, a Shoe
inherits from Deck
(brokenly, more later), so even more copies???)
You should specify the card ranks exactly once in your program, in one global structure. An Enum
would be a good choice for this:
from enum import Enum
class Rank(Enum):
TWO = ('2', 2)
THREE = ('3', 3)
FOUR = ('4', 4)
FIVE = ('5', 5)
SIX = ('6', 6)
SEVEN = ('7', 7)
EIGHT = ('8', 8)
NINE = ('9', 9)
TEN = ('10', 10)
JACK = ('J', 10)
QUEEN = ('Q', 10)
KING = ('K', 10)
ACE = ('A', 11)
@property
def symbol(self):
return self.value[0]
@property
def points(self):
return self.value[1]
Similarly, you could use an Enum
for suits:
class Suit(Enum):
CLUBS = ('Clubs', '♣')
DIAMONDS = ('Diamonds', '♦')
HEART = ('Hearts', '♥')
SPADES = ('Spades', '♠')
@property
def suit_name(self):
return self.value[0]
@property
def symbol(self):
return self.value[1]
Card
A card is pair of values: rank & suit. But more than that, it has a string which describes it, like "Queen of Clubs ♣"
. You use f"{rank} of {suit}"
in two different places to convert the a card (actually a tuple) into a suitable string. Since you have a Card
class, it should be able to convert itself into a suitable string.
A card has a point value, which (in the case of blackjack) is determined solely by the card's rank. But we shouldn't ask the card for its rank and then convert that into a point value, rather we should just ask the card for its point value, and let it determine its value from its rank & suit.
Cards are immutable. If you have a Card
, you shouldn't be able to change it to another suit or rank with a statement like card.rank = 'A'
. We can use a dataclass
to make our Card
object resistant to malicious changes:
from dataclasses import dataclass
...
@dataclass(frozen=True)
class Card:
rank: Rank
suit: Suit
def point_value(self):
return self.rank.points
def __str__(self):
return f"{self.rank.symbol} of {self.suit.suit_name} {self.suit.symbol}"
>>> card = Card(Rank.ACE, Suit.SPADES)
>>> print(card)
A of Spades ♠
>>> print(card.point_value())
11
Deck
class Deck(object):
def __init__(self):
self.deck = []
Strange. If I create a deck of cards, deck = Deck()
, it doesn't have any cards it in!
The constructor for a deck of cards should actually create a deck of cards:
class Deck(object):
def __init__(self):
self.deck = [Card(rank, suit) for rank, suit in itertools.product(Rank, Suit)]
Note the above actually creating a list of Card
objects here, not simply tuples of rank and suit values.
Your Deck
class seems rather limited in its functionality. You can't return cards to the deck, you can't shuffle the remaining cards in the deck, and so on.
Shoe
class Shoe(Deck):
def __init__(self):
self.shoe = []
As written, a Shoe
is a Deck
, but without any of the functionality of a Deck
. Although it inherits from Deck
, since super().__init__()
is never called, this inheritance is broken. Show().buildDeck()
will give an AttributeError
.
A Shoe
is not be a Deck
; don't inherit from it.
The shoe is constructed from 5 decks, sequentially! Imagine you have 5 decks, with red, yellow, green, blue, and orange backs. You're shuffling each of those decks individually, and then stacking them one on top of the other in the shoe without shuffling them together. This makes it impossible (instead of just unlikely) to draw three "Ace of Spaces ♠" sequentially from the shoe.
You should add cards from the 5 decks into the shoe, and THEN shuffle the shoe afterwards.
-
\$\begingroup\$ Enumerations are something that I am not as comprehensively involved with yet. As mentioned above, I have noted every suggestion and criticism here. I am reviewing more documentation and will be rewriting the game with these thoughts/criticisms in mind. My code did/does naturally feel unnecessarily verbose. I appreciate every word here and will be posting a, hopefully, better version. \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月04日 20:35:45 +00:00Commented Oct 4, 2022 at 20:35
-
\$\begingroup\$ I cant express enough again how much I appreciate comments like this. The amount of direction and knowledge you imparted on me is empowering. Thank you. \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月05日 17:10:08 +00:00Commented Oct 5, 2022 at 17:10
-
\$\begingroup\$ When you are "posting a, hopefully, better version", ensure you add a link in this question post to the new post, and a link from the new post back to this one. See Posting a new question for details. \$\endgroup\$AJNeufeld– AJNeufeld2022年10月06日 16:02:53 +00:00Commented Oct 6, 2022 at 16:02
-
\$\begingroup\$ Thank you My friend. I appreciate everything. I am looking forward to the day I can join a team and continually be surrounded by more and more experienced people. \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月06日 22:30:31 +00:00Commented Oct 6, 2022 at 22:30
-
\$\begingroup\$ V2 added above! \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月07日 17:23:46 +00:00Commented Oct 7, 2022 at 17:23
Unwanted recursion
Several of your methods contain unwanted recursions, i.e. these methods contain calls to themselves. I noticed that in (as in Player.placeBet()
, GameLogic.keepPlaying()
, and GameLogic.playerAction()
, but there may be more. This is very dangerous – it basically means that if you play long enough, your game will run out of memory, and will crash with a RecursionError
.
This means that you will have to revise these sections, probably by making use of appropriate while
loops. Here's a revision of placeBet()
– note the use of the while True
loop, which is a very popular Python idiom. You may want to adopt for the other problematic methods as well.
def place_bet(self):
while True:
response = input(f"BALANCE: ${self.balance}\n"
"How much would you like to bet? (1, 2, 5, 10, 25): ")
try:
bet = int(response)
except ValueError:
print("Please enter a number!")
else:
if bet not in (1, 2, 5, 10, 25):
print("Only bets of 1, 2, 5, 10, or 25 are possible.")
else:
self.balance -= bet
self.bet = bet
print(f"\nCurrent Bet: {currentBet}\nBALANCE: {self.balance}\n")
break
Too many local variables
Many of your methods contain unnecessary local variables. For instance, Card.getValues()
creates cardValues
(a copy of valueChart
), as well as the variable returnedValues
. You iterate through cardValues
by creating two more loop variables rank
and value
, which are also unneeded: basically, all you need here is this:
def getValues(self):
return self.valueChart[self.rank]
Given that, you may consider getting rid of getValues()
in the first place as it could be replaced by a simple dictionary look-up.
Overpowered method
The method GameLogic.playerAction()
is overpowered – it does way too much, much more than you'd expect. Not only does it handle the selected action, it also seems to be responsible for the dealer's reaction e.g. when the player loses, it's responsible for the cashout, and it resets for the next round. You really need to break up the logic here into smaller bits.
Wrong, missing, or dangerous game behavior
Your code currently behaves somewhat unexpectedly at times. Things that I've noticed:
- It's possible to bet more than you currently have
- Shouldn't you insta-win if your starting hand is a blackjack?
- Your game is very unforgiving when it comes to typing mistake (e.g.
hir
instead ofhit
). Mistakes like this should be caught e.g. by anotherwhile
loop.
-
1\$\begingroup\$ I appreciate all of this constructive criticism. I am running through some more information and documentation now, and I will start rewriting the game with all of this advice in mind. I really do appreciate every word that I received here. This is how I have learned to best self-teach. \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月04日 20:29:47 +00:00Commented Oct 4, 2022 at 20:29
-
1\$\begingroup\$ I am currently rebuilding the program and when I came to use the try/except, in the exact same manner you have mentioned above, the error I get is a ValueError instead of a TypeError. I say this not to point out any potential error, but because I do not understand WHY it is a ValueError versus a TypeError ValueError: invalid literal for int() with base 10: \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月05日 20:37:28 +00:00Commented Oct 5, 2022 at 20:37
-
\$\begingroup\$ @InfiniteGrasp: Good catch! You can stop trying to understand why your code doesn't behave like mine – I've made a mistake: Trying to convert an invalid string to an integer does indeed throw a
ValueError
and not aTypeError
. I've fixed my answer in response to your comment. \$\endgroup\$Schmuddi– Schmuddi2022年10月05日 22:12:35 +00:00Commented Oct 5, 2022 at 22:12 -
\$\begingroup\$ I actually submitted the question to stackoverflow because I am genuinely curious. I moved past and am not trying to actually correctly build the deck of cards, which is easier than I thought now that I am getting the gist of enumerations. To the above though, it seems like int() itself accepts all types, but only will accept values of base 10...This is the best I could come up with as to why this was the correct reasoning. Is this in the ball park? \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月05日 22:16:09 +00:00Commented Oct 5, 2022 at 22:16
-
\$\begingroup\$ V2 added above! \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月07日 17:23:34 +00:00Commented Oct 7, 2022 at 17:23
A lot have been said by the others and I feel that it may be unnecessary to repeat them. I also have another take on the problem you presented:
import random
import time
class Card:
cards_values = {
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"10": 10,
"J": 10,
"Q": 10,
"K": 10,
"A": 11,
}
allowed_suits = {
"Spades ♠",
"Clubs ♣",
"Hearts ♥",
"Diamonds ♦"
}
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
@property
def value(self):
return self.cards_values[self.rank]
class Participant:
def __init__(self, name, balance):
self.name = name
self.balance = balance
self.clear_hand()
def receive_card(self, card):
self.hand.append(card)
self.score += card.value
def clear_hand(self):
self.hand = []
self.score = 0
def wins(self, amount):
self.balance += amount
def loses(self, amount):
self.balance -= min(amount, self.balance)
@staticmethod
def ask_user_for_choice(question, choices, please_give_good_answer):
not_chosen = True
while not_chosen:
answer = input(question).upper().strip()
if not_chosen := (answer not in {x.upper().strip() for x in choices}):
print(please_give_good_answer)
return answer
def show_hand_and_score(self):
# Show dots
self.show_dots()
hand = "\n".join(
(
f"{self.name}'s hand:",
'\n'.join(
f"{current_card.rank} of {current_card.suit}"
for current_card in self.hand
)
)
)
print(hand)
print(f"{self.name}'s score: {self.score}")
@staticmethod
def show_dots():
for nb_dots in range(5):
print("." * nb_dots, "\r", end='')
time.sleep(0.5)
print()
def show_balance(self):
print(f"{self.name}'s BALANCE: {self.balance}$")
def has_busted(self):
if has_busted := self.score > 21:
print(f"{self.name} has busted!")
return has_busted
class Player(Participant):
def ask_bet(self):
self.show_balance()
current_bet = int(
self.ask_user_for_choice(
f"How much would you like to bet? (1, 2, 5, 10, 25): ",
("1", "2", "5", "10", "25"),
"Please only choose your bet as one of these choices: 1, 2, 5, 10 or 25"
)
)
if current_bet > self.balance:
raise ValueError(f'Balance (${self.balance})is not big enough for bet (${current_bet})')
return current_bet
def ask_if_hit_is_next_move(self):
player_action = self.ask_user_for_choice(
"Would you like to HIT or STAND? ",
('HIT', 'STAND'),
'Please only answer "HIT" or "STAND"'
)
return player_action == 'HIT'
def has_busted_after_hitting(self, card_received):
self.receive_card(card_received)
self.show_hand_and_score()
return self.has_busted ()
def has_won(self, dealers_score):
return self.score > dealers_score
def wins(self, current_bet):
super().wins(current_bet)
print(f"{self.name} WON {current_bet}$")
def loses(self, current_bet):
super().loses(current_bet)
print(f"{self.name} LOST {current_bet}$")
def ask_if_we_continue(self):
player_answer = self.ask_user_for_choice(
"\nPRESS ENTER FOR NEXT HAND\nType 'EXIT' to quit game\n",
('', 'EXIT'),
'Please only press enter for next hand or type "EXIT" to quit the game'
)
return player_answer == 'EXIT'
class Dealer(Participant):
def __init__(self, name, balance, nb_decks):
super().__init__(name, balance)
self.nb_decks = nb_decks
self.shoe = None
def deal_card(self):
if not self.shoe:
self.create_shoe()
return next(self.shoe)
def create_shoe(self):
def create_deck():
cards = [
Card(current_rank, current_suit)
for current_rank in Card.cards_values
for current_suit in Card.allowed_suits
]
random.shuffle(cards)
return cards
self.shoe = (
current_card
for _ in range(self.nb_decks)
for current_card in create_deck()
)
def has_busted_after_getting_cards(self):
self.show_hand_and_score()
while self.score < 16:
self.receive_card(self.deal_card())
self.show_hand_and_score()
return self.has_busted()
def has_enough_funds_for_bet(self, current_bet):
return self.balance >= current_bet
def already_won(self):
return self.score == 21
def show_hand_and_score(self, hidden=False):
if hidden:
self.show_dots()
hand = "\n".join(
(
f"{self.name}'s hand:",
f"{self.hand[0].rank} of {self.hand[0].suit}",
"(hidden card)"
)
)
print(hand)
else:
super().show_hand_and_score()
class Blackjack_table:
def __init__(self, bank=1000000, nb_decks=5, default_player_starting_bablance=200):
name_not_confirmed = True
while name_not_confirmed:
player_name = input("What name do you want to use? ")
name_not_confirmed = Player.ask_user_for_choice(
f"Is your name {player_name} (Y/N)? ",
'YN',
'Please answer only "Y" or "N"'
) == 'N'
self.player = Player(player_name, default_player_starting_bablance)
self.dealer = Dealer("Dealer", bank, nb_decks)
def play_rounds(self):
finished_playing = False
while not finished_playing:
if self.player.balance <= 0:
print("You don't have any funds left")
break
if self.dealer.balance <= 0:
print("The bank has lost all their funds.")
break
self.player.clear_hand()
self.dealer.clear_hand()
try:
self.current_bet = self.player.ask_bet()
except ValueError as bet_error:
print(bet_error.args[0])
continue
if not self.dealer.has_enough_funds_for_bet(self.current_bet):
print("Sorry, the dealer doesn't have enough funds for this bet")
self.dealer.show_balance()
finished_playing = self.player.ask_if_we_continue()
continue
for _ in range(2):
self.player.receive_card(self.dealer.deal_card())
self.dealer.receive_card(self.dealer.deal_card())
self.player.show_hand_and_score()
self.dealer.show_hand_and_score(hidden=True)
if self.player.has_busted():
self.dealer_wins()
finished_playing = self.player.ask_if_we_continue()
continue
if self.dealer.has_busted():
self.player_wins()
finished_playing = self.player.ask_if_we_continue()
continue
round_is_not_finished = True
while round_is_not_finished:
if self.player.ask_if_hit_is_next_move():
if self.player.has_busted_after_hitting(self.dealer.deal_card()):
self.dealer.show_hand_and_score()
self.dealer_wins()
round_is_not_finished = False
# round_is_not_finished stays True otherwise
else: # player is standing
if self.dealer.has_busted_after_getting_cards():
self.player_wins()
else:
if self.player.has_won(self.dealer.score):
self.player_wins()
else:
self.dealer_wins()
round_is_not_finished = False
finished_playing = self.player.ask_if_we_continue()
print("Gave over. Hope to see you sson!")
def player_wins(self):
print("The player WINS!")
self.player.wins(self.current_bet)
self.dealer.loses(self.current_bet)
self.player.show_balance()
self.dealer.show_balance()
def dealer_wins(self):
print("The player LOSES!")
self.dealer.wins(self.current_bet)
self.player.loses(self.current_bet)
self.player.show_balance()
self.dealer.show_balance()
if __name__ == '__main__':
table = Blackjack_table(100)
table.play_rounds()
In addition to what the others have already said I would add that you should look into:
- The difference between class and instance variables
- How dictionaries work (because the method
getValues()
in yourCard
class is essentially looking up a value in a dictionnary) - How using a list comprehensions using more than one
for
clause can help you "flatten" a hierarchy of lists. - How using properties can enable to having calculated attributes (among other things) (see https://stackoverflow.com/questions/7374748/whats-the-difference-between-a-python-property-and-attribute)
For most part, I try to :
Explore related questions
See similar questions with these tags.