3
\$\begingroup\$

Been working with python for a couple years on the side, looking to get feedback on this Blackjack game I made for some OOP practice. Code executes as expected (and I know the game is incomplete - no splitting, etc).

https://github.com/josh9730/misc/blob/main/blackjack.py

Code:

import cards


class Blackjack:
    game_over = False

    def __init__(self):
        self.deck = cards.Deck()
        self.money = 100
        self.money_lost = 0
        self.bet = 0

    def _calc_points(self, hand: list) -> int:
        """Calculate points for the hand supplied."""
        num_aces = 0
        points = 0
        for card in hand:
            if card.value in ("Jack", "Queen", "King"):
                points += 10
            elif card.value == "Ace":
                num_aces += 1
                points += 11
            else:
                points += int(card.value)

        while points > 21 and num_aces:
            points -= 10
            num_aces -= 1

        return points

    def _test_game_over(self):
        """Check if game_over (i.e. break conditions):
        - player has 21
            dealer will hit until equal to 21 or bust
        - player has over 21
            player busts
        """
        if self.dealer_points < self.player_points == 21:
            self.dealer_hit()

        elif self.player_points > 21:
            self.game_over = True

    def _hit(self, hand: list) -> int:
        """Hit and return calculated points.

        Args:
            hand [list]: list of Card objects. Expect either self.player_hand or
            self.dealer_hand
        """
        new_card = self.deck.deal_cards(1)
        hand.extend(new_card)
        return self._calc_points(hand)

    def _show_last_card(self):
        """Draw last card on lose or draw."""
        return self.deck.deal_cards(1)

    def _print_cards_per(self, player: str, hand: list):
        """Print cards and points per-player.

        - if player or game_over == True, then all cards will be shown and the point
        total displayed.
        - if dealer and not game_over, then only the dealer's first card will be shown.
        The point total for the dealer will be only an empty string.

        Args:
            player [str]: 'dealer' or 'player'. Used to determine which hand to display.
            hand [list]: list of Card objects. Expect either self.player_hand or
            self.dealer_hand
        """
        points = ""
        if player == "player" or self.game_over:
            cards_str = ", ".join(str(i) for i in hand)
            points = self.player_points if player == "player" else self.dealer_points

        else:
            cards_str = f"{hand[0]}, UNKNOWN"

        print(
            f"{player.capitalize()} Hand:\n\t{cards_str}",
            f"\n\tPoints: {points}",
        )

    def _laugh_at_player(self):
        """Show last card to the player to see if they would have won."""
        last_card = self._show_last_card()
        print(f"\tThe next card was the {str(last_card[0])}\n")

        self.player_hand.extend(last_card)
        points = self._calc_points(self.player_hand)

        if 21 >= points > self.dealer_points:
            print("\tHaha, you would have won, sucker!")

    def calc_bet(self, win=False):
        """Calculate winnings/losings."""
        if win:
            self.money += self.bet

        else:
            self.money -= self.bet
            self.money_lost += self.bet

    def show_me_the_money(self):
        """Function to print current money pot."""
        print(f"\nYou have: ${self.money}")
        print(f"You have lost: ${self.money_lost}\n")

    def deal(self):
        """Deal new hand to players.

        - deal 2 to player and dealer
        - Calculate points for each player
        - Test if player has 21, no need to prompt for stay/hit
        """
        self.dealer_hand = self.deck.deal_cards(2)
        self.player_hand = self.deck.deal_cards(2)

        self.dealer_points = self._calc_points(self.dealer_hand)
        self.player_points = self._calc_points(self.player_hand)

        self._test_game_over()

    def player_hit(self):
        """Hit player and test game_over."""
        self.player_points = self._hit(self.player_hand)
        self._test_game_over()

    def dealer_hit(self):
        """Hit dealer, looping until dealer_points are equal to or greater
        than the player_points, then changing game_over to True."""
        while self.player_points > self.dealer_points:
            self.dealer_points = self._hit(self.dealer_hand)

        self.game_over = True

    def show_cards(self):
        """Show the cards & points for both the dealer and player."""
        self._print_cards_per("dealer", self.dealer_hand)
        self._print_cards_per("player", self.player_hand)
        print("")

    def show_winner(self):
        """Determine winner, print GAME OVER string."""
        msg = "\n***** Game Over -- {} *****\n"

        if self.player_points == self.dealer_points:
            print(msg.format("DRAW"))
            print("At least you didn't lose any money.")
            self._laugh_at_player()

        elif self.player_points > 21 or 21 > self.dealer_points > self.player_points:
            self.calc_bet()
            print(msg.format("YOU LOSE"))
            print(f"You lost ${self.bet}")
            self._laugh_at_player()

        else:
            self.calc_bet(win=True)
            print(msg.format("YOU WIN"))
            print(f"You won ${self.bet}!")


