diff --git a/blackjack/card.py b/blackjack/card.py new file mode 100644 index 0000000..d2fabf5 --- /dev/null +++ b/blackjack/card.py @@ -0,0 +1,17 @@ +class Card(): + def __init__(self, suit, rank): + self.suit = suit.lower() + self.rank = rank.lower() + + def __repr__(self): + return "{} of {}".format(self.rank.title(), self.suit.title()) + +if __name__ == "__main__": + test_list = [("hearts", "king"), ("diamonds", "2")] + card_list = [Card(suit, rank) for suit, rank in test_list] + assert card_list[0].suit == "hearts" + assert card_list[1].suit == "diamonds" + assert card_list[0].rank == "king" + assert card_list[1].rank == "2" + assert str(card_list[0]) == "King of Hearts" + assert str(card_list[1]) == "2 of Diamonds" diff --git a/blackjack/dealer.py b/blackjack/dealer.py new file mode 100644 index 0000000..af0d0b2 --- /dev/null +++ b/blackjack/dealer.py @@ -0,0 +1,26 @@ +from player import Player +from hand import Hand + + +class Dealer(Player): + def __init__(self): + self.hidden = True + + def display_hand(self): + return self.hand.card_list_display(self.hidden) + + def bet(self): + pass + + def add_score(self): + pass + + def get_hand_value(self): + return self.hand.get_score(self.hidden) + + def start_game(self): + self.hidden = True + self.hand = Hand() + + def start_turn(self): + self.hidden = False diff --git a/blackjack/deck.py b/blackjack/deck.py new file mode 100644 index 0000000..e529dd3 --- /dev/null +++ b/blackjack/deck.py @@ -0,0 +1,37 @@ +import random +from card import Card + + +class Deck(): + def __init__(self): + self.create_card_list() + self.shuffle() + + @property + def card_list(self): + return self._card_list + + def shuffle(self): + random.shuffle(self._card_list) + + def draw(self): + return self._card_list.pop() + + def create_card_list(self): + numbers = list(map(str, range(2, 11))) + ranks = ['ace', 'king', 'queen', 'jack'] + ranks.extend(numbers) + suits = ['hearts', 'spades', 'diamonds', 'clubs'] + self._card_list = [Card(suit, rank) + for rank in ranks for suit in suits] + + @property + def card_list_display(self): + return '\n'.join(map(str, self._card_list)) + +if __name__ == "__main__": + test_deck = Deck() + example_card = Card("hearts", "3") + assert len(test_deck.card_list) == 52 + assert type(test_deck.draw()) == type(example_card) + assert len(test_deck.card_list) == 51 diff --git a/blackjack/display.py b/blackjack/display.py new file mode 100644 index 0000000..ef2092d --- /dev/null +++ b/blackjack/display.py @@ -0,0 +1,25 @@ +import os + + +class Display(): + def __init__(self, number_of_persistent_lines=1): + self.number_of_persistent_lines = number_of_persistent_lines + self.reset() + + def print_display(self): + os.system("clear") + for line in self.lines: + print(line) + self.lines = self.lines[:self.number_of_persistent_lines] + + def set_display(self, message, position): + if position >= self.number_of_persistent_lines: + return + self.lines[position] = message + + def add_messages(self, *args): + for message in args: + self.lines.append(message) + + def reset(self): + self.lines = ["" for i in range(self.number_of_persistent_lines)] diff --git a/blackjack/game.py b/blackjack/game.py new file mode 100644 index 0000000..7253a98 --- /dev/null +++ b/blackjack/game.py @@ -0,0 +1,211 @@ +from player import Player +from dealer import Dealer +from deck import Deck +from display import Display + + +class Game(): + def __init__(self): + self.player = Player() + self.dealer = Dealer() + self.display = Display(8) + self.pot = 0 + + def print_display(self, wait=False): + self.update_display() + self.display.print_display() + if wait: + self.get_continue() + + def update_display(self, hidden=True): + display_list = ["Dealer:", self.dealer.display_hand(), + "Score: " + str(self.dealer.get_hand_value()) + + " | Pot: " + str(self.pot), + "- "*20, "You:", self.player.display_hand(), + "Score: " + str(self.player.get_hand_value()) + + " | Money: " + str(self.player.score)] + for index, string in enumerate(display_list): + self.display.set_display(string, index) + + def start_round(self): + self.deck = Deck() + self.player.start_game() + self.dealer.start_game() + self.add_message("Welcome to Blackjack!") + self.print_display(True) + self.pot = self.player.bet(self.get_input("bet")) + self.initial_deal() + if self.dealer.has_blackjack() and self.player.has_blackjack(): + self.dealer.hidden = False + self.push("Both dealer and player have Blackjack.") + return self.get_input("hand") + elif self.player.has_blackjack(): + self.player_wins("You have Blackjack!") + return self.get_input("hand") + elif self.dealer.has_blackjack(): + self.dealer.hidden = False + self.dealer_wins("Dealer has Blackjack.") + return self.get_input("hand") + return self.play() + + def initial_deal(self): + for i in range(2): + self.player.add_card(self.deal()) + self.print_display(True) + self.dealer.add_card(self.deal()) + self.print_display(True) + + def get_input(self, type): + self.print_display() + if type == "hand": + return self.get_hand_input() + elif type == "bet": + return self.get_bet_input() + elif type == "choice": + return self.get_stand_or_hit() + + def get_hand_input(self): + if not self.enough_money(): + return False + while True: + string = "Do you want to play another hand? (y/n) > " + value = input(string).lower() + if value in ("y", "n"): + return value + self.add_message("Please enter 'y' or 'n'") + self.print_display() + + def get_bet_input(self): + while True: + bet = input("How much do you want to bet? > ") + if bet.isdigit() and bet != "0": + if int(bet) > self.player.score: + self.add_message("You don't have that much.") + self.print_display() + continue + return int(bet) + self.add_message("Please enter a positive integer.") + self.print_display() + + def get_stand_or_hit(self): + while True: + string = "Do you want to stand, hit, or double down? > " + value = input(string).lower() + if value in ("stand", "hit", "double down"): + return value + self.add_message("Please enter 'stand', 'hit', or 'double down'.") + self.print_display() + + def add_message(self, message): + self.display.add_messages(message) + + def get_continue(self): + input("Press Enter to continue.") + + def deal(self): + return self.deck.draw() + + def player_play(self): + while True: + choice = self.get_input("choice") + if choice == "hit": + self.player.add_card(self.deal()) + if self.player.has_blackjack(): + self.add_message("You have Blackjack!") + self.print_display(True) + return True + elif self.player.is_busted(): + self.add_message("You have busted.") + self.print_display(True) + return False + continue + if choice == "double down": + if self.player.score < self.pot: + self.add_message("You don't have enough money.") + self.print_display(True) + continue + self.add_message("Doubling down.") + self.pot += self.player.bet(self.pot) + self.print_display(True) + self.player.add_card(self.deal()) + if self.player.has_blackjack(): + self.add_message("You have Blackjack!") + self.print_display(True) + return True + elif self.player.is_busted(): + self.add_message("You have busted.") + self.print_display(True) + return False + return True + + def dealer_play(self): + self.dealer.start_turn() + self.print_display(True) + while self.dealer.get_hand_value() < 17: + self.add_message("Dealer hits.") + self.dealer.add_card(self.deal()) + if self.dealer.has_blackjack(): + self.add_message("Dealer has Blackjack.") + self.print_display(True) + return + if self.dealer.is_busted(): + self.add_message("Dealer is busted!") + self.print_display(True) + return + self.print_display(True) + self.add_message("Dealer stands.") + self.print_display(True) + + def play(self): + if self.player_play(): + self.dealer_play() + if self.dealer.get_hand_value() == self.player.get_hand_value(): + self.push() + elif self.dealer.is_busted() or self.player.has_blackjack(): + self.player_wins() + elif self.dealer.has_blackjack() or self.player.is_busted(): + self.dealer_wins() + elif self.player.get_hand_value() > self.dealer.get_hand_value(): + self.player_wins() + else: + self.dealer_wins() + return (self.get_input("hand") == "y") + + def run(self): + while self.start_round(): + pass + return True + + def player_wins(self, message=""): + if message: + self.add_message(message) + self.add_message("You win the hand!") + self.print_display(True) + pot_multiplier = 2 + if self.player.has_blackjack(): + pot_multiplier = 3 + self.add_message("You won {}!".format(self.pot * pot_multiplier)) + self.player.add_score(self.pot * pot_multiplier) + self.pot = 0 + self.print_display(True) + + def dealer_wins(self, message=""): + if message: + self.add_message(message) + self.add_message("You lose the hand.") + self.print_display(True) + self.pot = 0 + + def push(self, message=""): + if message: + self.add_message(message) + self.add_message("The round is a push.") + self.print_display(True) + + def enough_money(self): + if self.player.score < 1: + self.add_message("You don't have enough money to continue.") + self.add_message("Thank you for playing.") + self.print_display(True) + return False + return True diff --git a/blackjack/hand.py b/blackjack/hand.py new file mode 100644 index 0000000..ad9512f --- /dev/null +++ b/blackjack/hand.py @@ -0,0 +1,79 @@ +from card import Card + + +class Hand(): + def __init__(self): + self._card_list = [] + self.score = 0 + self.blackjack = False + self.busted = False + self.soft = False + + @property + def card_list(self): + return [card for card in self._card_list] + + def get_score(self, hidden=False): + if hidden: + return 0 + return sum([card.value for card in self.card_list]) + + def start_card_value(self, rank): + if not rank.isalpha() and rank != "ace": + return int(rank) + elif rank != "ace": + return 10 + self.soft = True + return 11 + + def evaluate_score(self): + if self.get_score() > 21: + for card in self.card_list: + if card.rank == "ace" and card.value == 11: + card.value = 1 + self.soft = False + break + if 11 in [card.value for card in self.card_list]: + self.soft = True + if self.get_score() == 21: + self.blackjack = True + return + if self.get_score() > 21: + self.busted = True + self.blackjack = False + + def add_card(self, card): + card.value = self.start_card_value(card.rank) + self._card_list.append(card) + self.evaluate_score() + + def card_list_display(self, hidden=False): + card_list = self.card_list + if hidden and card_list: + card_list[0] = Card("Cards", "Unknown") + return ", ".join(map(str, card_list)) + +if __name__ == "__main__": + from card import Card + test_hand = Hand() + test_hand.add_card(Card("Spades", "Ace")) + assert test_hand.get_score() == 11 + test_hand.add_card(Card("Clubs", "Ace")) + assert test_hand.get_score() == 12 + test_hand.add_card(Card("Diamonds", "5")) + assert test_hand.get_score() == 17 + assert test_hand.soft + assert not test_hand.blackjack + assert not test_hand.busted + test_hand.add_card(Card("Hearts", "Queen")) + assert test_hand.get_score() == 17 + test_hand.add_card(Card("Hearts", "4")) + assert test_hand.get_score() == 21 + assert not test_hand.soft + assert not test_hand.busted + assert test_hand.blackjack + test_hand.add_card(Card("Clubs", "Jack")) + assert test_hand.busted + assert not test_hand.blackjack + assert not test_hand.soft + assert test_hand.get_score() == 31 diff --git a/blackjack/main.py b/blackjack/main.py new file mode 100644 index 0000000..2111265 --- /dev/null +++ b/blackjack/main.py @@ -0,0 +1,5 @@ +from game import Game + + +game = Game() +game.run() diff --git a/blackjack/player.py b/blackjack/player.py new file mode 100644 index 0000000..218fcac --- /dev/null +++ b/blackjack/player.py @@ -0,0 +1,37 @@ +from hand import Hand + + +class Player(): + def __init__(self): + self.score = 100 + + def start_game(self): + self.hand = Hand() + + def end_game(self): + pass + + def add_card(self, card): + self.hand.add_card(card) + + def is_busted(self): + return self.hand.busted + + def has_blackjack(self): + return self.hand.blackjack + + def has_soft_hand(self): + return self.hand.soft + + def bet(self, amount=10): + self.score -= amount + return amount + + def add_score(self, amount): + self.score += amount + + def get_hand_value(self): + return self.hand.get_score(False) + + def display_hand(self): + return self.hand.card_list_display(False)