[Edited] formatting and Flake8 linter corrections
Four days ago I posted my first object oriented programming project. Everything I have learned to date is self-taught and posting project to receive criticism is the best mentor I currently have.
The feedback on posts like this is integral to me, so please use a fine-tooth comb as you are willing. The more detailed, the more minute the mistake, the better you help me become.
The first bit of code is the code to be inherited.
import abc
import itertools
import random
from enum import Enum
from dataclasses import dataclass
class User(metaclass=abc.ABCMeta):
def __init__(self):
self.name = ""
self.score = 0
self.hand = []
self.balance = 0
@abc.abstractmethod
def define_name(self):
"""Set either the dealer or player name"""
return
def print_hand(self, cards_to_be_shown):
if cards_to_be_shown > len(self.hand):
print("Too many cards requested to be shown")
return
counter = 0
print(f"\n\n{self.name}'s HAND:")
for card in self.hand:
print(card)
counter += 1
if counter >= cards_to_be_shown:
print('', end="\r")
break
def discard_hand(self):
self.hand = []
def calculate_score(self):
self.score = 0
for card in self.hand:
self.score += card.rank.points
def print_score(self):
print(f"Total Score: {self.score}")
def add_card_to_hand(self, card):
self.hand.append(card)
class Player(User):
def __init__(self):
super(Player, self).__init__()
self.balance = 200
self.define_name()
self.bet = self.place_bet()
def define_name(self):
while True:
user_name = input("Your Name: ")
confirmation = input(f"Your name is {user_name}? Y/N: ")
confirmation = confirmation.lower().strip()
if confirmation == "y":
self.name = user_name
print("username set!")
break
def place_bet(self):
while True:
response = input(
f"BALANCE: {self.balance}\nMake Bet (1/2/5/10/25/50/100): ")
try:
current_bet = int(response)
except ValueError:
print("Please Enter a Valid number")
else:
if current_bet not in (1, 2, 5, 10, 25, 50, 100, 1000):
print(
"Sorry, only bets of exactly 1, 2, 5, 10, 25, 50 & 100 are allowed.")
else:
if current_bet <= self.balance:
self.balance -= current_bet
self.bet = current_bet
print(f"${self.bet} BET PLACED")
break
else:
print(
f"Amount Entered Higher than Balance. MAXIMUM BET = {self.balance}")
def reset_bet(self):
self.bet = 0
def get_user_action(self):
likely_action = ''
while True:
actions = ("hit", "stand")
response = input("HIT OR STAND?: ")
response = response.lower().strip()
counter = 0
for i in range(len(response)):
try:
if response[i] == actions[0][i]:
counter += 1
likely_action = actions[0]
elif response[i] == actions[1][i]:
counter += 1
likely_action = actions[1]
except ValueError:
"just keep swimming. I feel like its bad practice but I used the try loop "
if counter < 2:
print("Please Re-enter: ")
else:
return likely_action
break
# What can a player do that a dealer cannot?
# makebet
class Dealer(User):
def __init__(self):
super(Dealer, self).__init__()
self.balance = 1000000
self.define_name()
def define_name(self):
self.name = "Dealer"
# what can a dealer do that a player cannot?
# show one card on deal
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]
class Suits(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]
@dataclass(frozen=True)
class Card:
rank: Rank
suit: Suits
def point_value(self):
return self.rank.points
def __str__(self):
return f"{self.rank.symbol} of {self.suit.suit_name} {self.suit.symbol}"
class Deck:
def __init__(self):
self.deck = [Card(rank, suit)
for rank, suit in itertools.product(Rank, Suits)]
random.shuffle(self.deck)
def reveal_deck(self):
for card in self.deck:
card = str(card)
print(card)
class Shoe:
def __init__(self):
self.new_shoe = []
for i in range(5):
for card in Deck().deck:
self.new_shoe.append(card)
def reveal_shoe(self):
print(self.new_shoe)
def deal_one_card(self):
card_dealt = self.new_shoe[0]
self.new_shoe.pop(0)
return card_dealt
class Game:
def __init__(self, player, dealer, shoe):
self.player = player
self.dealer = dealer
self.game_shoe = shoe
def deal_cards(self, cards_per_player):
for i in range(cards_per_player):
self.player.add_card_to_hand(self.game_shoe.deal_one_card())
self.dealer.add_card_to_hand(self.game_shoe.deal_one_card())
# delete this (prints out the current hand data)
# print(self.player.hand)
# print(self.dealer.hand)
def hit(self, person):
person.add_card_to_hand(self.game_shoe.deal_one_card())
person.calculate_score()
person.print_hand(len(person.hand))
person.print_score()
def stand(self):
print("Chose to stay")
def play_action(self, action, person):
if action == "hit":
self.hit(person)
elif action == "stand":
self.stand()
else:
("Somehow a bad action was passed")
def player_turn(self):
while True:
action = self.player.get_user_action()
self.play_action(action, self.player)
if action == "stand":
break
elif self.player.score > 21:
print("Ya busted early bud\nGAME OVER")
self.player.reset_bet()
self.reset_hands()
self.keep_playing()
break
def dealer_turn(self):
self.dealer.print_hand(len(self.dealer.hand))
self.dealer.print_score()
while True:
self.dealer.calculate_score()
if self.dealer.score < 16:
self.play_action("hit", self.dealer)
elif self.dealer.score >= 16 and self.dealer.score < 22:
self.play_action("stand", self.dealer)
break
elif self.dealer.score >= 22:
print("Dealer BUSTS!")
self.payout()
self.player.reset_bet()
self.reset_hands()
self.keep_playing()
break
def reset_hands(self):
self.player.discard_hand()
self.dealer.discard_hand()
def payout(self):
print(self.player.bet)
self.dealer.balance -= self.player.bet
self.player.balance += (self.player.bet * 2)
def game_begin(self):
if self.player.bet == 0:
self.player.place_bet()
self.deal_cards(2)
self.player.print_hand(2)
self.player.calculate_score()
self.player.print_score()
self.dealer.print_hand(1)
self.dealer.calculate_score()
def middle_game(self):
self.player_turn()
if self.player.score < 22:
self.dealer_turn()
if self.dealer.score < 22:
self.end_game()
else:
# ask next game function
pass
def end_game(self):
if self.player.score > self.dealer.score:
print("YOU WIN!")
self.payout()
# payout to player
pass
elif self.player.score >= self.dealer.score:
print("DEALER WINS!!")
self.reset_hands()
self.player.reset_bet()
keep_playing_variable = self.keep_playing()
# determine winner
# payout winner
# reset_bet()
# ask next game function
return keep_playing_variable
def keep_playing(self):
response = input("Would you like to keep playing? Y/N: ")
response = response.lower().strip()
if response == "n":
return False
The second bit of code is the code I actually execute. I will say, something about how I set-up and execute the gameplay below just doesn't feel right, although I have come to no conclusion as to why.
from blackjackhelpers import Player, Dealer, Deck, Shoe, Game
keep_playing_variable = True
player1 = Player()
dealer1 = Dealer()
game_shoe = Shoe()
new_game = Game(player1, dealer1, game_shoe)
while keep_playing_variable:
new_game.game_begin()
new_game.middle_game()
-
1\$\begingroup\$ Given that we've found an error when you enter "stand" (and by my reading, anything with four or more letters), I think this is off topic. How did you not try "stand"? It also looks like "sit" and "stt" will cause behavior you didn't expect. \$\endgroup\$Teepeemm– Teepeemm2022年10月08日 20:38:46 +00:00Commented Oct 8, 2022 at 20:38
-
\$\begingroup\$ Fair enough. And noted \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月08日 21:12:49 +00:00Commented Oct 8, 2022 at 21:12
-
\$\begingroup\$ I fixed the loop and I immensely appreciate that flaw you pointed out. Mostly because it clearly exposes a lack of proper testing. Something that should probably become a standard practice for me henceforth. Thanks mate \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月09日 01:56:06 +00:00Commented Oct 9, 2022 at 1:56
-
\$\begingroup\$ Please see What to do when someone answers . I have rolled back Rev 6 → 4. \$\endgroup\$200_success– 200_success2022年10月09日 06:59:14 +00:00Commented Oct 9, 2022 at 6:59
-
1\$\begingroup\$ One habit that would be really good to develop to improve your testing is adopting Test-Driven-Development (TDD), where you write your tests BEFORE you write the production code. While writing your tests, make them as complete a test suite as possible (including all edge cases, etc). This way your tests focus on what the production code SHOULD be doing rather than testing what you have written - it really focuses your mind on all the requirements. \$\endgroup\$racraman– racraman2022年10月10日 05:53:55 +00:00Commented Oct 10, 2022 at 5:53
2 Answers 2
Bug!
You have a bug!
I was just playing your game, and this happened:
Your Name: Thonnu
Your name is Thonnu? Y/N: y
username set!
BALANCE: 200
Make Bet (1/2/5/10/25/50/100): 100
100ドル BET PLACED
Thonnu's HAND:
5 of Hearts ♥
3 of Clubs ♣
Total Score: 8
Dealer's HAND:
3 of Diamonds ♦
HIT OR STAND?: hit
Thonnu's HAND:
5 of Hearts ♥
3 of Clubs ♣
9 of Clubs ♣
Total Score: 17
HIT OR STAND?: stand
Traceback (most recent call last):
File "main.py", line 12, in <module>
new_game.middle_game()
File "blackjackhelpers.py", line 318, in middle_game
self.player_turn()
File "blackjackhelpers.py", line 266, in player_turn
action = self.player.get_user_action()
File "blackjackhelpers.py", line 111, in get_user_action
if response[i] == actions[0][i]:
IndexError: string index out of range
The fix
In this section of your code (lines 110 to 118):
for i in range(len(response)):
try:
if response[i] == actions[0][i]:
counter += 1
likely_action = actions[0]
elif response[i] == actions[1][i]:
counter += 1
likely_action = actions[1]
except ValueError:
"just keep swimming. I feel like its bad practice but I used the try loop "
Your try
/except
block catches ValueError
s, but this was an IndexError
. Instead, use:
except (ValueError, IndexError):
to catch them both
-
1\$\begingroup\$ Even better would be to diagnose why there's an index error. \$\endgroup\$Teepeemm– Teepeemm2022年10月08日 13:44:06 +00:00Commented Oct 8, 2022 at 13:44
-
1\$\begingroup\$ I actually am super curious why there is an index error. Perhaps there was empty space that wasn't cut off before setting it to the variable. After work 100% I will be checking this out AND fixing the try/except block. \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月08日 17:52:26 +00:00Commented Oct 8, 2022 at 17:52
-
\$\begingroup\$ I assessed the situation and it was quite apparent what the issue was. Instead of adding IndexError to the except, I added the ability for the if/elif to filter, and then gathered a list of likely actions everytime we made a comparison. Then whichever seemed most likely, I returned. \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月09日 02:26:50 +00:00Commented Oct 9, 2022 at 2:26
Game ending
When the player reaches 0,ドル this happens:
Would you like to keep playing? Y/N: y
BALANCE: 0
Make Bet (1/2/5/10/25/50/100): 100
Amount Entered Higher than Balance. MAXIMUM BET = 0
BALANCE: 0
Make Bet (1/2/5/10/25/50/100): 0
Sorry, only bets of exactly 1, 2, 5, 10, 25, 50 & 100 are allowed.
BALANCE: 0
Make Bet (1/2/5/10/25/50/100):
It just goes in an endless loop and the player is forced to re-run the program.
Instead, I would suggest adding a feature at the start of the place_bet
function (line 72), like this:
def place_bet(self):
if self.balance == 0:
# Exit the game here
-
1\$\begingroup\$ Good catch my friend. I'll add that here in a bit. \$\endgroup\$Infinite Grasp– Infinite Grasp2022年10月08日 16:41:22 +00:00Commented Oct 8, 2022 at 16:41
Explore related questions
See similar questions with these tags.