#
# Pytests
#


def create_hand(hand: list) -> list:
    """Initialize card namedtuple."""
    return [cards.Card(i, "Hearts") for i in hand]


def test_hands():
    """Test various hand conditions."""
    hands = [
        (12, ["Ace", "Ace", "10"]),
        (13, ["Ace", "Ace", "Ace", "10"]),
        (14, ["Ace", "Ace", "Ace", "Ace", "10"]),
        (17, ["Ace", "Ace", "King", "5"]),
        (18, ["Ace", "Ace", "Ace", "King", "5"]),
        (26, ["Ace", "King", "Queen", "5"]),
    ]

    game = Blackjack()
    for hand_points in hands:
        hand = create_hand(hand_points[1])
        points = game._calc_points(hand)
        assert points == hand_points[0]


def test_21():
    """Test if player score == 21 ends the game."""
    game = Blackjack()
    game.deal()
    game.player_points = 21
    game._test_game_over()
    assert game.game_over


def test_lose_bet():
    """Test bet if lose."""
    game = Blackjack()
    game.bet = 10
    game.calc_bet(win=False)
    assert game.money == 90
    assert game.money_lost == 10


def test_win_bet():
    """Test bet if win."""
    game = Blackjack()
    game.bet = 10
    game.calc_bet(win=True)
    assert game.money == 110
    assert game.money_lost == 0


#
# End Pytests
#


def main():
    game = Blackjack()

    play = True
    while play:
        game.deal()
        game.show_me_the_money()

        bet = 0
        while not bet:

            bet_str = input("Place your bet: ")
            try:
                bet = int(bet_str)

            except ValueError:
                print("Must be valid bet.")
                bet = False

            else:
                if bet > game.money:
                    print("You are too poor for that bet.")
                    bet = False
                elif bet <= 0:
                    print("Stop betting nonsense.")
                    bet = False
                elif bet == game.money:
                    print("Feeling cocky!")

        print(f"You bet ${bet}\n")
        game.bet = bet
        game.show_cards()

        while not game.game_over:

            response = ""
            while response not in ("hit", "stay"):
                response = input("Hit or stay? ").lower()

            if response == "hit":
                print("")
                game.player_hit()
                game.show_cards()

            elif response == "stay":
                game.dealer_hit()
                game.show_cards()

        game.show_winner()

        # play new game & reset
        print("=" * 50)
        if game.money == 0:
            print("You're broke, get out of the casino!")
            exit()
        new_game = ""
        while new_game not in ("yes", "no"):
            new_game = input("New Game? ").lower()
            print("")

        if new_game == "no":
            play = False
        else:
            game.game_over = False

    winnings = game.money - game.money_lost
    if winnings > 0:
        print(f"Good Game! You won a total of ${winnings}.")
    elif winnings < 0:
        print(f"You're not very good. You lost a total of ${winnings}.")
    else:
        print("Yay... You broke even...")


if __name__ == "__main__":
    main()

\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.