From da72fd6f873fd004b0d2e8e7a7ecbb50a9c59d28 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Wed, 21 Jan 2015 15:31:23 -0500 Subject: [PATCH 01/50] Basics of card and deck class done. Test files working. --- lib/blackjack/card.py | 47 ++++++++++++++++++++++++++ lib/blackjack/deck.py | 37 ++++++++++++++++++++ lib/blackjack/test_card.py | 69 ++++++++++++++++++++++++++++++++++++++ lib/blackjack/test_deck.py | 30 +++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 lib/blackjack/card.py create mode 100644 lib/blackjack/deck.py create mode 100644 lib/blackjack/test_card.py create mode 100644 lib/blackjack/test_deck.py diff --git a/lib/blackjack/card.py b/lib/blackjack/card.py new file mode 100644 index 0000000..53b0da4 --- /dev/null +++ b/lib/blackjack/card.py @@ -0,0 +1,47 @@ +class Card: + """Playing card class. Each instance represents a single playing card. + + Responsibilities: + + * Each card has a string name attribute corresponding to it's value + * Each card has a suit attribute + * Each card has a value determined by the private evaluate_card_value + method + * Ace value defaults to 11 but can be changed to 1 by calling the + swap_ace method. + + Collaborators: + + * Cards are collected into decks of varying sizes + * Cards are distributed to player and dealer hands + """ + + suits = {"spades": "♤", + "hearts": "♡", + "clubs": "♧", + "diamonds": "♢"} + + def __init__(self, name, suit): + self.name = name + self.suit = suit + self.__evaluate_card_value__() + + def __str__(self): + return self.name + self.suits[self.suit] + + def __repr__(self): + return self.__str__() + + def __evaluate_card_value__(self): + if self.name.isdigit(): + self.value = int(self.name) + elif self.name == "A": + self.value = 11 + else: + self.value = 10 + + def swap_ace(self): + if self.value == 11: + self.value = 1 + else: + self.value = 11 diff --git a/lib/blackjack/deck.py b/lib/blackjack/deck.py new file mode 100644 index 0000000..82e8ee5 --- /dev/null +++ b/lib/blackjack/deck.py @@ -0,0 +1,37 @@ +from random import shuffle + +from card import Card + + +class Deck: + """Deck of cards class. A deck of cards is a collection of card objects. + + Responsibilities: + + * The Deck class generates a series of Card objects corresponding with + the required cards in a complete deck of cards. + * It is also responsible for randomizing the location of the cards + within the deck list. + * Takes an optional argument decks at creation to allow for creation of + a shoe containing any number of decks. + + Collaborators: + + * Deck collects Card Objects + * Deck pops and returns cards from it's card list when requested by + the Game. + """ + + + def __init__(self, decks=1): + suits = ["hearts", "spades", "clubs", "diamonds"] + names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "J", "Q", "K"] + self.cards = [Card(name, suit) for suit in suits + for name in names + for deck in range(decks)] + shuffle(self.cards) + + + def deal_card(self): + return self.cards.pop() diff --git a/lib/blackjack/test_card.py b/lib/blackjack/test_card.py new file mode 100644 index 0000000..463113c --- /dev/null +++ b/lib/blackjack/test_card.py @@ -0,0 +1,69 @@ +from card import Card +"""Test functions for the card class""" + +def test_construction(): + new_card = Card("2", "clubs") + assert new_card + +def test_card_attributes(): + new_card_A = Card("A", "clubs") + new_card_2 = Card("2", "hearts") + new_card_3 = Card("3", "spades") + new_card_4 = Card("4", "diamonds") + new_card_5 = Card("5", "clubs") + new_card_6 = Card("6", "hearts") + new_card_7 = Card("7", "spades") + new_card_8 = Card("8", "diamonds") + new_card_9 = Card("9", "clubs") + new_card_10 = Card("10", "hearts") + new_card_J = Card("J", "spades") + new_card_Q = Card("Q", "diamonds") + new_card_K = Card("K", "clubs") + + assert new_card_A.name == "A" + assert new_card_2.name == "2" + assert new_card_3.name == "3" + assert new_card_4.name == "4" + assert new_card_5.name == "5" + assert new_card_6.name == "6" + assert new_card_7.name == "7" + assert new_card_8.name == "8" + assert new_card_9.name == "9" + assert new_card_10.name == "10" + assert new_card_J.name == "J" + assert new_card_Q.name == "Q" + assert new_card_K.name == "K" + + assert new_card_A.suit == "clubs" + assert new_card_2.suit == "hearts" + assert new_card_3.suit == "spades" + assert new_card_4.suit == "diamonds" + assert new_card_5.suit == "clubs" + assert new_card_6.suit == "hearts" + assert new_card_7.suit == "spades" + assert new_card_8.suit == "diamonds" + assert new_card_9.suit == "clubs" + assert new_card_10.suit == "hearts" + assert new_card_J.suit == "spades" + assert new_card_Q.suit == "diamonds" + assert new_card_K.suit == "clubs" + + assert new_card_A.value == 11 + assert new_card_2.value == 2 + assert new_card_3.value == 3 + assert new_card_4.value == 4 + assert new_card_5.value == 5 + assert new_card_6.value == 6 + assert new_card_7.value == 7 + assert new_card_8.value == 8 + assert new_card_9.value == 9 + assert new_card_10.value == 10 + assert new_card_J.value == 10 + assert new_card_Q.value == 10 + assert new_card_K.value == 10 + +def test_swap_ace(): + new_card = Card("A", "hearts") + assert new_card.value == 11 + new_card.swap_ace() + assert new_card.value == 1 diff --git a/lib/blackjack/test_deck.py b/lib/blackjack/test_deck.py new file mode 100644 index 0000000..89af199 --- /dev/null +++ b/lib/blackjack/test_deck.py @@ -0,0 +1,30 @@ +from deck import Deck +from card import Card + +def test_deck_creation(): + new_deck = Deck() + assert new_deck + +def test_deck_length(): + new_deck = Deck() + assert len(new_deck.cards) == 52 + +def test_shoe_creation(): + new_deck = Deck(2) + assert len(new_deck.cards) == 104 + new_deck_2 = Deck(4) + assert len(new_deck_2.cards) == 208 + +def test_shuffle_deck(): + suits = ["hearts", "spades", "clubs", "diamonds"] + names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "J", "Q", "K"] + unshuffled_deck = [Card(name, suit) for suit in suits + for name in names] + new_deck = Deck() + + assert new_deck.cards != unshuffled_deck + +def test_deal_cards(): + new_deck = Deck() + assert new_deck.deal_card() From 59b8a0e62beb8629479bb70acef4a87d473327cc Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Wed, 21 Jan 2015 16:07:42 -0500 Subject: [PATCH 02/50] Fixed pep8 errors, added reshuffle and deal methods to the deck class and test functions to verify functionality. --- lib/blackjack/card.py | 2 +- lib/blackjack/deck.py | 20 +++++++++++++------- lib/blackjack/test_card.py | 3 +++ lib/blackjack/test_deck.py | 17 +++++++++++++++-- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/lib/blackjack/card.py b/lib/blackjack/card.py index 53b0da4..2840869 100644 --- a/lib/blackjack/card.py +++ b/lib/blackjack/card.py @@ -27,7 +27,7 @@ def __init__(self, name, suit): self.__evaluate_card_value__() def __str__(self): - return self.name + self.suits[self.suit] + return self.name + self.suits[self.suit] def __repr__(self): return self.__str__() diff --git a/lib/blackjack/deck.py b/lib/blackjack/deck.py index 82e8ee5..b2272e0 100644 --- a/lib/blackjack/deck.py +++ b/lib/blackjack/deck.py @@ -1,5 +1,4 @@ from random import shuffle - from card import Card @@ -19,19 +18,26 @@ class Deck: * Deck collects Card Objects * Deck pops and returns cards from it's card list when requested by - the Game. + a calling function. + * Deck can be reinitialized by an outside function by calling the + reshuffle method. """ - def __init__(self, decks=1): + self.decks = decks suits = ["hearts", "spades", "clubs", "diamonds"] names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"] self.cards = [Card(name, suit) for suit in suits - for name in names - for deck in range(decks)] + for name in names + for deck in range(self.decks)] shuffle(self.cards) - - def deal_card(self): + def deal(self): + """Pops a single card from the end of the card list and returns + it to the calling function""" return self.cards.pop() + + def reshuffle(self): + """Reinitializes the deck""" + self.__init__() diff --git a/lib/blackjack/test_card.py b/lib/blackjack/test_card.py index 463113c..14cf5b5 100644 --- a/lib/blackjack/test_card.py +++ b/lib/blackjack/test_card.py @@ -1,10 +1,12 @@ from card import Card """Test functions for the card class""" + def test_construction(): new_card = Card("2", "clubs") assert new_card + def test_card_attributes(): new_card_A = Card("A", "clubs") new_card_2 = Card("2", "hearts") @@ -62,6 +64,7 @@ def test_card_attributes(): assert new_card_Q.value == 10 assert new_card_K.value == 10 + def test_swap_ace(): new_card = Card("A", "hearts") assert new_card.value == 11 diff --git a/lib/blackjack/test_deck.py b/lib/blackjack/test_deck.py index 89af199..e7a6d0f 100644 --- a/lib/blackjack/test_deck.py +++ b/lib/blackjack/test_deck.py @@ -1,30 +1,43 @@ from deck import Deck from card import Card + def test_deck_creation(): new_deck = Deck() assert new_deck + def test_deck_length(): new_deck = Deck() assert len(new_deck.cards) == 52 + def test_shoe_creation(): new_deck = Deck(2) assert len(new_deck.cards) == 104 new_deck_2 = Deck(4) assert len(new_deck_2.cards) == 208 + def test_shuffle_deck(): suits = ["hearts", "spades", "clubs", "diamonds"] names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"] unshuffled_deck = [Card(name, suit) for suit in suits - for name in names] + for name in names] new_deck = Deck() assert new_deck.cards != unshuffled_deck + def test_deal_cards(): new_deck = Deck() - assert new_deck.deal_card() + assert new_deck.deal() + + +def test_reshuffle_deck(): + new_deck = Deck() + new_deck.deal() + new_deck.deal() + new_deck.reshuffle() + assert len(new_deck.cards) == 52 From 19a4bbc53590416fdd9d8793cfd845936e34bd60 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Wed, 21 Jan 2015 22:44:44 -0500 Subject: [PATCH 03/50] Updated Player Class, added tests for Player. Added money attribute and method to modify the amount of money the player has, plus added ValueError is the players money attempts to drop below zero. Also added extensive README.md with planned class layout and To Do list for each class. --- blackjack/__init__.py | 0 blackjack/card.py | 47 ++++++++++++++++++++++ blackjack/deck.py | 43 ++++++++++++++++++++ blackjack/hand.py | 3 ++ blackjack/player.py | 24 ++++++++++++ blackjack/tests/test_card.py | 72 ++++++++++++++++++++++++++++++++++ blackjack/tests/test_deck.py | 43 ++++++++++++++++++++ blackjack/tests/test_player.py | 29 ++++++++++++++ 8 files changed, 261 insertions(+) create mode 100644 blackjack/__init__.py create mode 100644 blackjack/card.py create mode 100644 blackjack/deck.py create mode 100644 blackjack/hand.py create mode 100644 blackjack/player.py create mode 100644 blackjack/tests/test_card.py create mode 100644 blackjack/tests/test_deck.py create mode 100644 blackjack/tests/test_player.py diff --git a/blackjack/__init__.py b/blackjack/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/blackjack/card.py b/blackjack/card.py new file mode 100644 index 0000000..2840869 --- /dev/null +++ b/blackjack/card.py @@ -0,0 +1,47 @@ +class Card: + """Playing card class. Each instance represents a single playing card. + + Responsibilities: + + * Each card has a string name attribute corresponding to it's value + * Each card has a suit attribute + * Each card has a value determined by the private evaluate_card_value + method + * Ace value defaults to 11 but can be changed to 1 by calling the + swap_ace method. + + Collaborators: + + * Cards are collected into decks of varying sizes + * Cards are distributed to player and dealer hands + """ + + suits = {"spades": "♤", + "hearts": "♡", + "clubs": "♧", + "diamonds": "♢"} + + def __init__(self, name, suit): + self.name = name + self.suit = suit + self.__evaluate_card_value__() + + def __str__(self): + return self.name + self.suits[self.suit] + + def __repr__(self): + return self.__str__() + + def __evaluate_card_value__(self): + if self.name.isdigit(): + self.value = int(self.name) + elif self.name == "A": + self.value = 11 + else: + self.value = 10 + + def swap_ace(self): + if self.value == 11: + self.value = 1 + else: + self.value = 11 diff --git a/blackjack/deck.py b/blackjack/deck.py new file mode 100644 index 0000000..b2272e0 --- /dev/null +++ b/blackjack/deck.py @@ -0,0 +1,43 @@ +from random import shuffle +from card import Card + + +class Deck: + """Deck of cards class. A deck of cards is a collection of card objects. + + Responsibilities: + + * The Deck class generates a series of Card objects corresponding with + the required cards in a complete deck of cards. + * It is also responsible for randomizing the location of the cards + within the deck list. + * Takes an optional argument decks at creation to allow for creation of + a shoe containing any number of decks. + + Collaborators: + + * Deck collects Card Objects + * Deck pops and returns cards from it's card list when requested by + a calling function. + * Deck can be reinitialized by an outside function by calling the + reshuffle method. + """ + + def __init__(self, decks=1): + self.decks = decks + suits = ["hearts", "spades", "clubs", "diamonds"] + names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "J", "Q", "K"] + self.cards = [Card(name, suit) for suit in suits + for name in names + for deck in range(self.decks)] + shuffle(self.cards) + + def deal(self): + """Pops a single card from the end of the card list and returns + it to the calling function""" + return self.cards.pop() + + def reshuffle(self): + """Reinitializes the deck""" + self.__init__() diff --git a/blackjack/hand.py b/blackjack/hand.py new file mode 100644 index 0000000..8621583 --- /dev/null +++ b/blackjack/hand.py @@ -0,0 +1,3 @@ +class Hand: + + pass diff --git a/blackjack/player.py b/blackjack/player.py new file mode 100644 index 0000000..7ccdade --- /dev/null +++ b/blackjack/player.py @@ -0,0 +1,24 @@ +class Player: + """This is the player class, it contains information about the player's + hand and hand value. + + Responsibilities: + + * Stores Hand objects containing Card objects that the player controls + * Keeps track of players betting money + + Collaborations: + + * Informs the game of amount of money player has remaining to wager + """ + + def __init__(self, name): + self.name = name + self.hands = [] + self.money = 1000 + + def modify_money(self, value): + if self.money + value > 0: + self.money += value + else: + raise ValueError("Player money cannot be negative.") diff --git a/blackjack/tests/test_card.py b/blackjack/tests/test_card.py new file mode 100644 index 0000000..14cf5b5 --- /dev/null +++ b/blackjack/tests/test_card.py @@ -0,0 +1,72 @@ +from card import Card +"""Test functions for the card class""" + + +def test_construction(): + new_card = Card("2", "clubs") + assert new_card + + +def test_card_attributes(): + new_card_A = Card("A", "clubs") + new_card_2 = Card("2", "hearts") + new_card_3 = Card("3", "spades") + new_card_4 = Card("4", "diamonds") + new_card_5 = Card("5", "clubs") + new_card_6 = Card("6", "hearts") + new_card_7 = Card("7", "spades") + new_card_8 = Card("8", "diamonds") + new_card_9 = Card("9", "clubs") + new_card_10 = Card("10", "hearts") + new_card_J = Card("J", "spades") + new_card_Q = Card("Q", "diamonds") + new_card_K = Card("K", "clubs") + + assert new_card_A.name == "A" + assert new_card_2.name == "2" + assert new_card_3.name == "3" + assert new_card_4.name == "4" + assert new_card_5.name == "5" + assert new_card_6.name == "6" + assert new_card_7.name == "7" + assert new_card_8.name == "8" + assert new_card_9.name == "9" + assert new_card_10.name == "10" + assert new_card_J.name == "J" + assert new_card_Q.name == "Q" + assert new_card_K.name == "K" + + assert new_card_A.suit == "clubs" + assert new_card_2.suit == "hearts" + assert new_card_3.suit == "spades" + assert new_card_4.suit == "diamonds" + assert new_card_5.suit == "clubs" + assert new_card_6.suit == "hearts" + assert new_card_7.suit == "spades" + assert new_card_8.suit == "diamonds" + assert new_card_9.suit == "clubs" + assert new_card_10.suit == "hearts" + assert new_card_J.suit == "spades" + assert new_card_Q.suit == "diamonds" + assert new_card_K.suit == "clubs" + + assert new_card_A.value == 11 + assert new_card_2.value == 2 + assert new_card_3.value == 3 + assert new_card_4.value == 4 + assert new_card_5.value == 5 + assert new_card_6.value == 6 + assert new_card_7.value == 7 + assert new_card_8.value == 8 + assert new_card_9.value == 9 + assert new_card_10.value == 10 + assert new_card_J.value == 10 + assert new_card_Q.value == 10 + assert new_card_K.value == 10 + + +def test_swap_ace(): + new_card = Card("A", "hearts") + assert new_card.value == 11 + new_card.swap_ace() + assert new_card.value == 1 diff --git a/blackjack/tests/test_deck.py b/blackjack/tests/test_deck.py new file mode 100644 index 0000000..e7a6d0f --- /dev/null +++ b/blackjack/tests/test_deck.py @@ -0,0 +1,43 @@ +from deck import Deck +from card import Card + + +def test_deck_creation(): + new_deck = Deck() + assert new_deck + + +def test_deck_length(): + new_deck = Deck() + assert len(new_deck.cards) == 52 + + +def test_shoe_creation(): + new_deck = Deck(2) + assert len(new_deck.cards) == 104 + new_deck_2 = Deck(4) + assert len(new_deck_2.cards) == 208 + + +def test_shuffle_deck(): + suits = ["hearts", "spades", "clubs", "diamonds"] + names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "J", "Q", "K"] + unshuffled_deck = [Card(name, suit) for suit in suits + for name in names] + new_deck = Deck() + + assert new_deck.cards != unshuffled_deck + + +def test_deal_cards(): + new_deck = Deck() + assert new_deck.deal() + + +def test_reshuffle_deck(): + new_deck = Deck() + new_deck.deal() + new_deck.deal() + new_deck.reshuffle() + assert len(new_deck.cards) == 52 diff --git a/blackjack/tests/test_player.py b/blackjack/tests/test_player.py new file mode 100644 index 0000000..28f27cc --- /dev/null +++ b/blackjack/tests/test_player.py @@ -0,0 +1,29 @@ +from player import Player + + +def test_player_creation(): + player = Player("Alan") + assert player + + +def test_player_attributes(): + player = Player("Alan") + assert player.name == "Alan" + assert player.money == 1000 + assert player.hands == [] + + +def test_modify_money(): + player = Player("Alan") + player.modify_money(1000) + assert player.money == 2000 + player.modify_money(-500) + assert player.money == (1500) + + +def test_modify_money_exception(): + player = Player("Alan") + try: + player.modify_money(-1001) + except ValueError: + assert True From 0003d98d636581a876ecd71eb08fa0760485a675 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 08:07:14 -0500 Subject: [PATCH 04/50] Forgot to add README.md to staging on previous commit. --- blackjack/README.md | 160 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 blackjack/README.md diff --git a/blackjack/README.md b/blackjack/README.md new file mode 100644 index 0000000..bb5ca29 --- /dev/null +++ b/blackjack/README.md @@ -0,0 +1,160 @@ +Alan Grissett +alan.grissett@gmail.com + +The Iron Yard - Durham +Python Development - Cohort 3 +[ Key: + + - Implemented + * - Not Yet Implemented + ? - Unsure if should be included in class + \ - Planned But Low Priority +] +Blackjack Project + +The goal of this project is to complete a fully functional console based +version of Blackjack. The game will allow the player to select options for +the rules used in the play of the game - including the number of decks used, +dealer hit/stand on soft 17, early/late surrender, hit/resplit aces, +insurance, double on 9/10/11 or 10/11 only and no double on split. + +The game will be divided into classes for each component of the game. +The initial class plan is as follows and is subject to change: + + ++ : The Card class will contain the information about a single card in the +deck. Cards will have attributes for rank, suit and value. Numbered cards +are worth their face value, while Jacks, Queens and Kings are worth 10. Aces +are worth 1 or 11 depending on the player's current hand value. The Card +class will provide a method for updating the value of the ace if requested +to do so by the game. + +The Card objects will be created and stored by the Deck class and will +subsequently be distributed to Hand objects assigned to the player and +dealer respectively. + +To Do: ++ - Assign class attributes ++ - Add method to evaluate the value of Card ++ - Add ASCII representations of suits ++ - Add method for swapping value of Aces + + ++ : The Deck class will be an object containing a collection of at least +one full deck of cards upon initialization. The Deck will construct a list +containing at least one of each card in a standard playing deck and then +use a random number generator to randomize the location of the cards within +the list. The Deck defaults to having a single set of 52 cards, but has an +optional argument to contain any number of decks (with a maximum of 8). +The Deck contains methods to reshuffle the deck and to remove cards from +the list and return their values to the requester. + +To Do: ++ - Generate a list containing Card objects representing a full deck of cards ++ - Randomize card locations within the deck list ++ - Add optional class attribute to increase the number of decks of Card + objects created ++ - Add a method to remove a card from the card list and return the value ++ - Add a method to reshuffle the deck of cards +* - Add a randomly placed flag between the last 35-52 cards to indicate + the deck should be reshuffled when using more than one deck. + + +*/ : The player class object will contain the amount of money the +player has remaining to wager and a list of hands (multiple hands on split) +controlled by the player. + +To Do: ++ - Add class attributes ++ - Add methods to increment or decrement remaining money ++ - Add name attribute +/ - Add method to save player state so the game can be resumed with current + money amount maintained + + +* : The Dealer class will contain a single Hand object corresponding +to the cards it is dealt. It will also contain a rule set for deciding +whether to hit or stand for it's given hand value. + +To Do: +* - Add class attributes +* - Add basic decision rules +/ - Add optional rules based on user selected options + + +* : The Hand class will contain a list of cards currently associated +with a Player or Dealer. It will have a method for determining and returning +the current value of itself and a method to receive new cards from the deck +and add them to the class's Card list. + +To Do: +* - Add class attribute and initialization +* - Add method to evaluate value of cards +* - Add method to request change in the value of an ace if present in the hand +* - Add method to receive Card objects and add them to the Hand's Card list + + +* : The GameOptions class will store all of the user selected +rules for gameplay. Attributes will include an integer value between 1 and +8 representing the number of decks to be used in the game, as well as a +series of Boolean values with default values that will activate or deactivate +certain rule variations. + +To Do: +* - Add basic list of class attributes, starting with number of decks +* - Add in common rule variations. No hit on soft 17 and Late/Early surrender +/ - Add in advanced rule variations + + +* : The Game class will be the backbone of the game. It will store +the wager amount made by the player, created and distribute Hand objects to +the Player and Dealer and will control game flow based on hand +values held by the player and dealer. It will have methods to calculate +payouts based off of results of the game. Payouts will be 2:1 for a standard +win and 3:2 on blackjack. The Game class will pass the payout information +for use by the Player class to update the Player's money total. ?May create +the Dealer object and retrieve the ruleset from GameOptions.? + +To Do: +* - Add list of Game attributes +* - Add method to distribute Hands to Player and Dealer +* - Add method to check the status of the game by resolving Hand values +* - Add method to distribute payouts to the player +? - Add method for determining available actions for the player (Maybe in Hand + Class?) +? - Add method to retrieve the ruleset from GameOptions and create the + Dealer based on currently selected rules. (Maybe in Blackjack class?) + + +* : The Blackjack class will be the driver script for the game. +It will control all of the program flow and interact with the Game and +Interface Classes to start new hands, request menu displays, request output +to the console from the Interface Class. This file will contain the +main method for the game, and will be the script executed to start the +game. + +To Do: +* - Plan overall program flow and determine best structure for this class +* - Add main method to initialize the game and start program flow + +* : The Interface class will contain all of the ASCII text that +will be used for in-game card representations, menus and methods for +receiving input from the user. + +To Do: +* - Design main menu +* - Design options menu +* - Add method for updating options based on user input from options menu +* - Add method for displaying current hands for the dealer and player to + the console +* - Add method to display win/lose text after each hand +...more + + +/////: Very unlikely, but this class would replace the +Interface class to provide a graphical game experience. The interface +would allow for mouse input or keyboard input corresponding to available +menu options or avaiable actions within the game. The rest of the +functionality would be identical to interface. + +To Do: +* - The rest of the project From d9c7b28e87a67c01868be4e0716c9d9d2d907a89 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 08:40:21 -0500 Subject: [PATCH 05/50] Deleted /lib/blackjack/ files --- lib/blackjack/card.py | 47 ------------------------- lib/blackjack/deck.py | 43 ----------------------- lib/blackjack/test_card.py | 72 -------------------------------------- lib/blackjack/test_deck.py | 43 ----------------------- 4 files changed, 205 deletions(-) delete mode 100644 lib/blackjack/card.py delete mode 100644 lib/blackjack/deck.py delete mode 100644 lib/blackjack/test_card.py delete mode 100644 lib/blackjack/test_deck.py diff --git a/lib/blackjack/card.py b/lib/blackjack/card.py deleted file mode 100644 index 2840869..0000000 --- a/lib/blackjack/card.py +++ /dev/null @@ -1,47 +0,0 @@ -class Card: - """Playing card class. Each instance represents a single playing card. - - Responsibilities: - - * Each card has a string name attribute corresponding to it's value - * Each card has a suit attribute - * Each card has a value determined by the private evaluate_card_value - method - * Ace value defaults to 11 but can be changed to 1 by calling the - swap_ace method. - - Collaborators: - - * Cards are collected into decks of varying sizes - * Cards are distributed to player and dealer hands - """ - - suits = {"spades": "♤", - "hearts": "♡", - "clubs": "♧", - "diamonds": "♢"} - - def __init__(self, name, suit): - self.name = name - self.suit = suit - self.__evaluate_card_value__() - - def __str__(self): - return self.name + self.suits[self.suit] - - def __repr__(self): - return self.__str__() - - def __evaluate_card_value__(self): - if self.name.isdigit(): - self.value = int(self.name) - elif self.name == "A": - self.value = 11 - else: - self.value = 10 - - def swap_ace(self): - if self.value == 11: - self.value = 1 - else: - self.value = 11 diff --git a/lib/blackjack/deck.py b/lib/blackjack/deck.py deleted file mode 100644 index b2272e0..0000000 --- a/lib/blackjack/deck.py +++ /dev/null @@ -1,43 +0,0 @@ -from random import shuffle -from card import Card - - -class Deck: - """Deck of cards class. A deck of cards is a collection of card objects. - - Responsibilities: - - * The Deck class generates a series of Card objects corresponding with - the required cards in a complete deck of cards. - * It is also responsible for randomizing the location of the cards - within the deck list. - * Takes an optional argument decks at creation to allow for creation of - a shoe containing any number of decks. - - Collaborators: - - * Deck collects Card Objects - * Deck pops and returns cards from it's card list when requested by - a calling function. - * Deck can be reinitialized by an outside function by calling the - reshuffle method. - """ - - def __init__(self, decks=1): - self.decks = decks - suits = ["hearts", "spades", "clubs", "diamonds"] - names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", - "10", "J", "Q", "K"] - self.cards = [Card(name, suit) for suit in suits - for name in names - for deck in range(self.decks)] - shuffle(self.cards) - - def deal(self): - """Pops a single card from the end of the card list and returns - it to the calling function""" - return self.cards.pop() - - def reshuffle(self): - """Reinitializes the deck""" - self.__init__() diff --git a/lib/blackjack/test_card.py b/lib/blackjack/test_card.py deleted file mode 100644 index 14cf5b5..0000000 --- a/lib/blackjack/test_card.py +++ /dev/null @@ -1,72 +0,0 @@ -from card import Card -"""Test functions for the card class""" - - -def test_construction(): - new_card = Card("2", "clubs") - assert new_card - - -def test_card_attributes(): - new_card_A = Card("A", "clubs") - new_card_2 = Card("2", "hearts") - new_card_3 = Card("3", "spades") - new_card_4 = Card("4", "diamonds") - new_card_5 = Card("5", "clubs") - new_card_6 = Card("6", "hearts") - new_card_7 = Card("7", "spades") - new_card_8 = Card("8", "diamonds") - new_card_9 = Card("9", "clubs") - new_card_10 = Card("10", "hearts") - new_card_J = Card("J", "spades") - new_card_Q = Card("Q", "diamonds") - new_card_K = Card("K", "clubs") - - assert new_card_A.name == "A" - assert new_card_2.name == "2" - assert new_card_3.name == "3" - assert new_card_4.name == "4" - assert new_card_5.name == "5" - assert new_card_6.name == "6" - assert new_card_7.name == "7" - assert new_card_8.name == "8" - assert new_card_9.name == "9" - assert new_card_10.name == "10" - assert new_card_J.name == "J" - assert new_card_Q.name == "Q" - assert new_card_K.name == "K" - - assert new_card_A.suit == "clubs" - assert new_card_2.suit == "hearts" - assert new_card_3.suit == "spades" - assert new_card_4.suit == "diamonds" - assert new_card_5.suit == "clubs" - assert new_card_6.suit == "hearts" - assert new_card_7.suit == "spades" - assert new_card_8.suit == "diamonds" - assert new_card_9.suit == "clubs" - assert new_card_10.suit == "hearts" - assert new_card_J.suit == "spades" - assert new_card_Q.suit == "diamonds" - assert new_card_K.suit == "clubs" - - assert new_card_A.value == 11 - assert new_card_2.value == 2 - assert new_card_3.value == 3 - assert new_card_4.value == 4 - assert new_card_5.value == 5 - assert new_card_6.value == 6 - assert new_card_7.value == 7 - assert new_card_8.value == 8 - assert new_card_9.value == 9 - assert new_card_10.value == 10 - assert new_card_J.value == 10 - assert new_card_Q.value == 10 - assert new_card_K.value == 10 - - -def test_swap_ace(): - new_card = Card("A", "hearts") - assert new_card.value == 11 - new_card.swap_ace() - assert new_card.value == 1 diff --git a/lib/blackjack/test_deck.py b/lib/blackjack/test_deck.py deleted file mode 100644 index e7a6d0f..0000000 --- a/lib/blackjack/test_deck.py +++ /dev/null @@ -1,43 +0,0 @@ -from deck import Deck -from card import Card - - -def test_deck_creation(): - new_deck = Deck() - assert new_deck - - -def test_deck_length(): - new_deck = Deck() - assert len(new_deck.cards) == 52 - - -def test_shoe_creation(): - new_deck = Deck(2) - assert len(new_deck.cards) == 104 - new_deck_2 = Deck(4) - assert len(new_deck_2.cards) == 208 - - -def test_shuffle_deck(): - suits = ["hearts", "spades", "clubs", "diamonds"] - names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", - "10", "J", "Q", "K"] - unshuffled_deck = [Card(name, suit) for suit in suits - for name in names] - new_deck = Deck() - - assert new_deck.cards != unshuffled_deck - - -def test_deal_cards(): - new_deck = Deck() - assert new_deck.deal() - - -def test_reshuffle_deck(): - new_deck = Deck() - new_deck.deal() - new_deck.deal() - new_deck.reshuffle() - assert len(new_deck.cards) == 52 From e088f8e34e4145cfa1f7b118669dbece541428c1 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 13:09:50 -0500 Subject: [PATCH 06/50] Updated some test files and added Hand class. --- blackjack/hand.py | 31 +++++++++++++++++++- blackjack/tests/test_card.py | 31 +++++++++----------- blackjack/tests/test_deck.py | 19 ++---------- blackjack/tests/test_hand.py | 56 ++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 35 deletions(-) create mode 100644 blackjack/tests/test_hand.py diff --git a/blackjack/hand.py b/blackjack/hand.py index 8621583..2e5d539 100644 --- a/blackjack/hand.py +++ b/blackjack/hand.py @@ -1,3 +1,32 @@ class Hand: - pass + def __init__(self, cards=[]): + self.cards = cards + + def add_cards(self, card): + self.cards.append(card) + + + def get_ranks(self): + card_rank_list = [] + + for card in self.cards: + card_rank_list.append(card.rank) + + return card_rank_list + + def get_value(self): + hand_value = 0 + for card in self.cards: + hand_value += card.value + + if ((hand_value > 21 and "A" in self.get_ranks()) or + hand_value <= 11 and "A" in self.get_ranks()): + for card in self.cards: + if hand_value > 21 and card.value == 11: + card.swap_ace() + hand_value = self.get_value() + elif hand_value <= 11 and card.value == 1: + card.swap_ace() + hand_value = self.get_value() + return hand_value diff --git a/blackjack/tests/test_card.py b/blackjack/tests/test_card.py index 14cf5b5..9746a68 100644 --- a/blackjack/tests/test_card.py +++ b/blackjack/tests/test_card.py @@ -2,11 +2,6 @@ """Test functions for the card class""" -def test_construction(): - new_card = Card("2", "clubs") - assert new_card - - def test_card_attributes(): new_card_A = Card("A", "clubs") new_card_2 = Card("2", "hearts") @@ -22,19 +17,19 @@ def test_card_attributes(): new_card_Q = Card("Q", "diamonds") new_card_K = Card("K", "clubs") - assert new_card_A.name == "A" - assert new_card_2.name == "2" - assert new_card_3.name == "3" - assert new_card_4.name == "4" - assert new_card_5.name == "5" - assert new_card_6.name == "6" - assert new_card_7.name == "7" - assert new_card_8.name == "8" - assert new_card_9.name == "9" - assert new_card_10.name == "10" - assert new_card_J.name == "J" - assert new_card_Q.name == "Q" - assert new_card_K.name == "K" + assert new_card_A.rank == "A" + assert new_card_2.rank == "2" + assert new_card_3.rank == "3" + assert new_card_4.rank == "4" + assert new_card_5.rank == "5" + assert new_card_6.rank == "6" + assert new_card_7.rank == "7" + assert new_card_8.rank == "8" + assert new_card_9.rank == "9" + assert new_card_10.rank == "10" + assert new_card_J.rank == "J" + assert new_card_Q.rank == "Q" + assert new_card_K.rank == "K" assert new_card_A.suit == "clubs" assert new_card_2.suit == "hearts" diff --git a/blackjack/tests/test_deck.py b/blackjack/tests/test_deck.py index e7a6d0f..c2f8884 100644 --- a/blackjack/tests/test_deck.py +++ b/blackjack/tests/test_deck.py @@ -2,11 +2,6 @@ from card import Card -def test_deck_creation(): - new_deck = Deck() - assert new_deck - - def test_deck_length(): new_deck = Deck() assert len(new_deck.cards) == 52 @@ -21,10 +16,10 @@ def test_shoe_creation(): def test_shuffle_deck(): suits = ["hearts", "spades", "clubs", "diamonds"] - names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", + ranks = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"] - unshuffled_deck = [Card(name, suit) for suit in suits - for name in names] + unshuffled_deck = [Card(rank, suit) for suit in suits + for rank in ranks] new_deck = Deck() assert new_deck.cards != unshuffled_deck @@ -33,11 +28,3 @@ def test_shuffle_deck(): def test_deal_cards(): new_deck = Deck() assert new_deck.deal() - - -def test_reshuffle_deck(): - new_deck = Deck() - new_deck.deal() - new_deck.deal() - new_deck.reshuffle() - assert len(new_deck.cards) == 52 diff --git a/blackjack/tests/test_hand.py b/blackjack/tests/test_hand.py new file mode 100644 index 0000000..483e1e8 --- /dev/null +++ b/blackjack/tests/test_hand.py @@ -0,0 +1,56 @@ +from hand import Hand +from card import Card + + +def test_card_list_holds_cards(): + test_cards = [] + test_cards.append(Card("10", "diamonds")) + test_cards.append(Card("J", "clubs")) + + hand = Hand(test_cards) + assert hand.cards + +def test_get_hand_value(): + test_cards = [] + test_cards.append(Card("10", "diamonds")) + test_cards.append(Card("J", "clubs")) + + hand = Hand(test_cards) + assert hand.get_value() == 20 + +def test_get_ranks(): + test_cards = [] + test_cards.append(Card("10", "diamonds")) + test_cards.append(Card("J", "clubs")) + + hand = Hand(test_cards) + assert hand.get_ranks() == ["10", "J"] + + +def test_ace_detection_and_swap(): + test_cards = [] + test_cards.append(Card("10", "diamonds")) + test_cards.append(Card("J", "clubs")) + test_cards.append(Card("A", "hearts")) + + hand = Hand(test_cards) + + assert hand.get_value() == 21 + +def test_two_aces(): + test_cards = [] + test_cards.append(Card("A", "diamonds")) + test_cards.append(Card("A", "hearts")) + + hand = Hand(test_cards) + + assert hand.get_value() == 12 + +def test_ace_value_incorrect(): + test_cards = [] + test_cards.append(Card("A", "spades")) + test_cards.append(Card("10", "clubs")) + test_cards[0].swap_ace() + hand = Hand(test_cards) + + assert hand.get_value() == 21 From 5ccd1a504e6677e600ab535bacef9ea673687ac3 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 13:11:44 -0500 Subject: [PATCH 07/50] Updated a few variable names in each file. Moved README to base directory. --- blackjack/README.md | 160 ----------------------------------- blackjack/card.py | 22 +++-- blackjack/deck.py | 14 +-- blackjack/player.py | 6 +- blackjack/tests/test_hand.py | 5 +- 5 files changed, 26 insertions(+), 181 deletions(-) delete mode 100644 blackjack/README.md diff --git a/blackjack/README.md b/blackjack/README.md deleted file mode 100644 index bb5ca29..0000000 --- a/blackjack/README.md +++ /dev/null @@ -1,160 +0,0 @@ -Alan Grissett -alan.grissett@gmail.com - -The Iron Yard - Durham -Python Development - Cohort 3 -[ Key: - + - Implemented - * - Not Yet Implemented - ? - Unsure if should be included in class - \ - Planned But Low Priority -] -Blackjack Project - -The goal of this project is to complete a fully functional console based -version of Blackjack. The game will allow the player to select options for -the rules used in the play of the game - including the number of decks used, -dealer hit/stand on soft 17, early/late surrender, hit/resplit aces, -insurance, double on 9/10/11 or 10/11 only and no double on split. - -The game will be divided into classes for each component of the game. -The initial class plan is as follows and is subject to change: - - -+ : The Card class will contain the information about a single card in the -deck. Cards will have attributes for rank, suit and value. Numbered cards -are worth their face value, while Jacks, Queens and Kings are worth 10. Aces -are worth 1 or 11 depending on the player's current hand value. The Card -class will provide a method for updating the value of the ace if requested -to do so by the game. - -The Card objects will be created and stored by the Deck class and will -subsequently be distributed to Hand objects assigned to the player and -dealer respectively. - -To Do: -+ - Assign class attributes -+ - Add method to evaluate the value of Card -+ - Add ASCII representations of suits -+ - Add method for swapping value of Aces - - -+ : The Deck class will be an object containing a collection of at least -one full deck of cards upon initialization. The Deck will construct a list -containing at least one of each card in a standard playing deck and then -use a random number generator to randomize the location of the cards within -the list. The Deck defaults to having a single set of 52 cards, but has an -optional argument to contain any number of decks (with a maximum of 8). -The Deck contains methods to reshuffle the deck and to remove cards from -the list and return their values to the requester. - -To Do: -+ - Generate a list containing Card objects representing a full deck of cards -+ - Randomize card locations within the deck list -+ - Add optional class attribute to increase the number of decks of Card - objects created -+ - Add a method to remove a card from the card list and return the value -+ - Add a method to reshuffle the deck of cards -* - Add a randomly placed flag between the last 35-52 cards to indicate - the deck should be reshuffled when using more than one deck. - - -*/ : The player class object will contain the amount of money the -player has remaining to wager and a list of hands (multiple hands on split) -controlled by the player. - -To Do: -+ - Add class attributes -+ - Add methods to increment or decrement remaining money -+ - Add name attribute -/ - Add method to save player state so the game can be resumed with current - money amount maintained - - -* : The Dealer class will contain a single Hand object corresponding -to the cards it is dealt. It will also contain a rule set for deciding -whether to hit or stand for it's given hand value. - -To Do: -* - Add class attributes -* - Add basic decision rules -/ - Add optional rules based on user selected options - - -* : The Hand class will contain a list of cards currently associated -with a Player or Dealer. It will have a method for determining and returning -the current value of itself and a method to receive new cards from the deck -and add them to the class's Card list. - -To Do: -* - Add class attribute and initialization -* - Add method to evaluate value of cards -* - Add method to request change in the value of an ace if present in the hand -* - Add method to receive Card objects and add them to the Hand's Card list - - -* : The GameOptions class will store all of the user selected -rules for gameplay. Attributes will include an integer value between 1 and -8 representing the number of decks to be used in the game, as well as a -series of Boolean values with default values that will activate or deactivate -certain rule variations. - -To Do: -* - Add basic list of class attributes, starting with number of decks -* - Add in common rule variations. No hit on soft 17 and Late/Early surrender -/ - Add in advanced rule variations - - -* : The Game class will be the backbone of the game. It will store -the wager amount made by the player, created and distribute Hand objects to -the Player and Dealer and will control game flow based on hand -values held by the player and dealer. It will have methods to calculate -payouts based off of results of the game. Payouts will be 2:1 for a standard -win and 3:2 on blackjack. The Game class will pass the payout information -for use by the Player class to update the Player's money total. ?May create -the Dealer object and retrieve the ruleset from GameOptions.? - -To Do: -* - Add list of Game attributes -* - Add method to distribute Hands to Player and Dealer -* - Add method to check the status of the game by resolving Hand values -* - Add method to distribute payouts to the player -? - Add method for determining available actions for the player (Maybe in Hand - Class?) -? - Add method to retrieve the ruleset from GameOptions and create the - Dealer based on currently selected rules. (Maybe in Blackjack class?) - - -* : The Blackjack class will be the driver script for the game. -It will control all of the program flow and interact with the Game and -Interface Classes to start new hands, request menu displays, request output -to the console from the Interface Class. This file will contain the -main method for the game, and will be the script executed to start the -game. - -To Do: -* - Plan overall program flow and determine best structure for this class -* - Add main method to initialize the game and start program flow - -* : The Interface class will contain all of the ASCII text that -will be used for in-game card representations, menus and methods for -receiving input from the user. - -To Do: -* - Design main menu -* - Design options menu -* - Add method for updating options based on user input from options menu -* - Add method for displaying current hands for the dealer and player to - the console -* - Add method to display win/lose text after each hand -...more - - -/////: Very unlikely, but this class would replace the -Interface class to provide a graphical game experience. The interface -would allow for mouse input or keyboard input corresponding to available -menu options or avaiable actions within the game. The rest of the -functionality would be identical to interface. - -To Do: -* - The rest of the project diff --git a/blackjack/card.py b/blackjack/card.py index 2840869..c16122c 100644 --- a/blackjack/card.py +++ b/blackjack/card.py @@ -1,9 +1,14 @@ +suits = ["hearts", "spades", "clubs", "diamonds"] +ranks = ["A", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "J", "Q", "K"] + + class Card: """Playing card class. Each instance represents a single playing card. Responsibilities: - * Each card has a string name attribute corresponding to it's value + * Each card has a string rank attribute corresponding to it's value * Each card has a suit attribute * Each card has a value determined by the private evaluate_card_value method @@ -21,21 +26,24 @@ class Card: "clubs": "♧", "diamonds": "♢"} - def __init__(self, name, suit): - self.name = name + def __init__(self, rank, suit): + self.rank = rank self.suit = suit self.__evaluate_card_value__() def __str__(self): - return self.name + self.suits[self.suit] + return self.rank + self.suits[self.suit] def __repr__(self): return self.__str__() + def __eq__(self, other): + return self.rank == other.rank and self.suit == other.suit + def __evaluate_card_value__(self): - if self.name.isdigit(): - self.value = int(self.name) - elif self.name == "A": + if self.rank.isdigit(): + self.value = int(self.rank) + elif self.rank == "A": self.value = 11 else: self.value = 10 diff --git a/blackjack/deck.py b/blackjack/deck.py index b2272e0..4e19d02 100644 --- a/blackjack/deck.py +++ b/blackjack/deck.py @@ -1,5 +1,5 @@ from random import shuffle -from card import Card +from card import Card, ranks, suits class Deck: @@ -25,11 +25,9 @@ class Deck: def __init__(self, decks=1): self.decks = decks - suits = ["hearts", "spades", "clubs", "diamonds"] - names = ["A", "2", "3", "4", "5", "6", "7", "8", "9", - "10", "J", "Q", "K"] - self.cards = [Card(name, suit) for suit in suits - for name in names + + self.cards = [Card(rank, suit) for suit in suits + for rank in ranks for deck in range(self.decks)] shuffle(self.cards) @@ -37,7 +35,3 @@ def deal(self): """Pops a single card from the end of the card list and returns it to the calling function""" return self.cards.pop() - - def reshuffle(self): - """Reinitializes the deck""" - self.__init__() diff --git a/blackjack/player.py b/blackjack/player.py index 7ccdade..7fc520c 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -1,6 +1,6 @@ class Player: - """This is the player class, it contains information about the player's - hand and hand value. + """This is the player class, it contains a list of hands controlled by the + player and keeps track of how much money the player has won. Responsibilities: @@ -15,7 +15,7 @@ class Player: def __init__(self, name): self.name = name self.hands = [] - self.money = 1000 + self.money = 100 def modify_money(self, value): if self.money + value > 0: diff --git a/blackjack/tests/test_hand.py b/blackjack/tests/test_hand.py index 483e1e8..fbd34de 100644 --- a/blackjack/tests/test_hand.py +++ b/blackjack/tests/test_hand.py @@ -10,6 +10,7 @@ def test_card_list_holds_cards(): hand = Hand(test_cards) assert hand.cards + def test_get_hand_value(): test_cards = [] test_cards.append(Card("10", "diamonds")) @@ -18,6 +19,7 @@ def test_get_hand_value(): hand = Hand(test_cards) assert hand.get_value() == 20 + def test_get_ranks(): test_cards = [] test_cards.append(Card("10", "diamonds")) @@ -34,9 +36,9 @@ def test_ace_detection_and_swap(): test_cards.append(Card("A", "hearts")) hand = Hand(test_cards) - assert hand.get_value() == 21 + def test_two_aces(): test_cards = [] test_cards.append(Card("A", "diamonds")) @@ -46,6 +48,7 @@ def test_two_aces(): assert hand.get_value() == 12 + def test_ace_value_incorrect(): test_cards = [] test_cards.append(Card("A", "spades")) From 4c415c75320ea2968a934fb05767ee10b3991fd2 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 13:18:28 -0500 Subject: [PATCH 08/50] Added docstring to the beginning of hand.py --- README_1.md | 76 ---------------------------------------------- README_2.md | 77 ----------------------------------------------- blackjack/hand.py | 17 ++++++++++- 3 files changed, 16 insertions(+), 154 deletions(-) delete mode 100644 README_1.md delete mode 100644 README_2.md diff --git a/README_1.md b/README_1.md deleted file mode 100644 index 0795a37..0000000 --- a/README_1.md +++ /dev/null @@ -1,76 +0,0 @@ -# Blackjack, Part I - -## Description - -Plan how to create a game of blackjack. - -## Objectives - -### Learning Objectives - -After completing this assignment, you should understand: - -* Object-oriented design. - -### Performance Objectives - -After completing this assignment, you should be able to: - -* Design a system using responsibilities and collaborators. -* Turn your design into objects. - -## Details - -### Deliverables - -* A Git repo called blackjack containing at least: - * a `README.md` file explaining your design. - * a `lib/blackjack` directory with a file for each of your classes, with - their responsibilities and collaborators described in a comment - * a completed Card and Deck class - * tests for Cards and Decks - -### Requirements - -* Passing unit tests -* No PEP8 or Pyflakes warnings or errors -* Use the project layout from _The Hacker's Guide to Python_ - -## Normal Mode - -Read through [the rules of blackjack](https://en.wikipedia.org/wiki/Blackjack) -carefully. After reading through them, write out the steps to run the game in -outline format. (See the additional resources for more on the rules of -blackjack.) - -After that, go through your steps and find all the actors -- that is, nouns -that take actions. Create class-responsibility-collaborator (CRC) cards for -each and then create empty classes for each of them with the responsibilities -and collaborators at the top as a comment. Here is an example that you might -find in `lib/blackjack/card.py`: - -```py -class Card: - """A playing card. - - Responsibilities: - - * Has a rank and a suit. - * Has a point value. Aces point values depend on the Hand. - - Collaborators: - - * Collected into a Deck. - * Collected into a Hand for each player and a Hand for the dealer. - """ -``` - -Lastly, implement the `Card` and `Deck` classes. - -## Additional Resources - -* Other Blackjack rule summaries: - * http://www.pagat.com/banking/blackjack.html - * http://wizardofodds.com/games/blackjack/basics/#toc-Rules -* [Portland Pattern Repository page on CRC cards](http://c2.com/cgi/wiki?CrcCard) -* [A Brief Tour of Responsibility-Driven Design](http://www.wirfs-brock.com/PDFs/A_Brief-Tour-of-RDD.pdf) diff --git a/README_2.md b/README_2.md deleted file mode 100644 index 1e6c0bb..0000000 --- a/README_2.md +++ /dev/null @@ -1,77 +0,0 @@ -# Blackjack - -## Description - -Implement the game of Blackjack. - -## Objectives - -### Learning Objectives - -After completing this assignment, you should understand: - -* how to track state by using objects - -### Performance Objectives - -After completing this assignment, you should be able to: - -* Build an interactive game -* Test an object-oriented program - -## Details - -### Deliverables - -* A Git repo called blackjack containing at least: - * `README.rst` file explaining how to run your project - * a `requirements.txt` file - * a full suite of tests for your project - -### Requirements - -* Passing unit tests -* No PEP8 or Pyflakes warnings or errors - -## Normal Mode - -Take your notes and code from [the previous project](README_1.md). Using those, -create a game of Blackjack that one person plays on the command line against a -computer dealer, with the following rules: - -* The game should start the player with $100 and bets are $10. -* The only valid moves are hit and stand. -* Allow the player to keep playing as long as they have money. -* The dealer uses one deck in their shoe and reshuffles after each round. - -## Hard Mode - -In addition to the requirements from **Normal Mode**: - -* The dealer uses a shoe of six decks. With a shoe, the dealer uses something called a _cut card_. A plastic card is inserted somewhere near the bottom of the shoe. Once it is hit, the shoe is reshuffled at the end of the round. You can simulate this by reshuffling after there are 26 or less cards left in the shoe. -* The player can choose how much they want to bet before each round. -* Add doubling-down. -* Add surrender (early surrender as opposed to late surrender.) -* Add [insurance](https://en.wikipedia.org/wiki/Blackjack#Insurance). -* Add splitting hands. - -## Nightmare Mode - -In addition to the requirements from **Hard Mode**: - -* Add the ability to choose rulesets, like: - * No surrender/early surrender/late surrender. - * Dealer hits soft 17 vs dealer stands of soft 17. - * Number of decks used in the shoe. - -Each choice should be able to be made separately. - -## Notes - -This is again an assignment with a text-based interface, which can be very hard -to test. You will do best at this assignment by building all the logic for each -piece and testing it well before then adding the interface on top of it. - -## Additional Resources - -* [Building Skills in Object-Oriented Design](http://www.itmaybeahack.com/book/oodesign-python-2.1/html/index.html) diff --git a/blackjack/hand.py b/blackjack/hand.py index 2e5d539..4b339ab 100644 --- a/blackjack/hand.py +++ b/blackjack/hand.py @@ -1,4 +1,20 @@ class Hand: + """ Hand class contains the current cards in play for either the player + or dealer. It has methods to return the total value of the cards held + as well as update the value of the Ace based on the current value of + the hand. + + Responsibilities: + * Container for Card objects + * Reporting the combined value of all Card objects held + * Logic to ensure that Aces are counted as the correct value based on + the total value of the current hand. + + Interactions: + * Hand objects are distributed to the player and the dealer. + * Hand objects receive card objects from the deck. + """ + def __init__(self, cards=[]): self.cards = cards @@ -6,7 +22,6 @@ def __init__(self, cards=[]): def add_cards(self, card): self.cards.append(card) - def get_ranks(self): card_rank_list = [] From 3467a93d5345ea50652431a7ada131d01c359202 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 13:51:36 -0500 Subject: [PATCH 09/50] Finished basic layout for dealer using rule that dealer always stands on 17. Added tests. --- README.md | 163 ++++++++++++++++++++++++++++++++- blackjack/dealer.py | 22 +++++ blackjack/tests/test_dealer.py | 20 ++++ blackjack/tests/test_player.py | 12 +-- 4 files changed, 208 insertions(+), 9 deletions(-) create mode 100644 blackjack/dealer.py create mode 100644 blackjack/tests/test_dealer.py diff --git a/README.md b/README.md index e5d7b00..16df601 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,161 @@ -# Blackjack +Alan Grissett +alan.grissett@gmail.com -* [Part 1](README_1.md) -* [Part 2](README_2.md) \ No newline at end of file +The Iron Yard - Durham +Python Development - Cohort 3 +[ Key: + + - Implemented + * - Not Yet Implemented + ? - Unsure if should be included in class + \ - Planned But Low Priority +] +Blackjack Project + +The goal of this project is to complete a fully functional console based +version of Blackjack. The game will allow the player to select options for +the rules used in the play of the game - including the number of decks used, +dealer hit/stand on soft 17, early/late surrender, hit/resplit aces, +insurance, double on 9/10/11 or 10/11 only and no double on split. + +The game will be divided into classes for each component of the game. +The initial class plan is as follows and is subject to change: + + ++ : The Card class will contain the information about a single card in the +deck. Cards will have attributes for rank, suit and value. Numbered cards +are worth their face value, while Jacks, Queens and Kings are worth 10. Aces +are worth 1 or 11 depending on the player's current hand value. The Card +class will provide a method for updating the value of the ace if requested +to do so by the game. + +The Card objects will be created and stored by the Deck class and will +subsequently be distributed to Hand objects assigned to the player and +dealer respectively. + +To Do: ++ - Assign class attributes ++ - Add method to evaluate the value of Card ++ - Add ASCII representations of suits ++ - Add method for swapping value of Aces + + ++ : The Deck class will be an object containing a collection of at least +one full deck of cards upon initialization. The Deck will construct a list +containing at least one of each card in a standard playing deck and then +use a random number generator to randomize the location of the cards within +the list. The Deck defaults to having a single set of 52 cards, but has an +optional argument to contain any number of decks (with a maximum of 8). +The Deck contains methods to reshuffle the deck and to remove cards from +the list and return their values to the requester. + +To Do: ++ - Generate a list containing Card objects representing a full deck of cards ++ - Randomize card locations within the deck list ++ - Add optional class attribute to increase the number of decks of Card + objects created ++ - Add a method to remove a card from the card list and return the value ++ - Add a method to reshuffle the deck of cards +* - Add a randomly placed flag between the last 35-52 cards to indicate + the deck should be reshuffled when using more than one deck. + + ++/ : The player class object will contain the amount of money the +player has remaining to wager and a list of hands (multiple hands on split) +controlled by the player. + +To Do: ++ - Add class attributes ++ - Add methods to increment or decrement remaining money ++ - Add name attribute +/ - Add method to save player state so the game can be resumed with current + money amount maintained + + +* : The Dealer class will contain a single Hand object corresponding +to the cards it is dealt. It will also contain a rule set for deciding +whether to hit or stand for it's given hand value. + +To Do: ++ - Add class attribute ++ - Add basic decision rules +/ - Add optional rules based on user selected options + + ++ : The Hand class will contain a list of cards currently associated +with a Player or Dealer. It will have a method for determining and returning +the current value of itself and a method to receive new cards from the deck +and add them to the class's Card list. + +To Do: ++ - Add class attribute and initialization ++ - Add method to evaluate value of cards ++ - Add method to request change in the value of an ace if present in the hand ++ - Add method to receive Card objects and add them to the Hand's Card list + + +* : The GameOptions class will store all of the user selected +rules for gameplay. Attributes will include an integer value between 1 and +8 representing the number of decks to be used in the game, as well as a +series of Boolean values with default values that will activate or deactivate +certain rule variations. + +To Do: +* - Add basic list of class attributes, starting with number of decks +* - Add in common rule variations. No hit on soft 17 and Late/Early surrender +/ - Add in advanced rule variations + + +* : The Game class will be the backbone of the game. It will store +the wager amount made by the player, created and distribute Hand objects to +the Player and Dealer and will control game flow based on hand +values held by the player and dealer. It will have methods to calculate +payouts based off of results of the game. Payouts will be 2:1 for a standard +win and 3:2 on blackjack. The Game class will pass the payout information +for use by the Player class to update the Player's money total. ?May create +the Dealer object and retrieve the ruleset from GameOptions.? + +To Do: +* - Add list of Game attributes +* - Add method to distribute Hands to Player and Dealer +* - Add method to check the status of the game by resolving Hand values +* - Add method to distribute payouts to the player +? - Add method for determining available actions for the player (Maybe in Hand + Class?) +? - Add method to retrieve the ruleset from GameOptions and create the + Dealer based on currently selected rules. (Maybe in Blackjack class?) + + +* : The Blackjack class will be the driver script for the game. +It will control all of the program flow and interact with the Game and +Interface Classes to start new hands, request menu displays, request output +to the console from the Interface Class. This file will contain the +main method for the game, and will be the script executed to start the +game. + +To Do: +* - Plan overall program flow and determine best structure for this class +* - Add main method to initialize the game and start program flow + + +* : The Interface class will contain all of the ASCII text that +will be used for in-game card representations, menus and methods for +receiving input from the user. + +To Do: +* - Design main menu +* - Design options menu +* - Add method for updating options based on user input from options menu +* - Add method for displaying current hands for the dealer and player to + the console +* - Add method to display win/lose text after each hand +...more + + +/////: Very unlikely, but this class would replace the +Interface class to provide a graphical game experience. The interface +would allow for mouse input or keyboard input corresponding to available +menu options or avaiable actions within the game. The rest of the +functionality would be identical to interface. + +To Do: +* - The rest of the project diff --git a/blackjack/dealer.py b/blackjack/dealer.py new file mode 100644 index 0000000..f7ef4de --- /dev/null +++ b/blackjack/dealer.py @@ -0,0 +1,22 @@ +class Dealer: + """This is the Dealer class, it contains information about the Dealer's + hand and hand value. + + Responsibilities: + + * Stores Hand objects containing Card objects that the dealer controls + * Determines the dealer's action based off of it's hand value + + Interactions: + * Receives hand value information from the Hand class + * Provides feedback about Dealer actions to the Game class + """ + + def __init__(self): + self.hand = None + + def hit(self): + if self.hand.get_value() < 17: + return True + else: + return False diff --git a/blackjack/tests/test_dealer.py b/blackjack/tests/test_dealer.py new file mode 100644 index 0000000..75966cf --- /dev/null +++ b/blackjack/tests/test_dealer.py @@ -0,0 +1,20 @@ +from dealer import Dealer +from card import Card +from hand import Hand + + +def test_dealer_hit(): + hand = Hand([Card("6", "clubs"), Card("5", "hearts")]) + dealer = Dealer() + + dealer.hand = hand + + assert dealer.hit() == True + +def test_dealer_does_not_hit(): + hand = Hand([Card("10", "clubs"), Card("7", "hearts")]) + dealer = Dealer() + + dealer.hand = hand + + assert dealer.hit() == False diff --git a/blackjack/tests/test_player.py b/blackjack/tests/test_player.py index 28f27cc..3a8c19d 100644 --- a/blackjack/tests/test_player.py +++ b/blackjack/tests/test_player.py @@ -9,21 +9,21 @@ def test_player_creation(): def test_player_attributes(): player = Player("Alan") assert player.name == "Alan" - assert player.money == 1000 + assert player.money == 100 assert player.hands == [] def test_modify_money(): player = Player("Alan") - player.modify_money(1000) - assert player.money == 2000 - player.modify_money(-500) - assert player.money == (1500) + player.modify_money(100) + assert player.money == 200 + player.modify_money(-50) + assert player.money == (150) def test_modify_money_exception(): player = Player("Alan") try: - player.modify_money(-1001) + player.modify_money(-101) except ValueError: assert True From 2f62722213a2b0e7435fe0bac778cf2c55e43ace Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 14:22:23 -0500 Subject: [PATCH 10/50] Added Interface class with method to display the main menu and take user input selecting option from the menu --- blackjack/interface.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 blackjack/interface.py diff --git a/blackjack/interface.py b/blackjack/interface.py new file mode 100644 index 0000000..e54b8f2 --- /dev/null +++ b/blackjack/interface.py @@ -0,0 +1,33 @@ +class Interface: + + def main_menu(self): + title_text = ("\n" + ".------..------..------..------..------." + ".------..------..------..------.\n" + "|B.--. ||L.--. ||A.--. ||C.--. ||K.--. |" + "|J.--. ||A.--. ||C.--. ||K.--. |\n" + "| :(): || :/\: || (\/) || :/\: || :/\: |" + "| :(): || (\/) || :/\: || :/\: |\n" + "| ()() || (__) || :\/: || :\/: || :\/: |" + "| ()() || :\/: || :\/: || :\/: |\n" + "| '--'B|| '--'L|| '--'A|| '--'C|| '--'K|" + "| '--'J|| '--'A|| '--'C|| '--'K|\n" + "`------'`------'`------'`------'`------'" + "`------'`------'`------'`------'") + + info = ("Programmed by: Alan Grissett\n" + "The Iron Yard - Durham\n" + "January 2015\n") + + menu_text = ("<><><><><><><><><><><><><><><><><><><><><><>" + "<><><><><><><><><><><><><><") + + print("\n"*80, menu_text) + print(title_text) + print("\n", menu_text) + print(info) + print("Main Menu:") + print("♡ 1 - Play Blackjack!") + print("♧ 2 - Set Game Options") + print("♢ 3 - Quit") + print("-----------------------") From 0c20133b65851de852bdff9035a2ae0f3b259ac9 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 16:12:23 -0500 Subject: [PATCH 11/50] Added game options class. A container for keeping track of the games ruleset. --- blackjack/game_options.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 blackjack/game_options.py diff --git a/blackjack/game_options.py b/blackjack/game_options.py new file mode 100644 index 0000000..6fcc57e --- /dev/null +++ b/blackjack/game_options.py @@ -0,0 +1,28 @@ +class GameOptions: + """ The game options class is responsible for storing all of the rule + variations that the player wishes to use in the game. It contains + boolean variables corresponding to the available rulesets. It also + checks to ensure that contradictory rules are not chosen. + + Responsibilities: + * Maintains a list of rules selected by the player + + Collaborations: + * Rules are updated by the interface + * Rules are passed to the game during initialization + """ + + + def __init__(self): + self.number_of_decks = 1 + self.hit_on_soft_17 = False + self.early_surrender = False + self.resplitting = False + self.resplit_aces = False + self.hit_split_aces = False + self.split_by_rank = False + self.no_surrender = False + self.double_9_10_11 = False + self.double_9_10 = False + self.no_hole_card = False + self.no_hole_card_obo = False From b1a43ef9fd671324b81927486426bf2d9e850169 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 16:12:47 -0500 Subject: [PATCH 12/50] Updated the dealer to allow a hit on soft 17 rule to be used. --- blackjack/dealer.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/blackjack/dealer.py b/blackjack/dealer.py index f7ef4de..9039bbf 100644 --- a/blackjack/dealer.py +++ b/blackjack/dealer.py @@ -12,11 +12,20 @@ class Dealer: * Provides feedback about Dealer actions to the Game class """ - def __init__(self): + def __init__(self, hit_soft_17=False): self.hand = None + self.hit_soft_17 = hit_soft_17 def hit(self): - if self.hand.get_value() < 17: - return True + if not self.hit_soft_17: + if self.hand.get_value() < 17: + return True + else: + return False else: - return False + if self.hand.get_value() < 17: + return True + elif self.hand.get_value() == 17 and "A" in self.hand.get_ranks(): + return True + else: + return False From cf8eb77bbd4802c00527aa99dbe9741fa6cff217 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 16:13:20 -0500 Subject: [PATCH 13/50] Added tests to the dealer test file to ensure hit on soft 17 was functioning. --- blackjack/tests/test_dealer.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/blackjack/tests/test_dealer.py b/blackjack/tests/test_dealer.py index 75966cf..8bc2b18 100644 --- a/blackjack/tests/test_dealer.py +++ b/blackjack/tests/test_dealer.py @@ -18,3 +18,11 @@ def test_dealer_does_not_hit(): dealer.hand = hand assert dealer.hit() == False + +def test_dealer_hits_soft_17(): + hand = Hand([Card("6", "clubs"), Card("A", "hearts")]) + dealer = Dealer(hit_soft_17=True) + + dealer.hand = hand + + assert dealer.hit() == True From e9b0e6b1fa10f705e487b26f0bad6d802cc86967 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 16:14:34 -0500 Subject: [PATCH 14/50] Updated the README.md file To Do lists to reflect completed aspects of the game. --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 16df601..bef29d1 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,6 @@ To Do: objects created + - Add a method to remove a card from the card list and return the value + - Add a method to reshuffle the deck of cards -* - Add a randomly placed flag between the last 35-52 cards to indicate - the deck should be reshuffled when using more than one deck. +/ : The player class object will contain the amount of money the @@ -100,9 +98,9 @@ series of Boolean values with default values that will activate or deactivate certain rule variations. To Do: -* - Add basic list of class attributes, starting with number of decks -* - Add in common rule variations. No hit on soft 17 and Late/Early surrender -/ - Add in advanced rule variations ++ - Add basic list of class attributes, starting with number of decks ++ - Add in common rule variations. No hit on soft 17 and Late/Early surrender ++ - Add in advanced rule variations * : The Game class will be the backbone of the game. It will store @@ -142,7 +140,7 @@ will be used for in-game card representations, menus and methods for receiving input from the user. To Do: -* - Design main menu ++ - Design main menu * - Design options menu * - Add method for updating options based on user input from options menu * - Add method for displaying current hands for the dealer and player to From 1df8bdd59ba2ee459f7582f02455b98f005f6ac2 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 16:38:38 -0500 Subject: [PATCH 15/50] Updated variable name in game_options, coded __init__ and create_hands methods for the new game class. Added docstrings to some classes --- README.md | 2 +- blackjack/game.py | 39 ++++++++++++++++++++++++++++++++++++ blackjack/game_options.py | 2 +- blackjack/tests/test_game.py | 13 ++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 blackjack/game.py create mode 100644 blackjack/tests/test_game.py diff --git a/README.md b/README.md index bef29d1..144ff91 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ To Do: Dealer based on currently selected rules. (Maybe in Blackjack class?) -* : The Blackjack class will be the driver script for the game. +* : The Run class will be the driver script for the game. It will control all of the program flow and interact with the Game and Interface Classes to start new hands, request menu displays, request output to the console from the Interface Class. This file will contain the diff --git a/blackjack/game.py b/blackjack/game.py new file mode 100644 index 0000000..71aa3d2 --- /dev/null +++ b/blackjack/game.py @@ -0,0 +1,39 @@ +from deck import Deck +from hand import Hand +from player import Player +from dealer import Dealer + + +class Game: + """ This is the main game class. It provides an interface for interaction + between the deck, player and dealer. It receives rule options from the + game options class to properly initialize the dealer to play with the + selected ruleset and properly initialize the deck to have the selected + number of cards. + + Responsibilites: + * Initializes the deck for play + * Initializes the dealer + * Keeps track of the player's wager + * Resolves whether the dealer of player wins a hand + + Collaborators: + * Interacts with the deck class to generate a deck of the correct size + * Interacts with the GameOptions class to determine the ruleset for the + game and initialize the dealer appropriately + * Receives wager from player and transfers winnings to player""" + + + def __init__(self, options, name): + self.deck = Deck(options.number_of_decks) + self.player = Player(name) + self.dealer = Dealer(options.hit_soft_17) + + def create_hands(self): + dealer_cards = [] + player_cards = [] + for _ in range(2): + player_cards.append(self.deck.deal()) + dealer_cards.append(self.deck.deal()) + self.player.hands.append(Hand(player_cards)) + self.dealer.hand = Hand(dealer_cards) diff --git a/blackjack/game_options.py b/blackjack/game_options.py index 6fcc57e..10e982f 100644 --- a/blackjack/game_options.py +++ b/blackjack/game_options.py @@ -15,7 +15,7 @@ class GameOptions: def __init__(self): self.number_of_decks = 1 - self.hit_on_soft_17 = False + self.hit_soft_17 = False self.early_surrender = False self.resplitting = False self.resplit_aces = False diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py new file mode 100644 index 0000000..f01b606 --- /dev/null +++ b/blackjack/tests/test_game.py @@ -0,0 +1,13 @@ +from game_options import GameOptions +from game import Game +from hand import Hand + + +def test_create_hands(): + options = GameOptions() + new_game = Game(options, "Alan") + new_game.create_hands() + + assert len(new_game.player.hands[0].cards) == 2 + assert len(new_game.dealer.hand.cards) == 2 + assert len(new_game.deck.cards) == 48 From 879b44fb0fa08f808f7b9f7922d6e7c4cb5e66c7 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Thu, 22 Jan 2015 17:17:42 -0500 Subject: [PATCH 16/50] Added methods and tests into game to check if a player wins, busts or pushes. Added method to determine if a player can split based on the rules set out in game options. If split_by_rank is set to true player can only split cards with the same rank. --- blackjack/game.py | 31 +++++++++++++++++++++++++++++++ blackjack/game_options.py | 2 -- blackjack/tests/test_game.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/blackjack/game.py b/blackjack/game.py index 71aa3d2..558c4e6 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -28,7 +28,20 @@ def __init__(self, options, name): self.deck = Deck(options.number_of_decks) self.player = Player(name) self.dealer = Dealer(options.hit_soft_17) + self.options = options + def can_split(self, hand): + if self.options.split_by_rank: + if hand.get_ranks()[0] == hand.get_ranks()[1]: + return True + else: + return False + else: + if hand.get_values()[0] == hand.get_values()[1]: + return True + else: + return False + def create_hands(self): dealer_cards = [] player_cards = [] @@ -37,3 +50,21 @@ def create_hands(self): dealer_cards.append(self.deck.deal()) self.player.hands.append(Hand(player_cards)) self.dealer.hand = Hand(dealer_cards) + + def check_bust(self, hand): + if hand.get_value() > 21: + return True + else: + return False + + def check_push(self, hand1, hand2): + if hand1.get_value() == hand2.get_value(): + return True + else: + return False + + def compare_hands(self, player_hand, dealer_hand ): + if player_hand.get_value > dealer_hand.get_value: + return True + else: + return False diff --git a/blackjack/game_options.py b/blackjack/game_options.py index 10e982f..5097360 100644 --- a/blackjack/game_options.py +++ b/blackjack/game_options.py @@ -24,5 +24,3 @@ def __init__(self): self.no_surrender = False self.double_9_10_11 = False self.double_9_10 = False - self.no_hole_card = False - self.no_hole_card_obo = False diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index f01b606..e932372 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -1,3 +1,4 @@ +from card import Card from game_options import GameOptions from game import Game from hand import Hand @@ -11,3 +12,36 @@ def test_create_hands(): assert len(new_game.player.hands[0].cards) == 2 assert len(new_game.dealer.hand.cards) == 2 assert len(new_game.deck.cards) == 48 + +def test_bust(): + options = GameOptions() + new_game = Game(options, "Alan") + hand = Hand([Card("J", "hearts"), Card("Q", "diamonds")]) + assert not new_game.check_bust(hand) + hand.cards.append(Card("K", "spades")) + assert new_game.check_bust(hand) + +def test_push(): + options = GameOptions() + new_game = Game(options, "Alan") + hand = Hand([Card("J", "hearts"), Card("Q", "diamonds")]) + hand2 = Hand([Card("K", "hearts"), Card("J", "diamonds")]) + assert new_game.check_push(hand, hand2) + hand.cards.append(Card("A", "spades")) + assert not new_game.check_push(hand, hand2) + +def test_can_split_by_rank(): + options = GameOptions() + options.split_by_rank = True + new_game = Game(options, "Alan") + hand = Hand([Card("Q", "hearts"), Card("Q", "diamonds")]) + assert new_game.can_split(hand) + hand = Hand([Card("Q", "hearts"), Card("10", "diamonds")]) + assert not new_game.can_split(hand) + +def test_can_split_by_value(): + options = GameOptions() + options.split_by_rank = True + new_game = Game(options, "Alan") + hand = Hand([Card("Q", "hearts"), Card("10", "diamonds")]) + assert not new_game.can_split(hand) From c7d06f5a6a99f74a04a44803ddf4e44b728c61a6 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 12:24:49 -0500 Subject: [PATCH 17/50] Added tests and functionality for several of the game options. --- blackjack/card.py | 4 +-- blackjack/dealer.py | 5 +-- blackjack/game.py | 45 ++++++++++++------------ blackjack/game_options.py | 9 +++++ blackjack/hand.py | 6 ++-- blackjack/interface.py | 14 ++++++++ blackjack/tests/__init__.py | 0 blackjack/tests/test_dealer.py | 6 ++-- blackjack/tests/test_game.py | 62 ++++++++++++++++++++++++++++++---- blackjack/tests/test_hand.py | 24 ++++++++----- 10 files changed, 125 insertions(+), 50 deletions(-) create mode 100644 blackjack/tests/__init__.py diff --git a/blackjack/card.py b/blackjack/card.py index c16122c..c57d793 100644 --- a/blackjack/card.py +++ b/blackjack/card.py @@ -29,7 +29,7 @@ class Card: def __init__(self, rank, suit): self.rank = rank self.suit = suit - self.__evaluate_card_value__() + self._evaluate_card_value() def __str__(self): return self.rank + self.suits[self.suit] @@ -40,7 +40,7 @@ def __repr__(self): def __eq__(self, other): return self.rank == other.rank and self.suit == other.suit - def __evaluate_card_value__(self): + def _evaluate_card_value(self): if self.rank.isdigit(): self.value = int(self.rank) elif self.rank == "A": diff --git a/blackjack/dealer.py b/blackjack/dealer.py index 9039bbf..60ac9cf 100644 --- a/blackjack/dealer.py +++ b/blackjack/dealer.py @@ -18,10 +18,7 @@ def __init__(self, hit_soft_17=False): def hit(self): if not self.hit_soft_17: - if self.hand.get_value() < 17: - return True - else: - return False + return self.hand.get_value() < 17 else: if self.hand.get_value() < 17: return True diff --git a/blackjack/game.py b/blackjack/game.py index 558c4e6..7c40496 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -30,41 +30,38 @@ def __init__(self, options, name): self.dealer = Dealer(options.hit_soft_17) self.options = options + def can_double(self, hand): + if self.options.double_9_10_11: + return 9 <= hand.get_value() < 12 and len(hand.cards) == 2 + elif self.options.double_9_10: + return 9 <= hand.get_value() < 11 and len(hand.cards) == 2 + else: + return len(hand.cards) == 2 + def can_split(self, hand): if self.options.split_by_rank: - if hand.get_ranks()[0] == hand.get_ranks()[1]: - return True - else: - return False + return hand.get_ranks()[0] == hand.get_ranks()[1] else: - if hand.get_values()[0] == hand.get_values()[1]: - return True - else: - return False - - def create_hands(self): + return hand.get_values()[0] == hand.get_values()[1] + + def can_surrender(self, player_hand, dealer_show_card): + return (not options.no_surrender and (len(player_hand.cards) == 2 + and dealer.show_card.rank == "A")) + + def create_hands(self, bet): dealer_cards = [] player_cards = [] for _ in range(2): player_cards.append(self.deck.deal()) dealer_cards.append(self.deck.deal()) - self.player.hands.append(Hand(player_cards)) - self.dealer.hand = Hand(dealer_cards) + self.player.hands.append(Hand(bet, player_cards)) + self.dealer.hand = Hand(0, dealer_cards) def check_bust(self, hand): - if hand.get_value() > 21: - return True - else: - return False + return hand.get_value() > 21 def check_push(self, hand1, hand2): - if hand1.get_value() == hand2.get_value(): - return True - else: - return False + return hand1.get_value() == hand2.get_value() def compare_hands(self, player_hand, dealer_hand ): - if player_hand.get_value > dealer_hand.get_value: - return True - else: - return False + return player_hand.get_value > dealer_hand.get_value diff --git a/blackjack/game_options.py b/blackjack/game_options.py index 5097360..ea864e2 100644 --- a/blackjack/game_options.py +++ b/blackjack/game_options.py @@ -22,5 +22,14 @@ def __init__(self): self.hit_split_aces = False self.split_by_rank = False self.no_surrender = False + self.no_double_after_split = False self.double_9_10_11 = False self.double_9_10 = False + + +# Rules to implement: +# Early Surrender - NOT DONE +# Resplitting - NOT DONE +# Resplitting Aces - NOT DONE +# Hit Split Aces - NOT DONE +# No Double After Split - NOT DONE diff --git a/blackjack/hand.py b/blackjack/hand.py index 4b339ab..7a06ad3 100644 --- a/blackjack/hand.py +++ b/blackjack/hand.py @@ -16,8 +16,9 @@ class Hand: """ - def __init__(self, cards=[]): + def __init__(self, bet, cards=[]): self.cards = cards + self.bet = bet def add_cards(self, card): self.cards.append(card) @@ -34,7 +35,8 @@ def get_value(self): hand_value = 0 for card in self.cards: hand_value += card.value - + if hand_value == 21 and len(self.cards) == 2: + return "BLACKJACK" if ((hand_value > 21 and "A" in self.get_ranks()) or hand_value <= 11 and "A" in self.get_ranks()): for card in self.cards: diff --git a/blackjack/interface.py b/blackjack/interface.py index e54b8f2..77278b1 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -1,6 +1,17 @@ class Interface: + """ Interface class will provide all of the methods of interacting with + the user. It will contain all of the methods for displaying to the + console as well as receiving and checking input from the player. + + Responsibilities: + * Outputting game information and menus to the player + * Receiving input from the player + """ + def main_menu(self): + """ Displays the title, programmer info, and main menu for the game. + Takes user input and returns to the calling Game class""" title_text = ("\n" ".------..------..------..------..------." ".------..------..------..------.\n" @@ -31,3 +42,6 @@ def main_menu(self): print("♧ 2 - Set Game Options") print("♢ 3 - Quit") print("-----------------------") + selection = input("♤ ") + + return selection diff --git a/blackjack/tests/__init__.py b/blackjack/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/blackjack/tests/test_dealer.py b/blackjack/tests/test_dealer.py index 8bc2b18..4f643a2 100644 --- a/blackjack/tests/test_dealer.py +++ b/blackjack/tests/test_dealer.py @@ -4,7 +4,7 @@ def test_dealer_hit(): - hand = Hand([Card("6", "clubs"), Card("5", "hearts")]) + hand = Hand(0, [Card("6", "clubs"), Card("5", "hearts")]) dealer = Dealer() dealer.hand = hand @@ -12,7 +12,7 @@ def test_dealer_hit(): assert dealer.hit() == True def test_dealer_does_not_hit(): - hand = Hand([Card("10", "clubs"), Card("7", "hearts")]) + hand = Hand(0, [Card("10", "clubs"), Card("7", "hearts")]) dealer = Dealer() dealer.hand = hand @@ -20,7 +20,7 @@ def test_dealer_does_not_hit(): assert dealer.hit() == False def test_dealer_hits_soft_17(): - hand = Hand([Card("6", "clubs"), Card("A", "hearts")]) + hand = Hand(0, [Card("6", "clubs"), Card("A", "hearts")]) dealer = Dealer(hit_soft_17=True) dealer.hand = hand diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index e932372..3a1b128 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -7,41 +7,89 @@ def test_create_hands(): options = GameOptions() new_game = Game(options, "Alan") - new_game.create_hands() + new_game.create_hands(10) assert len(new_game.player.hands[0].cards) == 2 assert len(new_game.dealer.hand.cards) == 2 assert len(new_game.deck.cards) == 48 + assert new_game.player.hands[0].bet == 10 + def test_bust(): options = GameOptions() new_game = Game(options, "Alan") - hand = Hand([Card("J", "hearts"), Card("Q", "diamonds")]) + hand = Hand(10, [Card("J", "hearts"), Card("Q", "diamonds")]) assert not new_game.check_bust(hand) hand.cards.append(Card("K", "spades")) assert new_game.check_bust(hand) + def test_push(): options = GameOptions() new_game = Game(options, "Alan") - hand = Hand([Card("J", "hearts"), Card("Q", "diamonds")]) - hand2 = Hand([Card("K", "hearts"), Card("J", "diamonds")]) + hand = Hand(10, [Card("J", "hearts"), Card("Q", "diamonds")]) + hand2 = Hand(10, [Card("K", "hearts"), Card("J", "diamonds")]) assert new_game.check_push(hand, hand2) hand.cards.append(Card("A", "spades")) assert not new_game.check_push(hand, hand2) + def test_can_split_by_rank(): options = GameOptions() options.split_by_rank = True new_game = Game(options, "Alan") - hand = Hand([Card("Q", "hearts"), Card("Q", "diamonds")]) + hand = Hand(10, [Card("Q", "hearts"), Card("Q", "diamonds")]) assert new_game.can_split(hand) - hand = Hand([Card("Q", "hearts"), Card("10", "diamonds")]) + hand = Hand(10, [Card("Q", "hearts"), Card("10", "diamonds")]) assert not new_game.can_split(hand) + def test_can_split_by_value(): options = GameOptions() options.split_by_rank = True new_game = Game(options, "Alan") - hand = Hand([Card("Q", "hearts"), Card("10", "diamonds")]) + hand = Hand(10, [Card("Q", "hearts"), Card("10", "diamonds")]) assert not new_game.can_split(hand) + + +def test_can_double_normal(): + options = GameOptions() + options.split_by_rank = True + new_game = Game(options, "Alan") + hand = Hand(10, [Card("6", "clubs"), Card("8", "hearts")]) + assert new_game.can_double(hand) + hand.cards.append(Card("3", "diamonds")) + assert not new_game.can_double(hand) + +def test_can_double_9_10_11(): + options = GameOptions() + options.double_9_10_11 = True + new_game = Game(options, "Alan") + hand = Hand(10, [Card("6", "clubs"), Card("3", "hearts")]) + assert new_game.can_double(hand) + hand = Hand(10, [Card("6", "clubs"), Card("4", "hearts")]) + assert new_game.can_double(hand) + hand = Hand(10, [Card("6", "clubs"), Card("5", "hearts")]) + assert new_game.can_double(hand) + hand = Hand(10, [Card("6", "clubs"), Card("8", "hearts")]) + assert not new_game.can_double(hand) + + +def test_can_double_9_10(): + options = GameOptions() + options.double_9_10 = True + new_game = Game(options, "Alan") + hand = Hand(10, [Card("6", "clubs"), Card("3", "hearts")]) + assert new_game.can_double(hand) + hand = Hand(10, [Card("6", "clubs"), Card("4", "hearts")]) + assert new_game.can_double(hand) + hand = Hand(10, [Card("6", "clubs"), Card("5", "hearts")]) + assert not new_game.can_double(hand) + hand = Hand(10, [Card("6", "clubs"), Card("8", "hearts")]) + assert not new_game.can_double(hand) + +def test_can_surrender(): + options = GameOptions() + new_game = Game(options, "Alan") + hand1 = Hand(10, [Card("6", "clubs"), Card("10", "hearts")]) + diff --git a/blackjack/tests/test_hand.py b/blackjack/tests/test_hand.py index fbd34de..810fdec 100644 --- a/blackjack/tests/test_hand.py +++ b/blackjack/tests/test_hand.py @@ -7,7 +7,7 @@ def test_card_list_holds_cards(): test_cards.append(Card("10", "diamonds")) test_cards.append(Card("J", "clubs")) - hand = Hand(test_cards) + hand = Hand(10, test_cards) assert hand.cards @@ -16,7 +16,7 @@ def test_get_hand_value(): test_cards.append(Card("10", "diamonds")) test_cards.append(Card("J", "clubs")) - hand = Hand(test_cards) + hand = Hand(10, test_cards) assert hand.get_value() == 20 @@ -25,7 +25,7 @@ def test_get_ranks(): test_cards.append(Card("10", "diamonds")) test_cards.append(Card("J", "clubs")) - hand = Hand(test_cards) + hand = Hand(10, test_cards) assert hand.get_ranks() == ["10", "J"] @@ -35,7 +35,7 @@ def test_ace_detection_and_swap(): test_cards.append(Card("J", "clubs")) test_cards.append(Card("A", "hearts")) - hand = Hand(test_cards) + hand = Hand(10, test_cards) assert hand.get_value() == 21 @@ -44,7 +44,7 @@ def test_two_aces(): test_cards.append(Card("A", "diamonds")) test_cards.append(Card("A", "hearts")) - hand = Hand(test_cards) + hand = Hand(10, test_cards) assert hand.get_value() == 12 @@ -52,8 +52,16 @@ def test_two_aces(): def test_ace_value_incorrect(): test_cards = [] test_cards.append(Card("A", "spades")) - test_cards.append(Card("10", "clubs")) + test_cards.append(Card("9", "clubs")) test_cards[0].swap_ace() - hand = Hand(test_cards) + hand = Hand(10, test_cards) - assert hand.get_value() == 21 + assert hand.get_value() == 20 + +def test_blackjack(): + test_cards = [] + test_cards.append(Card("A", "spades")) + test_cards.append(Card("10", "clubs")) + hand = Hand(10, test_cards) + + assert hand.get_value() == "BLACKJACK" From c752871d2eb6c9fd08d1499540edb4494ce18fab Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 12:53:46 -0500 Subject: [PATCH 18/50] Added surrender and no surrender checks and tests. --- blackjack/game.py | 20 ++++++++++++++++---- blackjack/tests/test_game.py | 7 ++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/blackjack/game.py b/blackjack/game.py index 7c40496..44f2a92 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -31,6 +31,8 @@ def __init__(self, options, name): self.options = options def can_double(self, hand): + """Checks to see if the option to double down is abailable to the + player""" if self.options.double_9_10_11: return 9 <= hand.get_value() < 12 and len(hand.cards) == 2 elif self.options.double_9_10: @@ -39,16 +41,22 @@ def can_double(self, hand): return len(hand.cards) == 2 def can_split(self, hand): + """Checks to see if the option to split is available to the player""" if self.options.split_by_rank: return hand.get_ranks()[0] == hand.get_ranks()[1] else: return hand.get_values()[0] == hand.get_values()[1] def can_surrender(self, player_hand, dealer_show_card): - return (not options.no_surrender and (len(player_hand.cards) == 2 - and dealer.show_card.rank == "A")) + """Checks to see if the options to surrender is available to the + player""" + return (not self.options.no_surrender + and len(player_hand.cards) == 2 + and dealer_show_card.rank == "A") def create_hands(self, bet): + """Creates the player and dealer hands and assigns them to their + respective objects""" dealer_cards = [] player_cards = [] for _ in range(2): @@ -58,10 +66,14 @@ def create_hands(self, bet): self.dealer.hand = Hand(0, dealer_cards) def check_bust(self, hand): + """Checks to see if a hand has busted""" return hand.get_value() > 21 - def check_push(self, hand1, hand2): - return hand1.get_value() == hand2.get_value() + def check_push(self, player_hand, dealer_hand): + """Compares a player and dealer hand and to see if a set of hands + results in a push state""" + return player_hand.get_value() == dealer_hand.get_value() def compare_hands(self, player_hand, dealer_hand ): + """Compares the player and dealer hand to resolve the winner""" return player_hand.get_value > dealer_hand.get_value diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 3a1b128..7d31b0a 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -92,4 +92,9 @@ def test_can_surrender(): options = GameOptions() new_game = Game(options, "Alan") hand1 = Hand(10, [Card("6", "clubs"), Card("10", "hearts")]) - + hand2 = Hand(10, [Card("7", "hearts"), Card("A", "spades")]) + assert new_game.can_surrender(hand1, hand2.cards[1]) + assert not new_game.can_surrender(hand1, hand2.cards[0]) + new_game.options.no_surrender = True + assert not new_game.can_surrender(hand1, hand2.cards[1]) + assert not new_game.can_surrender(hand1, hand2.cards[0]) From 6cc5df85780d2dbf4f7f9e610ac4168f9223e46c Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 14:23:41 -0500 Subject: [PATCH 19/50] Added no hit split aces functionality and tests --- blackjack/card.py | 2 +- blackjack/game.py | 19 ++++++++++++++-- blackjack/game_options.py | 5 +---- blackjack/tests/test_game.py | 43 ++++++++++++++++++++++++++++++++++-- 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/blackjack/card.py b/blackjack/card.py index c57d793..5559dcb 100644 --- a/blackjack/card.py +++ b/blackjack/card.py @@ -38,7 +38,7 @@ def __repr__(self): return self.__str__() def __eq__(self, other): - return self.rank == other.rank and self.suit == other.suit + return self.value == other.value def _evaluate_card_value(self): if self.rank.isdigit(): diff --git a/blackjack/game.py b/blackjack/game.py index 44f2a92..6b548ec 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -40,12 +40,27 @@ def can_double(self, hand): else: return len(hand.cards) == 2 + def can_hit(self, hand): + """Checks to see if the player has the option to hit. Only not + available if hitting split aces is disabled and player has split + aces""" + if not self.options.hit_split_aces and (len(self.player.hands) > 1 + and hand.cards[0].rank == "A"): + return False + else: + return True + def can_split(self, hand): """Checks to see if the option to split is available to the player""" - if self.options.split_by_rank: + if not self.options.resplitting and len(self.player.hands) > 1: + return False + elif not self.options.resplit_aces and (len(self.player.hands) > 1 + and "A" in hand.get_ranks()): + return False + elif self.options.split_by_rank: return hand.get_ranks()[0] == hand.get_ranks()[1] else: - return hand.get_values()[0] == hand.get_values()[1] + return hand.cards[0] == hand.cards[1] def can_surrender(self, player_hand, dealer_show_card): """Checks to see if the options to surrender is available to the diff --git a/blackjack/game_options.py b/blackjack/game_options.py index ea864e2..1026f89 100644 --- a/blackjack/game_options.py +++ b/blackjack/game_options.py @@ -17,7 +17,7 @@ def __init__(self): self.number_of_decks = 1 self.hit_soft_17 = False self.early_surrender = False - self.resplitting = False + self.resplitting = True self.resplit_aces = False self.hit_split_aces = False self.split_by_rank = False @@ -29,7 +29,4 @@ def __init__(self): # Rules to implement: # Early Surrender - NOT DONE -# Resplitting - NOT DONE -# Resplitting Aces - NOT DONE -# Hit Split Aces - NOT DONE # No Double After Split - NOT DONE diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 7d31b0a..5d4532f 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -34,6 +34,33 @@ def test_push(): assert not new_game.check_push(hand, hand2) +def test_can_split_resplitting(): + options = GameOptions() + new_game = Game(options, "Alan") + hand = Hand(10, [Card("J", "hearts"), Card("Q", "diamonds")]) + new_game.player.hands.append(hand) + assert new_game.can_split(hand) + new_game.player.hands.append(hand) + assert new_game.can_split(hand) + new_game.options.resplitting = False + assert not new_game.can_split(hand) + new_game.player.hands.pop() + assert new_game.can_split(hand) + + +def test_resplitting_aces(): + options = GameOptions() + new_game = Game(options, "Alan") + hand = Hand(10, [Card("A", "hearts"), Card("A", "diamonds")]) + new_game.player.hands.append(hand) + assert new_game.can_split(hand) + new_game.player.hands.append(hand) + print(len(new_game.player.hands)) + assert not new_game.can_split(hand) + new_game.options.resplit_aces = True + assert new_game.can_split(hand) + + def test_can_split_by_rank(): options = GameOptions() options.split_by_rank = True @@ -46,10 +73,9 @@ def test_can_split_by_rank(): def test_can_split_by_value(): options = GameOptions() - options.split_by_rank = True new_game = Game(options, "Alan") hand = Hand(10, [Card("Q", "hearts"), Card("10", "diamonds")]) - assert not new_game.can_split(hand) + assert new_game.can_split(hand) def test_can_double_normal(): @@ -88,6 +114,7 @@ def test_can_double_9_10(): hand = Hand(10, [Card("6", "clubs"), Card("8", "hearts")]) assert not new_game.can_double(hand) + def test_can_surrender(): options = GameOptions() new_game = Game(options, "Alan") @@ -98,3 +125,15 @@ def test_can_surrender(): new_game.options.no_surrender = True assert not new_game.can_surrender(hand1, hand2.cards[1]) assert not new_game.can_surrender(hand1, hand2.cards[0]) + + +def test_hit_split_aces(): + options = GameOptions() + new_game = Game(options, "Alan") + hand1 = Hand(10, [Card("A", "spades"), Card("6", "hearts")]) + hand2 = Hand(10, [Card("A", "hearts"), Card("7", "spades")]) + new_game.player.hands.append(hand1) + new_game.player.hands.append(hand2) + assert not new_game.can_hit(hand1) + new_game.player.hands.pop() + assert new_game.can_hit(hand1) From 7c05be2c80789d08a7f94dcfb2b89ed12aa494c4 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 14:57:07 -0500 Subject: [PATCH 20/50] Added no hit after double rules and tests. --- blackjack/game.py | 13 ++++++++----- blackjack/tests/test_game.py | 11 +++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/blackjack/game.py b/blackjack/game.py index 6b548ec..dd435a8 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -33,12 +33,15 @@ def __init__(self, options, name): def can_double(self, hand): """Checks to see if the option to double down is abailable to the player""" - if self.options.double_9_10_11: - return 9 <= hand.get_value() < 12 and len(hand.cards) == 2 - elif self.options.double_9_10: - return 9 <= hand.get_value() < 11 and len(hand.cards) == 2 + if self.options.no_double_after_split and len(self.player.hands) > 1: + return False else: - return len(hand.cards) == 2 + if self.options.double_9_10_11: + return 9 <= hand.get_value() < 12 and len(hand.cards) == 2 + elif self.options.double_9_10: + return 9 <= hand.get_value() < 11 and len(hand.cards) == 2 + else: + return len(hand.cards) == 2 def can_hit(self, hand): """Checks to see if the player has the option to hit. Only not diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 5d4532f..e093c7e 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -137,3 +137,14 @@ def test_hit_split_aces(): assert not new_game.can_hit(hand1) new_game.player.hands.pop() assert new_game.can_hit(hand1) + +def test_no_double_after_split(): + options = GameOptions() + new_game = Game(options, "Alan") + hand1 = Hand(10, [Card("A", "spades"), Card("6", "hearts")]) + hand2 = Hand(10, [Card("A", "hearts"), Card("7", "spades")]) + new_game.player.hands.append(hand1) + new_game.player.hands.append(hand2) + assert new_game.can_double(hand1) + new_game.options.no_double_after_split = True + assert not new_game.can_double(hand1) From 77d79fa949ce805c7b1ce81cf248295ec097cfa2 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 15:44:55 -0500 Subject: [PATCH 21/50] Added no hit split aces --- README.md | 13 ++++++------- blackjack/game.py | 10 ++++++++++ blackjack/game_options.py | 1 - blackjack/tests/test_game.py | 7 +++++++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 144ff91..d2ba9c5 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ whether to hit or stand for it's given hand value. To Do: + - Add class attribute + - Add basic decision rules -/ - Add optional rules based on user selected options ++ - Add optional rules based on user selected options + : The Hand class will contain a list of cards currently associated @@ -113,13 +113,12 @@ for use by the Player class to update the Player's money total. ?May create the Dealer object and retrieve the ruleset from GameOptions.? To Do: -* - Add list of Game attributes -* - Add method to distribute Hands to Player and Dealer -* - Add method to check the status of the game by resolving Hand values ++ - Add list of Game attributes ++ - Add method to distribute Hands to Player and Dealer ++ - Add method to check the status of the game by resolving Hand values * - Add method to distribute payouts to the player -? - Add method for determining available actions for the player (Maybe in Hand - Class?) -? - Add method to retrieve the ruleset from GameOptions and create the ++ - Add method for determining available actions for the player ++ - Add method to retrieve the ruleset from GameOptions and create the Dealer based on currently selected rules. (Maybe in Blackjack class?) diff --git a/blackjack/game.py b/blackjack/game.py index dd435a8..a4e41af 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -95,3 +95,13 @@ def check_push(self, player_hand, dealer_hand): def compare_hands(self, player_hand, dealer_hand ): """Compares the player and dealer hand to resolve the winner""" return player_hand.get_value > dealer_hand.get_value + + def takes_hit(self, hand): + hand.add_cards(self.deck.deal()) + + def splits(self, hand): + player.modify_money(-1 * hand.bet) + new_hand = Hand(hand.bet, [hand[1], self.deck.deal()]) + hand.pop(1) + hand.add_cards(self.deck.deal()) + self.player.hands.append(new_hand) diff --git a/blackjack/game_options.py b/blackjack/game_options.py index 1026f89..d07b9f1 100644 --- a/blackjack/game_options.py +++ b/blackjack/game_options.py @@ -29,4 +29,3 @@ def __init__(self): # Rules to implement: # Early Surrender - NOT DONE -# No Double After Split - NOT DONE diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index e093c7e..d036162 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -148,3 +148,10 @@ def test_no_double_after_split(): assert new_game.can_double(hand1) new_game.options.no_double_after_split = True assert not new_game.can_double(hand1) + +def test_hit(): + options = GameOptions() + new_game = Game(options, "Alan") + hand = Hand(10, [Card("A", "spades"), Card("6", "hearts")]) + new_game.takes_hit(hand) + assert len(hand.cards) == 3 From b99e4e9aa09e89ab11711e02eab4585bea3fed12 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 15:56:39 -0500 Subject: [PATCH 22/50] Added split functionality and test --- blackjack/game.py | 6 +++--- blackjack/tests/test_game.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/blackjack/game.py b/blackjack/game.py index a4e41af..401355d 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -100,8 +100,8 @@ def takes_hit(self, hand): hand.add_cards(self.deck.deal()) def splits(self, hand): - player.modify_money(-1 * hand.bet) - new_hand = Hand(hand.bet, [hand[1], self.deck.deal()]) - hand.pop(1) + self.player.modify_money(-1 * hand.bet) + new_hand = Hand(hand.bet, [hand.cards[1], self.deck.deal()]) + hand.cards.pop(1) hand.add_cards(self.deck.deal()) self.player.hands.append(new_hand) diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index d036162..7b10edd 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -138,6 +138,7 @@ def test_hit_split_aces(): new_game.player.hands.pop() assert new_game.can_hit(hand1) + def test_no_double_after_split(): options = GameOptions() new_game = Game(options, "Alan") @@ -149,9 +150,23 @@ def test_no_double_after_split(): new_game.options.no_double_after_split = True assert not new_game.can_double(hand1) + def test_hit(): options = GameOptions() new_game = Game(options, "Alan") hand = Hand(10, [Card("A", "spades"), Card("6", "hearts")]) new_game.takes_hit(hand) assert len(hand.cards) == 3 + + +def test_split(): + options = GameOptions() + new_game = Game(options, "Alan") + hand = Hand(10, [Card("J", "spades"), Card("J", "clubs")]) + new_game.player.hands.append(hand) + new_game.splits(hand) + assert len(new_game.player.hands) == 2 + assert len(hand.cards) == 2 + assert hand.cards[0].rank == "J" + assert new_game.player.hands[1].cards[0].rank == "J" + assert len(new_game.player.hands[1].cards) == 2 From c842d2bf200bfec3ce83601cfb23d04b0ef02464 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 16:00:41 -0500 Subject: [PATCH 23/50] Added double down functionality and test --- blackjack/game.py | 5 +++++ blackjack/tests/test_game.py | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/blackjack/game.py b/blackjack/game.py index 401355d..8b20a57 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -105,3 +105,8 @@ def splits(self, hand): hand.cards.pop(1) hand.add_cards(self.deck.deal()) self.player.hands.append(new_hand) + + def doubles(self, hand): + self.player.modify_money(-1 * hand.bet) + hand.bet *= 2 + self.takes_hit(hand) diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 7b10edd..326cd7e 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -170,3 +170,12 @@ def test_split(): assert hand.cards[0].rank == "J" assert new_game.player.hands[1].cards[0].rank == "J" assert len(new_game.player.hands[1].cards) == 2 + +def test_double_down(): + options = GameOptions() + new_game = Game(options, "Alan") + hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) + new_game.doubles(hand) + assert len(hand.cards) == 3 + assert hand.bet == 20 + assert new_game.player.money == 90 From 732dbe106b9d5f2634c0488ec01634f9c9a580d5 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 16:07:26 -0500 Subject: [PATCH 24/50] Added double down functionality and test --- blackjack/game.py | 15 --------------- blackjack/player.py | 19 +++++++++++++++++++ blackjack/tests/test_game.py | 12 +++++++++--- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/blackjack/game.py b/blackjack/game.py index 8b20a57..dd435a8 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -95,18 +95,3 @@ def check_push(self, player_hand, dealer_hand): def compare_hands(self, player_hand, dealer_hand ): """Compares the player and dealer hand to resolve the winner""" return player_hand.get_value > dealer_hand.get_value - - def takes_hit(self, hand): - hand.add_cards(self.deck.deal()) - - def splits(self, hand): - self.player.modify_money(-1 * hand.bet) - new_hand = Hand(hand.bet, [hand.cards[1], self.deck.deal()]) - hand.cards.pop(1) - hand.add_cards(self.deck.deal()) - self.player.hands.append(new_hand) - - def doubles(self, hand): - self.player.modify_money(-1 * hand.bet) - hand.bet *= 2 - self.takes_hit(hand) diff --git a/blackjack/player.py b/blackjack/player.py index 7fc520c..50a6602 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -22,3 +22,22 @@ def modify_money(self, value): self.money += value else: raise ValueError("Player money cannot be negative.") + + def takes_hit(self, hand): + hand.add_cards(self.deck.deal()) + + def splits(self, hand): + self.modify_money(-1 * hand.bet) + new_hand = Hand(hand.bet, [hand.cards[1], self.deck.deal()]) + hand.cards.pop(1) + hand.add_cards(self.deck.deal()) + self.hands.append(new_hand) + + def doubles(self, hand): + self.modify_money(-1 * hand.bet) + hand.bet *= 2 + self.takes_hit(hand) + + def surrenders(self, hand): + self.modify_money(.5 * hand.bet) + hand.bet *= .5 diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 326cd7e..1593c45 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -155,7 +155,7 @@ def test_hit(): options = GameOptions() new_game = Game(options, "Alan") hand = Hand(10, [Card("A", "spades"), Card("6", "hearts")]) - new_game.takes_hit(hand) + new_game.player.takes_hit(hand) assert len(hand.cards) == 3 @@ -164,7 +164,7 @@ def test_split(): new_game = Game(options, "Alan") hand = Hand(10, [Card("J", "spades"), Card("J", "clubs")]) new_game.player.hands.append(hand) - new_game.splits(hand) + new_game.player.splits(hand) assert len(new_game.player.hands) == 2 assert len(hand.cards) == 2 assert hand.cards[0].rank == "J" @@ -175,7 +175,13 @@ def test_double_down(): options = GameOptions() new_game = Game(options, "Alan") hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) - new_game.doubles(hand) + new_game.player.doubles(hand) assert len(hand.cards) == 3 assert hand.bet == 20 assert new_game.player.money == 90 + +def test_surrender(): + options = GameOptions() + new_game = Game(options, "Alan") + hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) + options.new_game.player.surrenders() From a1c0cf974576476510b551d77ffdf83a4812d696 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 16:18:24 -0500 Subject: [PATCH 25/50] Moved some functionality for player moves into player class from game class. Also added takes_hit function to dealer to update dealer hand. Updated test files to reflect changes. --- blackjack/dealer.py | 3 +++ blackjack/player.py | 15 ++++++++------- blackjack/tests/test_dealer.py | 8 ++++++++ blackjack/tests/test_game.py | 8 ++++---- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/blackjack/dealer.py b/blackjack/dealer.py index 60ac9cf..7467198 100644 --- a/blackjack/dealer.py +++ b/blackjack/dealer.py @@ -26,3 +26,6 @@ def hit(self): return True else: return False + + def takes_hit(self, card): + self.hand.cards.append(card) diff --git a/blackjack/player.py b/blackjack/player.py index 50a6602..a5cd430 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -1,3 +1,4 @@ +from hand import Hand class Player: """This is the player class, it contains a list of hands controlled by the player and keeps track of how much money the player has won. @@ -23,20 +24,20 @@ def modify_money(self, value): else: raise ValueError("Player money cannot be negative.") - def takes_hit(self, hand): - hand.add_cards(self.deck.deal()) + def takes_hit(self, hand, card): + hand.add_cards(card) - def splits(self, hand): + def splits(self, hand, cards): self.modify_money(-1 * hand.bet) - new_hand = Hand(hand.bet, [hand.cards[1], self.deck.deal()]) + new_hand = Hand(hand.bet, [hand.cards[1], cards[0]]) hand.cards.pop(1) - hand.add_cards(self.deck.deal()) + hand.add_cards(cards[1]) self.hands.append(new_hand) - def doubles(self, hand): + def doubles(self, hand, card): self.modify_money(-1 * hand.bet) hand.bet *= 2 - self.takes_hit(hand) + self.takes_hit(hand, card) def surrenders(self, hand): self.modify_money(.5 * hand.bet) diff --git a/blackjack/tests/test_dealer.py b/blackjack/tests/test_dealer.py index 4f643a2..8f157c1 100644 --- a/blackjack/tests/test_dealer.py +++ b/blackjack/tests/test_dealer.py @@ -1,6 +1,8 @@ from dealer import Dealer from card import Card from hand import Hand +from game_options import GameOptions +from game import Game def test_dealer_hit(): @@ -26,3 +28,9 @@ def test_dealer_hits_soft_17(): dealer.hand = hand assert dealer.hit() == True + +def test_dealer_takes_hit(): + options = GameOptions() + new_game = Game(options, "Alan") + new_game.dealer.hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) + new_game.dealer.takes_hit(new_game.deck.deal()) diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 1593c45..21cd2d2 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -155,7 +155,7 @@ def test_hit(): options = GameOptions() new_game = Game(options, "Alan") hand = Hand(10, [Card("A", "spades"), Card("6", "hearts")]) - new_game.player.takes_hit(hand) + new_game.player.takes_hit(hand, new_game.deck.deal()) assert len(hand.cards) == 3 @@ -164,7 +164,7 @@ def test_split(): new_game = Game(options, "Alan") hand = Hand(10, [Card("J", "spades"), Card("J", "clubs")]) new_game.player.hands.append(hand) - new_game.player.splits(hand) + new_game.player.splits(hand, [new_game.deck.deal(), new_game.deck.deal()]) assert len(new_game.player.hands) == 2 assert len(hand.cards) == 2 assert hand.cards[0].rank == "J" @@ -175,7 +175,7 @@ def test_double_down(): options = GameOptions() new_game = Game(options, "Alan") hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) - new_game.player.doubles(hand) + new_game.player.doubles(hand, new_game.deck.deal()) assert len(hand.cards) == 3 assert hand.bet == 20 assert new_game.player.money == 90 @@ -184,4 +184,4 @@ def test_surrender(): options = GameOptions() new_game = Game(options, "Alan") hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) - options.new_game.player.surrenders() + new_game.player.surrenders(hand) From 32f5eeecc0cae72ca10c07419811ebb6d2d73792 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Fri, 23 Jan 2015 19:04:50 -0500 Subject: [PATCH 26/50] Added to options menu to the interface file, also added a method to modify the gameoptions object to reflect selections made in the options menu. --- blackjack/interface.py | 128 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 13 deletions(-) diff --git a/blackjack/interface.py b/blackjack/interface.py index 77278b1..9b44cb3 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -1,3 +1,6 @@ +from game_options import GameOptions + + class Interface: """ Interface class will provide all of the methods of interacting with the user. It will contain all of the methods for displaying to the @@ -7,24 +10,23 @@ class Interface: * Outputting game information and menus to the player * Receiving input from the player """ - def main_menu(self): """ Displays the title, programmer info, and main menu for the game. Takes user input and returns to the calling Game class""" title_text = ("\n" - ".------..------..------..------..------." - ".------..------..------..------.\n" - "|B.--. ||L.--. ||A.--. ||C.--. ||K.--. |" - "|J.--. ||A.--. ||C.--. ||K.--. |\n" - "| :(): || :/\: || (\/) || :/\: || :/\: |" - "| :(): || (\/) || :/\: || :/\: |\n" - "| ()() || (__) || :\/: || :\/: || :\/: |" - "| ()() || :\/: || :\/: || :\/: |\n" - "| '--'B|| '--'L|| '--'A|| '--'C|| '--'K|" - "| '--'J|| '--'A|| '--'C|| '--'K|\n" - "`------'`------'`------'`------'`------'" - "`------'`------'`------'`------'") + ".------..------..------..------..------." + ".------..------..------..------.\n" + "|B.--. ||L.--. ||A.--. ||C.--. ||K.--. |" + "|J.--. ||A.--. ||C.--. ||K.--. |\n" + "| :(): || :/\: || (\/) || :/\: || :/\: |" + "| :(): || (\/) || :/\: || :/\: |\n" + "| ()() || (__) || :\/: || :\/: || :\/: |" + "| ()() || :\/: || :\/: || :\/: |\n" + "| '--'B|| '--'L|| '--'A|| '--'C|| '--'K|" + "| '--'J|| '--'A|| '--'C|| '--'K|\n" + "`------'`------'`------'`------'`------'" + "`------'`------'`------'`------'") info = ("Programmed by: Alan Grissett\n" "The Iron Yard - Durham\n" @@ -45,3 +47,103 @@ def main_menu(self): selection = input("♤ ") return selection + + def get_name(self): + """Prompts the user to input a name, used for the creation of a + Player object.""" + print("\n"*80) + print("Great! Let's play some Blackjack!") + name = input("Please enter your name: ") + return name + + def options_menu(self, options): + """Displays the options menu and updates option variables to reflect + user selections.""" + while True: + print("\n"*80) + print("Game Options:\n") + print("1 - Number of Decks: {}".format(options.number_of_decks)) + print("2 - Dealer Hits on Soft 17: {}".format("Enabled" if + options.hit_soft_17 + else "Disabled")) + print("3 - Early Surrender: {}".format("Enabled" if + options.early_surrender + else "Disabled")) + print("4 - No Surrender: {}".format("Enabled" if + options.no_surrender + else "Disabled")) + print("5 - Resplitting Allowed: {}".format("Enabled" if + options.resplitting + else "Disabled")) + print("6 - Resplit Aces: {}".format("Enabled" if + options.resplit_aces + else "Disabled")) + print("7 - Hit Split Aces: {}".format("Enabled" if + options.hit_split_aces + else "Disabled")) + print("8 - Split By Rank/Value: {}".format("Rank" if + options.split_by_rank + else "Value")) + print("9 - No Double After Split: ", end="") + print("{}".format("Enabled" if options.no_double_after_split + else "Disabled")) + print("10 - Double on 9-10-11 ", end="") + print("Only: {}".format("Enabled" if options.double_9_10_11 + else "Disabled")) + print("11 - Double on 9-10 Only: {}".format("Enabled" if + options.double_9_10 + else "Disabled")) + print("R - Reset to Defaults") + print("Q - Return to Main Menu") + selection = input("♡ ") + + if selection.upper() == "Q": + return options + else: + options = self.options_menu_input(selection, options) + + def options_menu_input(self, selection, options): + """ This method receives a selection from the options menu + and updates the game options object to reflect the desired + ruleset selected by the player.""" + if selection == "1": + try: + decks = int(input("Choose number of decks (8 max): ")) + if decks < 1 or decks > 8: + raise ValueError + options.number_of_decks = decks + except ValueError: + input("Invalid input. Press enter to continue.") + elif selection == "2": + options.hit_soft_17 = not options.hit_soft_17 + elif selection == "3": + options.early_surrender = not options.early_surrender + if options.no_surrender: + options.no_surrender = False + elif selection == "4": + options.no_surrender = not options.no_surrender + if options.early_surrender: + options.early_surrender = False + elif selection == "5": + options.resplitting = not options.resplitting + elif selection == "6": + options.resplit_aces = not options.resplit_aces + elif selection == "7": + options.hit_split_aces = not options.hit_split_aces + elif selection == "8": + options.split_by_rank = not options.split_by_rank + elif selection == "9": + options.no_double_after_split = (not + options.no_double_after_split) + elif selection == "10": + options.double_9_10_11 = not options.double_9_10_11 + if options.double_9_10: + options.double_9_10 = False + elif selection == "11": + options.double_9_10 = not options.double_9_10 + if options.double_9_10_11: + options.double_9_10_11 = False + elif selection.upper() == "R": + options = GameOptions() + + return options From a6cc8bed5ea1cf619939f79f83a192be504845c8 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 09:28:38 -0500 Subject: [PATCH 27/50] Added payout method and method to determine the player's available actions. --- blackjack/dealer.py | 3 ++ blackjack/game.py | 26 +++++++++++++++++ blackjack/interface.py | 34 +++++++++++++++++++---- blackjack/player.py | 5 ++++ blackjack/tests/test_game.py | 54 ++++++++++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 6 deletions(-) diff --git a/blackjack/dealer.py b/blackjack/dealer.py index 7467198..53032b4 100644 --- a/blackjack/dealer.py +++ b/blackjack/dealer.py @@ -27,5 +27,8 @@ def hit(self): else: return False + def get_show_card(self): + return self.hand.cards[0] + def takes_hit(self, card): self.hand.cards.append(card) diff --git a/blackjack/game.py b/blackjack/game.py index dd435a8..b53e5d1 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -72,6 +72,11 @@ def can_surrender(self, player_hand, dealer_show_card): and len(player_hand.cards) == 2 and dealer_show_card.rank == "A") + def can_insure(self, player_hand, dealer_show_card): + """Checks to see if the options to insure is available to the + player""" + return len(player_hand.cards) == 2 and dealer_show_card.rank == "A" + def create_hands(self, bet): """Creates the player and dealer hands and assigns them to their respective objects""" @@ -81,6 +86,7 @@ def create_hands(self, bet): player_cards.append(self.deck.deal()) dealer_cards.append(self.deck.deal()) self.player.hands.append(Hand(bet, player_cards)) + self.player.modify_money(bet * -1) self.dealer.hand = Hand(0, dealer_cards) def check_bust(self, hand): @@ -95,3 +101,23 @@ def check_push(self, player_hand, dealer_hand): def compare_hands(self, player_hand, dealer_hand ): """Compares the player and dealer hand to resolve the winner""" return player_hand.get_value > dealer_hand.get_value + + def get_available_actions(self, player_hand, dealer_show_card): + actions = {} + actions["hit"] = self.can_hit(player_hand) + actions["split"] = self.can_split(player_hand) + actions["surrender"] = self.can_surrender(player_hand, + dealer_show_card) + actions["double"] = self.can_double(player_hand) + actions["insure"] = self.can_insure(player_hand, dealer_show_card) + return actions + + def payout(self, player_hand, dealer_hand): + """Distributes money to the player after a hand is resolved and the + player wins.""" + if dealer_hand.get_value() == "BLACKJACK" and self.player.insured: + self.player.modify_money(player_hand.bet) + elif player_hand.get_value() == "BLACKJACK": + self.player.modify_money(player_hand.bet * 2.5) + else: + self.player.modify_money(player_hand.bet * 2) diff --git a/blackjack/interface.py b/blackjack/interface.py index 9b44cb3..e6f5fa9 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -1,6 +1,7 @@ from game_options import GameOptions +from random import choice - +icons = ["♡", "♧", "♢", "♤"] class Interface: """ Interface class will provide all of the methods of interacting with the user. It will contain all of the methods for displaying to the @@ -11,6 +12,8 @@ class Interface: * Receiving input from the player """ + def display_game_status(self, game): + pass def main_menu(self): """ Displays the title, programmer info, and main menu for the game. Takes user input and returns to the calling Game class""" @@ -44,22 +47,40 @@ def main_menu(self): print("♧ 2 - Set Game Options") print("♢ 3 - Quit") print("-----------------------") - selection = input("♤ ") + selection = input("{} ".format(choice(icons))) return selection + def get_bet(self, player): + """Prompts the user to select a bet amount in multiples of 5, not + exceeding 20""" + print("\n"*80) + while True: + print("How much would you like to wager on the next hand? ") + print("Please wager in multiples of 5. Maximum bet is 20.") + try: + print("You currently have {} dollars.".format(player.money)) + bet = int(input("{} ".format(choice(icons)))) + if bet % 5 is not 0 or 0 > bet > 20: + raise ValueError + else: + return bet + except ValueError: + print("Sorry, that's not a valid bet.") + def get_name(self): """Prompts the user to input a name, used for the creation of a Player object.""" print("\n"*80) print("Great! Let's play some Blackjack!") - name = input("Please enter your name: ") + name = input("Please enter your name {} ".format(choice(icons))) return name def options_menu(self, options): """Displays the options menu and updates option variables to reflect user selections.""" - while True: + while selection is not "Q": + selection = "" print("\n"*80) print("Game Options:\n") print("1 - Number of Decks: {}".format(options.number_of_decks)) @@ -95,7 +116,7 @@ def options_menu(self, options): else "Disabled")) print("R - Reset to Defaults") print("Q - Return to Main Menu") - selection = input("♡ ") + selection = input("{} ".format(choice(icons))) if selection.upper() == "Q": return options @@ -108,7 +129,8 @@ def options_menu_input(self, selection, options): ruleset selected by the player.""" if selection == "1": try: - decks = int(input("Choose number of decks (8 max): ")) + decks = int(input("Choose number of decks (8 max){} ".format( + choice(icons)))) if decks < 1 or decks > 8: raise ValueError options.number_of_decks = decks diff --git a/blackjack/player.py b/blackjack/player.py index a5cd430..4e21194 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -17,6 +17,11 @@ def __init__(self, name): self.name = name self.hands = [] self.money = 100 + self.insured = False + + def buys_insurance(self): + self.modify_money(self.hands[0] * -.5) + self.insured = True def modify_money(self, value): if self.money + value > 0: diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 21cd2d2..355ab8d 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -13,6 +13,7 @@ def test_create_hands(): assert len(new_game.dealer.hand.cards) == 2 assert len(new_game.deck.cards) == 48 assert new_game.player.hands[0].bet == 10 + assert new_game.player.money == 90 def test_bust(): @@ -185,3 +186,56 @@ def test_surrender(): new_game = Game(options, "Alan") hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) new_game.player.surrenders(hand) + +def test_payout(): + options = GameOptions() + new_game = Game(options, "Alan") + new_game.create_hands(10) + new_game.player.hands[0] = Hand(10,[Card("2", "spades"), + Card("J", "clubs")]) + new_game.dealer.hand = Hand(10,[Card("3", "spades"), + Card("Q", "clubs")]) + new_game.payout(new_game.player.hands[0], new_game.dealer.hand) + assert new_game.player.money == 110 + + new_game.create_hands(10) + new_game.player.hands[0] = Hand(10,[Card("A", "spades"), + Card("J", "clubs")]) + new_game.payout(new_game.player.hands[0], new_game.dealer.hand) + assert new_game.player.money == 125 + + new_game.create_hands(10) + assert new_game.player.money == 115 + new_game.player.hands[0] = Hand(10,[Card("2", "spades"), + Card("J", "clubs")]) + new_game.dealer.hand = Hand(0,[Card("A", "spades"), + Card("J", "clubs")]) + new_game.player.insured = True + assert new_game.dealer.hand.get_value() == "BLACKJACK" + new_game.payout(new_game.player.hands[0], new_game.dealer.hand) + assert new_game.player.money == 125 + +def test_get_available_actions(): + options = GameOptions() + new_game = Game(options, "Alan") + new_game.player.hands.append(Hand(10,[Card("2", "spades"), + Card("J", "clubs")])) + new_game.dealer.hand = Hand(0, [Card("J", "spades"), Card("7", "Clubs")]) + actions = new_game.get_available_actions(new_game.player.hands[0], + new_game.dealer.get_show_card()) + assert actions["hit"] + assert actions["double"] + assert not actions["split"] + assert not actions["surrender"] + assert not actions["insure"] + new_game.player.hands[0] = Hand(10,[Card("J", "spades"), + Card("J", "clubs")]) + new_game.dealer.hand = Hand(0, [Card("A", "spades"), Card("7", "Clubs")]) + actions = new_game.get_available_actions(new_game.player.hands[0], + new_game.dealer.get_show_card()) + + assert actions["hit"] + assert actions["double"] + assert actions["split"] + assert actions["surrender"] + assert actions["insure"] From 91f91320742960cdf981517402b4788cc2f1dca2 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 14:36:07 -0500 Subject: [PATCH 28/50] Updated several methods to work correctly with interface. Coded most of interface, debugging now. --- blackjack/game.py | 20 ++- blackjack/hand.py | 8 +- blackjack/interface.py | 233 ++++++++++++++++++++++++++++++++++- blackjack/main.py | 30 +++++ blackjack/player.py | 11 +- blackjack/tests/test_game.py | 4 +- blackjack/tests/test_hand.py | 2 +- 7 files changed, 289 insertions(+), 19 deletions(-) create mode 100644 blackjack/main.py diff --git a/blackjack/game.py b/blackjack/game.py index b53e5d1..5f9f7b9 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -70,12 +70,14 @@ def can_surrender(self, player_hand, dealer_show_card): player""" return (not self.options.no_surrender and len(player_hand.cards) == 2 - and dealer_show_card.rank == "A") + and dealer_show_card.rank == "A" + and not len(self.player.hands) > 1) def can_insure(self, player_hand, dealer_show_card): """Checks to see if the options to insure is available to the player""" - return len(player_hand.cards) == 2 and dealer_show_card.rank == "A" + return (len(player_hand.cards) == 2 and dealer_show_card.rank == "A" + and not len(self.player.hands) > 1 and not self.player.insured) def create_hands(self, bet): """Creates the player and dealer hands and assigns them to their @@ -100,7 +102,7 @@ def check_push(self, player_hand, dealer_hand): def compare_hands(self, player_hand, dealer_hand ): """Compares the player and dealer hand to resolve the winner""" - return player_hand.get_value > dealer_hand.get_value + return player_hand.get_value() > dealer_hand.get_value() def get_available_actions(self, player_hand, dealer_show_card): actions = {} @@ -115,9 +117,15 @@ def get_available_actions(self, player_hand, dealer_show_card): def payout(self, player_hand, dealer_hand): """Distributes money to the player after a hand is resolved and the player wins.""" - if dealer_hand.get_value() == "BLACKJACK" and self.player.insured: + if ((dealer_hand.get_value() == 21 and len(dealer_hand.cards) == 2) + and self.player.insured): self.player.modify_money(player_hand.bet) - elif player_hand.get_value() == "BLACKJACK": - self.player.modify_money(player_hand.bet * 2.5) else: self.player.modify_money(player_hand.bet * 2) + + def payout_blackjack(self, player_hand): + """Pays out on player blackjack""" + self.player.modify_money(int(player_hand.bet * 2.5)) + + def reshuffle(self): + self.deck = Deck(self.options.number_of_decks) diff --git a/blackjack/hand.py b/blackjack/hand.py index 7a06ad3..f1cd207 100644 --- a/blackjack/hand.py +++ b/blackjack/hand.py @@ -31,12 +31,16 @@ def get_ranks(self): return card_rank_list + def get_card_strings(self): + card_strings = [] + for card in self.cards: + card_strings.append(str(card)) + return card_strings + def get_value(self): hand_value = 0 for card in self.cards: hand_value += card.value - if hand_value == 21 and len(self.cards) == 2: - return "BLACKJACK" if ((hand_value > 21 and "A" in self.get_ranks()) or hand_value <= 11 and "A" in self.get_ranks()): for card in self.cards: diff --git a/blackjack/interface.py b/blackjack/interface.py index e6f5fa9..8576209 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -12,8 +12,232 @@ class Interface: * Receiving input from the player """ - def display_game_status(self, game): - pass + def __init__(self): + self.game = None + + def play_game(self, game): + """Starts each hand an keeps the game going. Offers the user the + choice to play another hand or not.""" + self.game = game + + while self.game.player.money >= 5: + self.play_hand() + while True: + player_choice = input("Play another hand? (Y/N) {} ".format( + choice(icons))).upper() + if player_choice in ["Y","N"]: + break + if player_choice == "N": + break + + def play_hand(self): + + print("\n"*80) + bet = self.get_bet() + self.game.player.reset_player() + self.game.create_hands(bet) + print("\n"*80) + current_hand = 0 + dealers_turn = False + self.game.player.insured = False + print("Dealing Cards {}\n".format(choice(icons))) + + if len(self.game.deck.cards) < 30: + self.game.reshuffle() + + while True: + + self.print_hands(current_hand) + if self.resolve_blackjacks(): + break + selection, actions = self.offer_actions(current_hand) + self.execute_selection(selection, actions, current_hand) + + if self.game.check_bust(self.game.player.hands[current_hand]): + if len(self.game.player.hands) == current_hand + 1: + print("You bust!\n") + break + else: + print("You bust!\n") + current_hand += 1 + elif selection == "R": + print("You surrendered...\n") + break + elif selection == "S" or selection == "D": + if len(self.game.player.hands) > current_hand + 1: + print("Dealer's turn!\n") + current_hand += 1 + else: + print("Dealer's trun!\n") + dealers_turn = True + + if dealers_turn: + need_to_compare = self.dealer_play() + + if need_to_compare: + self.evaluate_hands() + break + else: + break + + def evaluate_hands(self): + if len(self.game.player.hands) > 1: + for hand in enumerate(self.game.player.hands): + if self.game.check_bust(hand[1]): + print("Hand {} busted.".format(hand[0]+1)) + elif self.game.check_push(hand[1], self.game.dealer.hand): + print("Push on hand {}.".format(hand[0]+1)) + else: + if self.game.compare_hands(hand[1], self.game.dealer.hand): + print("Hand {} wins!".format(hand[0]+1)) + self.game.payout(hand[1], self.game.dealer.hand) + else: + print("Hand {} loses!".format(hand[0]+1)) + print_dealer_hand() + elif self.game.check_push(self.game.player.hands[0], + self.game.dealer.hand): + self.game.payout(self.game.player.hands[0], + self.game.dealer.hand) + print("Push.\n") + print_dealer_hand() + else: + if self.game.compare_hands(self.game.player.hands[0], + self.game.dealer.hand): + print("You win!\n") + self.game.payout(self.game.player.hands[0], + self.game.dealer.hand) + print_dealer_hand() + + else: + print("Dealer wins.\n") + print_dealer_hand() + + def dealer_play(self): + while self.game.dealer.hit(): + new_card = self.game.deck.deal() + print("Dealer hits, and recieves {}.\n".format(str(new_card))) + self.game.dealer.takes_hit(new_card) + + if self.game.check_bust(self.game.dealer.hand): + print("Dealer busts!\n") + for hand in self.game.player.hands: + if hand.get_value() <= 21: + self.game.payout(hand, self.game.dealer.hand) + return False + else: + return True + + def print_dealer_hand(self): + print("Dealer's final hand: {}".format( + self.game.dealer.hand.get_card_strings())) + + def resolve_blackjacks(self): + if self.check_for_player_blackjack(): + return True + if (self.game.dealer.get_show_card().rank == "A" + and len(self.game.player.hands[0].cards) == 2 + and len(self.game.player.hands) == 1): + self.offer_insurance() + if self.game.options.early_surrender: + if self.offer_surrender(): + self.game.player.surrenders(self.game.player.hands[0]) + print("You surrendered.\n") + print("Dealer had: {}\n".format( + self.game.dealer.hand.get_card_strings())) + return True + else: + if self.check_for_dealer_blackjack(): + print("Dealer has blackjack!\n") + return True + return False + + def check_for_dealer_blackjack(self): + return self.game.dealer.hand.get_value() == 21 + + def check_for_player_blackjack(self): + if (self.game.player.hands[0].get_value() == 21 + and len(self.game.player.hands[0].cards) == 2): + print("BLACKJACK!\n") + if self.game.dealer.hand.get_value() == 21: + print("Dealer has BLACKJACK. Push...\n") + self.game.payout(self.game.player.hands[0], + self.game.dealer.hand) + return True + else: + print("Blackjack pays 3:2!\n") + self.game.payout_blackjack(self.game.player.hands[0]) + return True + else: + return False + + def print_hands(self, current_hand): + print("Dealer's Hand: [{}, [X]]".format( + self.game.dealer.get_show_card())) + + print("Player's Hand{}: ".format("s" if len(self.game.player.hands) > 1 + else ""), end="") + for hand in self.game.player.hands: + print("{} ".format(hand.get_card_strings(), end="")) + print("Money: {}".format(self.game.player.money)) + + def execute_selection(self, selection, actions, current_hand): + if selection == "H" and actions["hit"]: + new_card = self.game.deck.deal() + print("Received {}".format(new_card)) + self.game.player.takes_hit(self.game.player.hands[current_hand], + new_card) + elif selection == "D" and actions["double"]: + new_card = self.game.deck.deal() + print("Received {}".format(new_card)) + self.game.player.doubles(self.game.player.hands[current_hand], + new_card) + elif selection == "P" and actions["split"]: + new_cards = [self.game.deck.deal(), self.game.deck.deal()] + self.game.player.splits(self.game.player.hands[current_hand], + new_cards) + elif selection == "R" and actions["surrender"]: + self.game.player.surrenders(self.game.player.hands[current_hand]) + + def offer_actions(self, current_hand): + actions = self.game.get_available_actions( + self.game.player.hands[current_hand], + self.game.dealer.get_show_card()) + valid_selection_made = False + while not valid_selection_made: + valid_input = ["S"] + if actions["hit"]: + print("(H)it, ", end="") + valid_input.append("H") + if actions["double"]: + print("(D)ouble Down, ", end="") + valid_input.append("D") + if actions["split"]: + print("S(P)lit, ", end="") + valid_input.append("P") + if actions["surrender"]: + print("Surrende(R), ", end="") + valid_input.append("R") + if actions["insure"]: + print("Buy (I)nsurance, ", end="") + valid_input.append("I") + print ("(S)tand") + selection = input("{} ".format(choice(icons))).upper() + valid_selection_made = selection in valid_input + + return selection, actions + + def offer_insurance(self): + print("Dealer has an Ace showing. Buy insurance?") + while True: + player_choice = input("(Y/N) {} ".format(choice(icons))).upper() + if player_choice == "Y": + self.game.player.buys_insurance() + break + elif player_choice == "N": + break + else: + print("Please enter Y or N.") + def main_menu(self): """ Displays the title, programmer info, and main menu for the game. Takes user input and returns to the calling Game class""" @@ -51,7 +275,7 @@ def main_menu(self): return selection - def get_bet(self, player): + def get_bet(self): """Prompts the user to select a bet amount in multiples of 5, not exceeding 20""" print("\n"*80) @@ -59,7 +283,8 @@ def get_bet(self, player): print("How much would you like to wager on the next hand? ") print("Please wager in multiples of 5. Maximum bet is 20.") try: - print("You currently have {} dollars.".format(player.money)) + print("You currently have {} dollars.".format( + self.game.player.money)) bet = int(input("{} ".format(choice(icons)))) if bet % 5 is not 0 or 0 > bet > 20: raise ValueError diff --git a/blackjack/main.py b/blackjack/main.py new file mode 100644 index 0000000..c8a297b --- /dev/null +++ b/blackjack/main.py @@ -0,0 +1,30 @@ +from game import Game +from game_options import GameOptions +from interface import Interface + + +class Main: + + def main(self): + """This is the launching method for the game. It displays the main menu, + receives menu selection and launches the options menu and the game when + prompted.""" + interface = Interface() + options = GameOptions() + + while True: + selection = 0 + selection = interface.main_menu() + if selection == "1": + name = interface.get_name() + game = Game(options, name) + interface.play_game(game) + elif selection == "2": + options = interface.options_menu(options) + elif selection == "3": + break + + +if __name__ == '__main__': + + Main().main() diff --git a/blackjack/player.py b/blackjack/player.py index 4e21194..77699a6 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -20,7 +20,7 @@ def __init__(self, name): self.insured = False def buys_insurance(self): - self.modify_money(self.hands[0] * -.5) + self.modify_money( int(-.5*self.hands[0].bet)) self.insured = True def modify_money(self, value): @@ -33,17 +33,20 @@ def takes_hit(self, hand, card): hand.add_cards(card) def splits(self, hand, cards): - self.modify_money(-1 * hand.bet) + self.modify_money(-1*hand.bet) new_hand = Hand(hand.bet, [hand.cards[1], cards[0]]) hand.cards.pop(1) hand.add_cards(cards[1]) self.hands.append(new_hand) def doubles(self, hand, card): - self.modify_money(-1 * hand.bet) + self.modify_money(-1*hand.bet) hand.bet *= 2 self.takes_hit(hand, card) def surrenders(self, hand): - self.modify_money(.5 * hand.bet) + self.modify_money(.5*hand.bet) hand.bet *= .5 + + def reset_player(self): + self.hands = [] diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 355ab8d..85dc11a 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -201,7 +201,7 @@ def test_payout(): new_game.create_hands(10) new_game.player.hands[0] = Hand(10,[Card("A", "spades"), Card("J", "clubs")]) - new_game.payout(new_game.player.hands[0], new_game.dealer.hand) + new_game.payout_blackjack(new_game.player.hands[0]) assert new_game.player.money == 125 new_game.create_hands(10) @@ -211,7 +211,7 @@ def test_payout(): new_game.dealer.hand = Hand(0,[Card("A", "spades"), Card("J", "clubs")]) new_game.player.insured = True - assert new_game.dealer.hand.get_value() == "BLACKJACK" + assert new_game.dealer.hand.get_value() == 21 new_game.payout(new_game.player.hands[0], new_game.dealer.hand) assert new_game.player.money == 125 diff --git a/blackjack/tests/test_hand.py b/blackjack/tests/test_hand.py index 810fdec..04ee95a 100644 --- a/blackjack/tests/test_hand.py +++ b/blackjack/tests/test_hand.py @@ -64,4 +64,4 @@ def test_blackjack(): test_cards.append(Card("10", "clubs")) hand = Hand(10, test_cards) - assert hand.get_value() == "BLACKJACK" + assert hand.get_value() == 21 From 95b8d82ab3cb3f498677ebec002b8e38d36b5c56 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 14:50:49 -0500 Subject: [PATCH 29/50] Fixed bug which crashed script if player bet more money than they had. --- blackjack/game.py | 7 ++++--- blackjack/interface.py | 20 ++++++++++---------- blackjack/player.py | 2 +- blackjack/tests/test_game.py | 2 -- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/blackjack/game.py b/blackjack/game.py index 5f9f7b9..cdd4692 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -107,11 +107,12 @@ def compare_hands(self, player_hand, dealer_hand ): def get_available_actions(self, player_hand, dealer_show_card): actions = {} actions["hit"] = self.can_hit(player_hand) - actions["split"] = self.can_split(player_hand) + actions["split"] = (self.can_split(player_hand) and + self.player.money >= player_hand.bet) actions["surrender"] = self.can_surrender(player_hand, dealer_show_card) - actions["double"] = self.can_double(player_hand) - actions["insure"] = self.can_insure(player_hand, dealer_show_card) + actions["double"] = (self.can_double(player_hand) and + self.player.money >= player_hand.bet) return actions def payout(self, player_hand, dealer_hand): diff --git a/blackjack/interface.py b/blackjack/interface.py index 8576209..e8161b7 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -73,7 +73,6 @@ def play_hand(self): if dealers_turn: need_to_compare = self.dealer_play() - if need_to_compare: self.evaluate_hands() break @@ -93,24 +92,24 @@ def evaluate_hands(self): self.game.payout(hand[1], self.game.dealer.hand) else: print("Hand {} loses!".format(hand[0]+1)) - print_dealer_hand() + self.print_dealer_hand() elif self.game.check_push(self.game.player.hands[0], self.game.dealer.hand): self.game.payout(self.game.player.hands[0], self.game.dealer.hand) print("Push.\n") - print_dealer_hand() + self.print_dealer_hand() else: if self.game.compare_hands(self.game.player.hands[0], self.game.dealer.hand): print("You win!\n") self.game.payout(self.game.player.hands[0], self.game.dealer.hand) - print_dealer_hand() + self.print_dealer_hand() else: print("Dealer wins.\n") - print_dealer_hand() + self.print_dealer_hand() def dealer_play(self): while self.game.dealer.hit(): @@ -136,7 +135,8 @@ def resolve_blackjacks(self): return True if (self.game.dealer.get_show_card().rank == "A" and len(self.game.player.hands[0].cards) == 2 - and len(self.game.player.hands) == 1): + and len(self.game.player.hands) == 1 + and self.game.player.money >= self.player.hands[0].bet): self.offer_insurance() if self.game.options.early_surrender: if self.offer_surrender(): @@ -217,9 +217,6 @@ def offer_actions(self, current_hand): if actions["surrender"]: print("Surrende(R), ", end="") valid_input.append("R") - if actions["insure"]: - print("Buy (I)nsurance, ", end="") - valid_input.append("I") print ("(S)tand") selection = input("{} ".format(choice(icons))).upper() valid_selection_made = selection in valid_input @@ -286,7 +283,10 @@ def get_bet(self): print("You currently have {} dollars.".format( self.game.player.money)) bet = int(input("{} ".format(choice(icons)))) - if bet % 5 is not 0 or 0 > bet > 20: + if (bet % 5 is not 0 + or bet < 0 + or bet > 20 + or bet > self.game.player.money): raise ValueError else: return bet diff --git a/blackjack/player.py b/blackjack/player.py index 77699a6..388a137 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -24,7 +24,7 @@ def buys_insurance(self): self.insured = True def modify_money(self, value): - if self.money + value > 0: + if self.money + value >= 0: self.money += value else: raise ValueError("Player money cannot be negative.") diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 85dc11a..976a5f2 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -227,7 +227,6 @@ def test_get_available_actions(): assert actions["double"] assert not actions["split"] assert not actions["surrender"] - assert not actions["insure"] new_game.player.hands[0] = Hand(10,[Card("J", "spades"), Card("J", "clubs")]) new_game.dealer.hand = Hand(0, [Card("A", "spades"), Card("7", "Clubs")]) @@ -238,4 +237,3 @@ def test_get_available_actions(): assert actions["double"] assert actions["split"] assert actions["surrender"] - assert actions["insure"] From 00afbdea65de1fed733d1ae7c780b28ed26e04ba Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 15:45:40 -0500 Subject: [PATCH 30/50] Fixed bug where player busting on last hand of split crashed game. Fixed bug where push payed out incorrect amount. Fixed formatting on player hand after split. --- blackjack/game.py | 2 ++ blackjack/hand.py | 3 ++- blackjack/interface.py | 43 ++++++++++++++++++++++++++++-------------- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/blackjack/game.py b/blackjack/game.py index cdd4692..b72cabe 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -121,6 +121,8 @@ def payout(self, player_hand, dealer_hand): if ((dealer_hand.get_value() == 21 and len(dealer_hand.cards) == 2) and self.player.insured): self.player.modify_money(player_hand.bet) + elif player_hand.get_value() == dealer_hand.get_value(): + self.player.modify_money(player_hand.bet) else: self.player.modify_money(player_hand.bet * 2) diff --git a/blackjack/hand.py b/blackjack/hand.py index f1cd207..64a9a01 100644 --- a/blackjack/hand.py +++ b/blackjack/hand.py @@ -35,7 +35,8 @@ def get_card_strings(self): card_strings = [] for card in self.cards: card_strings.append(str(card)) - return card_strings + cards = "[ " + " ".join(card_strings) + " ]" + return cards def get_value(self): hand_value = 0 diff --git a/blackjack/interface.py b/blackjack/interface.py index e8161b7..9f9a104 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -23,11 +23,15 @@ def play_game(self, game): while self.game.player.money >= 5: self.play_hand() while True: + if self.game.player.money < 5: + print("You don't have enough money to play!") + input() + break player_choice = input("Play another hand? (Y/N) {} ".format( choice(icons))).upper() if player_choice in ["Y","N"]: break - if player_choice == "N": + if player_choice == "N" or self.game.player.money < 5: break def play_hand(self): @@ -54,9 +58,12 @@ def play_hand(self): self.execute_selection(selection, actions, current_hand) if self.game.check_bust(self.game.player.hands[current_hand]): - if len(self.game.player.hands) == current_hand + 1: + if len(self.game.player.hands) == 1: print("You bust!\n") break + elif len(self.game.player.hands) > current_hand + 1: + print("You bust!\n") + print("Dealer's turn!\n") else: print("You bust!\n") current_hand += 1 @@ -65,10 +72,9 @@ def play_hand(self): break elif selection == "S" or selection == "D": if len(self.game.player.hands) > current_hand + 1: - print("Dealer's turn!\n") current_hand += 1 else: - print("Dealer's trun!\n") + print("Dealer's turn!\n") dealers_turn = True if dealers_turn: @@ -86,6 +92,7 @@ def evaluate_hands(self): print("Hand {} busted.".format(hand[0]+1)) elif self.game.check_push(hand[1], self.game.dealer.hand): print("Push on hand {}.".format(hand[0]+1)) + self.game.payout(hand[1], self.game.dealer.hand) else: if self.game.compare_hands(hand[1], self.game.dealer.hand): print("Hand {} wins!".format(hand[0]+1)) @@ -127,7 +134,7 @@ def dealer_play(self): return True def print_dealer_hand(self): - print("Dealer's final hand: {}".format( + print("Dealer's final hand: {}\n".format( self.game.dealer.hand.get_card_strings())) def resolve_blackjacks(self): @@ -136,7 +143,7 @@ def resolve_blackjacks(self): if (self.game.dealer.get_show_card().rank == "A" and len(self.game.player.hands[0].cards) == 2 and len(self.game.player.hands) == 1 - and self.game.player.money >= self.player.hands[0].bet): + and self.game.player.money >= self.game.player.hands[0].bet): self.offer_insurance() if self.game.options.early_surrender: if self.offer_surrender(): @@ -145,10 +152,11 @@ def resolve_blackjacks(self): print("Dealer had: {}\n".format( self.game.dealer.hand.get_card_strings())) return True - else: - if self.check_for_dealer_blackjack(): - print("Dealer has blackjack!\n") - return True + + if self.check_for_dealer_blackjack(): + print("Dealer has blackjack!\n") + return True + return False def check_for_dealer_blackjack(self): @@ -156,7 +164,8 @@ def check_for_dealer_blackjack(self): def check_for_player_blackjack(self): if (self.game.player.hands[0].get_value() == 21 - and len(self.game.player.hands[0].cards) == 2): + and len(self.game.player.hands[0].cards) == 2 + and len(self.game.player.hands) == 1): print("BLACKJACK!\n") if self.game.dealer.hand.get_value() == 21: print("Dealer has BLACKJACK. Push...\n") @@ -176,9 +185,14 @@ def print_hands(self, current_hand): print("Player's Hand{}: ".format("s" if len(self.game.player.hands) > 1 else ""), end="") - for hand in self.game.player.hands: - print("{} ".format(hand.get_card_strings(), end="")) - print("Money: {}".format(self.game.player.money)) + card_string = "" + for hand in enumerate(self.game.player.hands): + card_string += "{}{} ".format("*" if hand[0] == current_hand else + "", + hand[1].get_card_strings(), end="") + + print(card_string) + print("\nMoney: {}".format(self.game.player.money)) def execute_selection(self, selection, actions, current_hand): if selection == "H" and actions["hit"]: @@ -304,6 +318,7 @@ def get_name(self): def options_menu(self, options): """Displays the options menu and updates option variables to reflect user selections.""" + selection = "" while selection is not "Q": selection = "" print("\n"*80) From 8f05b25904909371fa427cc5c160487481009671 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 15:49:48 -0500 Subject: [PATCH 31/50] Fixed bug where insurance failed to pay when dealer hit blackjack. --- blackjack/interface.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blackjack/interface.py b/blackjack/interface.py index 9f9a104..53854ff 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -155,6 +155,8 @@ def resolve_blackjacks(self): if self.check_for_dealer_blackjack(): print("Dealer has blackjack!\n") + self.game.payout(self.game.player.hands[0], + self.game.dealer.hand) return True return False From 2f4fa7cd01e6230a5ef7ac5cbca805074e34cc1e Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 16:12:21 -0500 Subject: [PATCH 32/50] Updated surrender to no longer require the dealer's upcard to be Ace. Added docstrings to all methods which were missing them. --- blackjack/card.py | 2 ++ blackjack/dealer.py | 4 ++++ blackjack/game.py | 11 ++++++----- blackjack/hand.py | 6 ++++++ blackjack/interface.py | 27 +++++++++++++++++++++++---- blackjack/player.py | 15 +++++++++++++-- 6 files changed, 54 insertions(+), 11 deletions(-) diff --git a/blackjack/card.py b/blackjack/card.py index 5559dcb..a4a3722 100644 --- a/blackjack/card.py +++ b/blackjack/card.py @@ -41,6 +41,7 @@ def __eq__(self, other): return self.value == other.value def _evaluate_card_value(self): + """Evaluates the value of the card and sets it as a class attribute""" if self.rank.isdigit(): self.value = int(self.rank) elif self.rank == "A": @@ -49,6 +50,7 @@ def _evaluate_card_value(self): self.value = 10 def swap_ace(self): + """Swaps the value of the ace if requested by the game.""" if self.value == 11: self.value = 1 else: diff --git a/blackjack/dealer.py b/blackjack/dealer.py index 53032b4..d561cb0 100644 --- a/blackjack/dealer.py +++ b/blackjack/dealer.py @@ -17,6 +17,8 @@ def __init__(self, hit_soft_17=False): self.hit_soft_17 = hit_soft_17 def hit(self): + """Determines if the dealer should hit or not. Has option to allow + for hit/no hit on soft 17""" if not self.hit_soft_17: return self.hand.get_value() < 17 else: @@ -28,7 +30,9 @@ def hit(self): return False def get_show_card(self): + """Returns the dealer's show card for display in the game""" return self.hand.cards[0] def takes_hit(self, card): + """Appends a dealt card to the dealer's hand""" self.hand.cards.append(card) diff --git a/blackjack/game.py b/blackjack/game.py index b72cabe..6016167 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -65,12 +65,11 @@ def can_split(self, hand): else: return hand.cards[0] == hand.cards[1] - def can_surrender(self, player_hand, dealer_show_card): + def can_surrender(self, player_hand): """Checks to see if the options to surrender is available to the player""" return (not self.options.no_surrender and len(player_hand.cards) == 2 - and dealer_show_card.rank == "A" and not len(self.player.hands) > 1) def can_insure(self, player_hand, dealer_show_card): @@ -104,13 +103,14 @@ def compare_hands(self, player_hand, dealer_hand ): """Compares the player and dealer hand to resolve the winner""" return player_hand.get_value() > dealer_hand.get_value() - def get_available_actions(self, player_hand, dealer_show_card): + def get_available_actions(self, player_hand): + """Generates a dictionary specifying the available actions + for a given hand.""" actions = {} actions["hit"] = self.can_hit(player_hand) actions["split"] = (self.can_split(player_hand) and self.player.money >= player_hand.bet) - actions["surrender"] = self.can_surrender(player_hand, - dealer_show_card) + actions["surrender"] = self.can_surrender(player_hand) actions["double"] = (self.can_double(player_hand) and self.player.money >= player_hand.bet) return actions @@ -131,4 +131,5 @@ def payout_blackjack(self, player_hand): self.player.modify_money(int(player_hand.bet * 2.5)) def reshuffle(self): + """Creates a new deck for use by the game""" self.deck = Deck(self.options.number_of_decks) diff --git a/blackjack/hand.py b/blackjack/hand.py index 64a9a01..32f771a 100644 --- a/blackjack/hand.py +++ b/blackjack/hand.py @@ -21,9 +21,12 @@ def __init__(self, bet, cards=[]): self.bet = bet def add_cards(self, card): + """Appends a dealt card to the Hand object's cards list""" self.cards.append(card) def get_ranks(self): + """Returns a list of ranks of all cards in the Hand object's + card list""" card_rank_list = [] for card in self.cards: @@ -32,6 +35,8 @@ def get_ranks(self): return card_rank_list def get_card_strings(self): + """Returns a string representation of all cards in the Hand object's + card list""" card_strings = [] for card in self.cards: card_strings.append(str(card)) @@ -39,6 +44,7 @@ def get_card_strings(self): return cards def get_value(self): + """Returns the total value of all cards in the card list""" hand_value = 0 for card in self.cards: hand_value += card.value diff --git a/blackjack/interface.py b/blackjack/interface.py index 53854ff..84c67c8 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -35,7 +35,9 @@ def play_game(self, game): break def play_hand(self): - + """Hand loop. Continues until the end of the hand. Prints updates + about the status of the hand to the screen and makes method calls + to allow user actions.""" print("\n"*80) bet = self.get_bet() self.game.player.reset_player() @@ -86,6 +88,8 @@ def play_hand(self): break def evaluate_hands(self): + """Evaluates the final hands of the player and dealer and makes + the appropriate calls the the playout method of the Game class""" if len(self.game.player.hands) > 1: for hand in enumerate(self.game.player.hands): if self.game.check_bust(hand[1]): @@ -119,6 +123,9 @@ def evaluate_hands(self): self.print_dealer_hand() def dealer_play(self): + """Dealer's play method. Goes through all dealer turns until the + hit() method of dealer determines it needs to stand or the dealer + busts""" while self.game.dealer.hit(): new_card = self.game.deck.deal() print("Dealer hits, and recieves {}.\n".format(str(new_card))) @@ -134,10 +141,15 @@ def dealer_play(self): return True def print_dealer_hand(self): + """Prints the dealer's hand to the screen""" print("Dealer's final hand: {}\n".format( self.game.dealer.hand.get_card_strings())) def resolve_blackjacks(self): + """Resolves all cases of either or both the player and dealer having + blackjack and makes appropriate payouts. Also calls the offer + insurance method to allow the player to insure before blackjacks + are resolved.""" if self.check_for_player_blackjack(): return True if (self.game.dealer.get_show_card().rank == "A" @@ -162,14 +174,16 @@ def resolve_blackjacks(self): return False def check_for_dealer_blackjack(self): + """Checks the dealer's hand for blackjack""" return self.game.dealer.hand.get_value() == 21 def check_for_player_blackjack(self): + """Checks to see if the player has blackjack""" if (self.game.player.hands[0].get_value() == 21 and len(self.game.player.hands[0].cards) == 2 and len(self.game.player.hands) == 1): print("BLACKJACK!\n") - if self.game.dealer.hand.get_value() == 21: + if check_for_dealer_blackjack(): print("Dealer has BLACKJACK. Push...\n") self.game.payout(self.game.player.hands[0], self.game.dealer.hand) @@ -182,6 +196,7 @@ def check_for_player_blackjack(self): return False def print_hands(self, current_hand): + """Displays the dealer's and player's hands to the screen""" print("Dealer's Hand: [{}, [X]]".format( self.game.dealer.get_show_card())) @@ -197,6 +212,7 @@ def print_hands(self, current_hand): print("\nMoney: {}".format(self.game.player.money)) def execute_selection(self, selection, actions, current_hand): + """Takes the users selected action and performs it""" if selection == "H" and actions["hit"]: new_card = self.game.deck.deal() print("Received {}".format(new_card)) @@ -215,9 +231,10 @@ def execute_selection(self, selection, actions, current_hand): self.game.player.surrenders(self.game.player.hands[current_hand]) def offer_actions(self, current_hand): + """Prints available actions to the screen and allows user to input + their selected action.""" actions = self.game.get_available_actions( - self.game.player.hands[current_hand], - self.game.dealer.get_show_card()) + self.game.player.hands[current_hand]) valid_selection_made = False while not valid_selection_made: valid_input = ["S"] @@ -240,6 +257,8 @@ def offer_actions(self, current_hand): return selection, actions def offer_insurance(self): + """Method that allows the user to buy insurace if the dealer's upcard + is an Ace""" print("Dealer has an Ace showing. Buy insurance?") while True: player_choice = input("(Y/N) {} ".format(choice(icons))).upper() diff --git a/blackjack/player.py b/blackjack/player.py index 388a137..512deba 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -20,19 +20,27 @@ def __init__(self, name): self.insured = False def buys_insurance(self): + """Modifys the player's money if they choose to buy insurance when + offered""" self.modify_money( int(-.5*self.hands[0].bet)) self.insured = True def modify_money(self, value): + """Updates the player's money based on the supplied value. To + decrease money, value should be negative""" if self.money + value >= 0: self.money += value else: raise ValueError("Player money cannot be negative.") def takes_hit(self, hand, card): + """Adds the supplied card to the specified hand of the player. + Multiple hands may exist if player splits""" hand.add_cards(card) def splits(self, hand, cards): + """Splits the player's hand into two hands, each containing one of + the cards from the original hand, plus a newly dealt card.""" self.modify_money(-1*hand.bet) new_hand = Hand(hand.bet, [hand.cards[1], cards[0]]) hand.cards.pop(1) @@ -40,13 +48,16 @@ def splits(self, hand, cards): self.hands.append(new_hand) def doubles(self, hand, card): + """Doubles the player's bet on the current hand.""" self.modify_money(-1*hand.bet) hand.bet *= 2 self.takes_hit(hand, card) def surrenders(self, hand): - self.modify_money(.5*hand.bet) - hand.bet *= .5 + """Returns half of the player's bet if they choose to surrender the + hand.""" + self.modify_money(int(.5*hand.bet)) def reset_player(self): + """Resets the players list of controlled hands""" self.hands = [] From f8e5889fe1565885d71c1772d6a29d6739dabcc7 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 16:15:40 -0500 Subject: [PATCH 33/50] Updated game test file to reflect argument changes to the surrender method, no longer requiring dealer's upcard as an argument. --- blackjack/tests/test_game.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 976a5f2..5490f70 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -121,11 +121,10 @@ def test_can_surrender(): new_game = Game(options, "Alan") hand1 = Hand(10, [Card("6", "clubs"), Card("10", "hearts")]) hand2 = Hand(10, [Card("7", "hearts"), Card("A", "spades")]) - assert new_game.can_surrender(hand1, hand2.cards[1]) - assert not new_game.can_surrender(hand1, hand2.cards[0]) + assert new_game.can_surrender(hand1) new_game.options.no_surrender = True - assert not new_game.can_surrender(hand1, hand2.cards[1]) - assert not new_game.can_surrender(hand1, hand2.cards[0]) + assert not new_game.can_surrender(hand1) + assert not new_game.can_surrender(hand1) def test_hit_split_aces(): @@ -221,17 +220,15 @@ def test_get_available_actions(): new_game.player.hands.append(Hand(10,[Card("2", "spades"), Card("J", "clubs")])) new_game.dealer.hand = Hand(0, [Card("J", "spades"), Card("7", "Clubs")]) - actions = new_game.get_available_actions(new_game.player.hands[0], - new_game.dealer.get_show_card()) + actions = new_game.get_available_actions(new_game.player.hands[0]) assert actions["hit"] assert actions["double"] assert not actions["split"] - assert not actions["surrender"] + assert actions["surrender"] new_game.player.hands[0] = Hand(10,[Card("J", "spades"), Card("J", "clubs")]) new_game.dealer.hand = Hand(0, [Card("A", "spades"), Card("7", "Clubs")]) - actions = new_game.get_available_actions(new_game.player.hands[0], - new_game.dealer.get_show_card()) + actions = new_game.get_available_actions(new_game.player.hands[0]) assert actions["hit"] assert actions["double"] From c44b49f7227e4b3885c39c2558996c8cc028678d Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 16:18:12 -0500 Subject: [PATCH 34/50] Fixed missing self reference in interface when checking for blackjack --- blackjack/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blackjack/interface.py b/blackjack/interface.py index 84c67c8..b0ba7ae 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -183,7 +183,7 @@ def check_for_player_blackjack(self): and len(self.game.player.hands[0].cards) == 2 and len(self.game.player.hands) == 1): print("BLACKJACK!\n") - if check_for_dealer_blackjack(): + if self.check_for_dealer_blackjack(): print("Dealer has BLACKJACK. Push...\n") self.game.payout(self.game.player.hands[0], self.game.dealer.hand) From 0712babf595254b047fe73e1c1c5aa4a0363b740 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 16:23:15 -0500 Subject: [PATCH 35/50] Fixed bug where player would gain money if dealer hit blackjack --- blackjack/game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blackjack/game.py b/blackjack/game.py index 6016167..8a24b35 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -123,7 +123,7 @@ def payout(self, player_hand, dealer_hand): self.player.modify_money(player_hand.bet) elif player_hand.get_value() == dealer_hand.get_value(): self.player.modify_money(player_hand.bet) - else: + elif self.player_hand.get_value() > delaer_hand.get_value: self.player.modify_money(player_hand.bet * 2) def payout_blackjack(self, player_hand): From 45d55c75baaf8b2b80f9c2b7fcfb0d2db2c6a5b2 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 16:44:19 -0500 Subject: [PATCH 36/50] Fixed bug where Aces weren't allowed to split when using the split by value option, fixed error in test file where payout was not correctly predicted. --- blackjack/game.py | 5 +++-- blackjack/interface.py | 13 +++++++++---- blackjack/tests/test_game.py | 4 +++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/blackjack/game.py b/blackjack/game.py index 8a24b35..38f9815 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -63,7 +63,8 @@ def can_split(self, hand): elif self.options.split_by_rank: return hand.get_ranks()[0] == hand.get_ranks()[1] else: - return hand.cards[0] == hand.cards[1] + return (hand.cards[0] == hand.cards[1] or + hand.get_ranks()[0] == hand.get_ranks()[1]) def can_surrender(self, player_hand): """Checks to see if the options to surrender is available to the @@ -123,7 +124,7 @@ def payout(self, player_hand, dealer_hand): self.player.modify_money(player_hand.bet) elif player_hand.get_value() == dealer_hand.get_value(): self.player.modify_money(player_hand.bet) - elif self.player_hand.get_value() > delaer_hand.get_value: + elif player_hand.get_value() > dealer_hand.get_value(): self.player.modify_money(player_hand.bet * 2) def payout_blackjack(self, player_hand): diff --git a/blackjack/interface.py b/blackjack/interface.py index b0ba7ae..669c985 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -63,9 +63,14 @@ def play_hand(self): if len(self.game.player.hands) == 1: print("You bust!\n") break - elif len(self.game.player.hands) > current_hand + 1: + elif len(self.game.player.hands) == current_hand + 1: print("You bust!\n") - print("Dealer's turn!\n") + for hand in self.game.player.hands: + if hand.get_value() <= 21: + print("Dealer's turn!\n") + dealers_turn = True + else: + break else: print("You bust!\n") current_hand += 1 @@ -215,12 +220,12 @@ def execute_selection(self, selection, actions, current_hand): """Takes the users selected action and performs it""" if selection == "H" and actions["hit"]: new_card = self.game.deck.deal() - print("Received {}".format(new_card)) + print("\nReceived {}\n".format(new_card)) self.game.player.takes_hit(self.game.player.hands[current_hand], new_card) elif selection == "D" and actions["double"]: new_card = self.game.deck.deal() - print("Received {}".format(new_card)) + print("\nReceived {}\n".format(new_card)) self.game.player.doubles(self.game.player.hands[current_hand], new_card) elif selection == "P" and actions["split"]: diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 5490f70..b7cb220 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -77,6 +77,8 @@ def test_can_split_by_value(): new_game = Game(options, "Alan") hand = Hand(10, [Card("Q", "hearts"), Card("10", "diamonds")]) assert new_game.can_split(hand) + hand = Hand(10, [Card("A", "hearts"), Card("A", "diamonds")]) + assert new_game.can_split(hand) def test_can_double_normal(): @@ -190,7 +192,7 @@ def test_payout(): options = GameOptions() new_game = Game(options, "Alan") new_game.create_hands(10) - new_game.player.hands[0] = Hand(10,[Card("2", "spades"), + new_game.player.hands[0] = Hand(10,[Card("4", "spades"), Card("J", "clubs")]) new_game.dealer.hand = Hand(10,[Card("3", "spades"), Card("Q", "clubs")]) From 1a37950c6730c26f6ce2663346182404544a4354 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 17:17:41 -0500 Subject: [PATCH 37/50] Added feature to disallow doubling more than one hand after split. Also fixed bug that caused split hands where the second hand busted and first hand should have won to not pay out. --- README.md | 23 +++++++++++------------ blackjack/game.py | 5 +++-- blackjack/game_options.py | 4 ---- blackjack/interface.py | 4 ++-- blackjack/player.py | 4 ++++ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d2ba9c5..7be5ad6 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ dealer respectively. To Do: + - Assign class attributes + - Add method to evaluate the value of Card -+ - Add ASCII representations of suits ++ - Add Unicode representations of suits + - Add method for swapping value of Aces @@ -103,7 +103,7 @@ To Do: + - Add in advanced rule variations -* : The Game class will be the backbone of the game. It will store ++ : The Game class will be the backbone of the game. It will store the wager amount made by the player, created and distribute Hand objects to the Player and Dealer and will control game flow based on hand values held by the player and dealer. It will have methods to calculate @@ -116,13 +116,13 @@ To Do: + - Add list of Game attributes + - Add method to distribute Hands to Player and Dealer + - Add method to check the status of the game by resolving Hand values -* - Add method to distribute payouts to the player ++ - Add method to distribute payouts to the player + - Add method for determining available actions for the player + - Add method to retrieve the ruleset from GameOptions and create the Dealer based on currently selected rules. (Maybe in Blackjack class?) -* : The Run class will be the driver script for the game. ++
: The Run class will be the driver script for the game. It will control all of the program flow and interact with the Game and Interface Classes to start new hands, request menu displays, request output to the console from the Interface Class. This file will contain the @@ -130,22 +130,21 @@ main method for the game, and will be the script executed to start the game. To Do: -* - Plan overall program flow and determine best structure for this class -* - Add main method to initialize the game and start program flow ++ - Add main method to initialize the game and start program flow -* : The Interface class will contain all of the ASCII text that ++ : The Interface class will contain all of the ASCII text that will be used for in-game card representations, menus and methods for receiving input from the user. To Do: + - Design main menu -* - Design options menu -* - Add method for updating options based on user input from options menu -* - Add method for displaying current hands for the dealer and player to ++ - Design options menu ++ - Add method for updating options based on user input from options menu ++ - Add method for displaying current hands for the dealer and player to the console -* - Add method to display win/lose text after each hand -...more ++ - Add method to display win/lose text after each hand ++ - Add control game flow to the interface /////: Very unlikely, but this class would replace the diff --git a/blackjack/game.py b/blackjack/game.py index 38f9815..accaf5a 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -41,7 +41,7 @@ def can_double(self, hand): elif self.options.double_9_10: return 9 <= hand.get_value() < 11 and len(hand.cards) == 2 else: - return len(hand.cards) == 2 + return len(hand.cards) == 2 and not self.player.doubled def can_hit(self, hand): """Checks to see if the player has the option to hit. Only not @@ -124,7 +124,8 @@ def payout(self, player_hand, dealer_hand): self.player.modify_money(player_hand.bet) elif player_hand.get_value() == dealer_hand.get_value(): self.player.modify_money(player_hand.bet) - elif player_hand.get_value() > dealer_hand.get_value(): + elif (player_hand.get_value() > dealer_hand.get_value() or + dealer_hand.get_value() > 21): self.player.modify_money(player_hand.bet * 2) def payout_blackjack(self, player_hand): diff --git a/blackjack/game_options.py b/blackjack/game_options.py index d07b9f1..01371c7 100644 --- a/blackjack/game_options.py +++ b/blackjack/game_options.py @@ -25,7 +25,3 @@ def __init__(self): self.no_double_after_split = False self.double_9_10_11 = False self.double_9_10 = False - - -# Rules to implement: -# Early Surrender - NOT DONE diff --git a/blackjack/interface.py b/blackjack/interface.py index 669c985..c09eb28 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -45,7 +45,6 @@ def play_hand(self): print("\n"*80) current_hand = 0 dealers_turn = False - self.game.player.insured = False print("Dealing Cards {}\n".format(choice(icons))) if len(self.game.deck.cards) < 30: @@ -67,8 +66,9 @@ def play_hand(self): print("You bust!\n") for hand in self.game.player.hands: if hand.get_value() <= 21: - print("Dealer's turn!\n") dealers_turn = True + if dealers_turn: + print("Dealer's turn!\n") else: break else: diff --git a/blackjack/player.py b/blackjack/player.py index 512deba..6072a5a 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -18,6 +18,7 @@ def __init__(self, name): self.hands = [] self.money = 100 self.insured = False + self.doubled = False def buys_insurance(self): """Modifys the player's money if they choose to buy insurance when @@ -51,6 +52,7 @@ def doubles(self, hand, card): """Doubles the player's bet on the current hand.""" self.modify_money(-1*hand.bet) hand.bet *= 2 + self.doubled = True self.takes_hit(hand, card) def surrenders(self, hand): @@ -61,3 +63,5 @@ def surrenders(self, hand): def reset_player(self): """Resets the players list of controlled hands""" self.hands = [] + self.insured = False + self.doubled = False From 8cda2abee75db407906a40d0c7e7be25a69a8cb2 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 20:00:06 -0500 Subject: [PATCH 38/50] Updated the interface module - split the play_hand method into smaller methods for readability --- blackjack/interface.py | 147 +++++++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 64 deletions(-) diff --git a/blackjack/interface.py b/blackjack/interface.py index c09eb28..661214c 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -34,81 +34,83 @@ def play_game(self, game): if player_choice == "N" or self.game.player.money < 5: break - def play_hand(self): - """Hand loop. Continues until the end of the hand. Prints updates - about the status of the hand to the screen and makes method calls - to allow user actions.""" + def initialize_hand(self): + """Initializes the variables needed for the correct flow of the + hand""" print("\n"*80) bet = self.get_bet() + print("\n"*80) + self.game.player.reset_player() self.game.create_hands(bet) - print("\n"*80) - current_hand = 0 - dealers_turn = False - print("Dealing Cards {}\n".format(choice(icons))) + self.current_hand = 0 + self.dealers_turn = False + self.continue_hand = True if len(self.game.deck.cards) < 30: self.game.reshuffle() - while True: + def play_hand(self): + """Hand loop. Continues until the end of the hand. Prints updates + about the status of the hand to the screen and makes method calls + to allow user actions.""" + + self.initialize_hand() + print("Dealing Cards {}\n".format(choice(icons))) + + while self.continue_hand: + self.print_hands() - self.print_hands(current_hand) if self.resolve_blackjacks(): break - selection, actions = self.offer_actions(current_hand) - self.execute_selection(selection, actions, current_hand) - if self.game.check_bust(self.game.player.hands[current_hand]): - if len(self.game.player.hands) == 1: - print("You bust!\n") - break - elif len(self.game.player.hands) == current_hand + 1: - print("You bust!\n") - for hand in self.game.player.hands: - if hand.get_value() <= 21: - dealers_turn = True - if dealers_turn: - print("Dealer's turn!\n") - else: - break - else: - print("You bust!\n") - current_hand += 1 - elif selection == "R": - print("You surrendered...\n") - break - elif selection == "S" or selection == "D": - if len(self.game.player.hands) > current_hand + 1: - current_hand += 1 - else: - print("Dealer's turn!\n") - dealers_turn = True + selection, actions = self.offer_actions() + self.execute_selection(selection, actions) + + dealers_turn = self.players_turn(selection) if dealers_turn: + print("Dealer's turn!") need_to_compare = self.dealer_play() if need_to_compare: self.evaluate_hands() - break + self.continue_hand = False else: - break + self.continue_hand = False + + def players_turn(self, selection): + """Allows the player to make game selections and determines if the + dealer will need to take a turn""" + dealers_turn = False + if self.game.check_bust(self.game.player.hands[self.current_hand]): + if len(self.game.player.hands) == 1: + print("You bust!\n") + self.continue_hand = False + return False + elif len(self.game.player.hands) == self.current_hand + 1: + print("You bust!\n") + for hand in self.game.player.hands: + if hand.get_value() <= 21: + dealers_turn = True + return dealers_turn + else: + print("You bust!\n") + self.current_hand += 1 + elif selection == "R": + print("You surrendered...\n") + self.continue_hand = False + return False + elif selection == "S" or selection == "D": + if len(self.game.player.hands) > self.current_hand + 1: + self.current_hand += 1 + else: + return True def evaluate_hands(self): """Evaluates the final hands of the player and dealer and makes the appropriate calls the the playout method of the Game class""" if len(self.game.player.hands) > 1: - for hand in enumerate(self.game.player.hands): - if self.game.check_bust(hand[1]): - print("Hand {} busted.".format(hand[0]+1)) - elif self.game.check_push(hand[1], self.game.dealer.hand): - print("Push on hand {}.".format(hand[0]+1)) - self.game.payout(hand[1], self.game.dealer.hand) - else: - if self.game.compare_hands(hand[1], self.game.dealer.hand): - print("Hand {} wins!".format(hand[0]+1)) - self.game.payout(hand[1], self.game.dealer.hand) - else: - print("Hand {} loses!".format(hand[0]+1)) - self.print_dealer_hand() + self.evaluate_split_hands() elif self.game.check_push(self.game.player.hands[0], self.game.dealer.hand): self.game.payout(self.game.player.hands[0], @@ -122,11 +124,26 @@ def evaluate_hands(self): self.game.payout(self.game.player.hands[0], self.game.dealer.hand) self.print_dealer_hand() - else: print("Dealer wins.\n") self.print_dealer_hand() + def evaluate_split_hands(self): + """Method for evaluating multiple hands after a split.""" + for hand in enumerate(self.game.player.hands): + if self.game.check_bust(hand[1]): + print("Hand {} busted.".format(hand[0]+1)) + elif self.game.check_push(hand[1], self.game.dealer.hand): + print("Push on hand {}.".format(hand[0]+1)) + self.game.payout(hand[1], self.game.dealer.hand) + else: + if self.game.compare_hands(hand[1], self.game.dealer.hand): + print("Hand {} wins!".format(hand[0]+1)) + self.game.payout(hand[1], self.game.dealer.hand) + else: + print("Hand {} loses!".format(hand[0]+1)) + self.print_dealer_hand() + def dealer_play(self): """Dealer's play method. Goes through all dealer turns until the hit() method of dealer determines it needs to stand or the dealer @@ -200,7 +217,7 @@ def check_for_player_blackjack(self): else: return False - def print_hands(self, current_hand): + def print_hands(self): """Displays the dealer's and player's hands to the screen""" print("Dealer's Hand: [{}, [X]]".format( self.game.dealer.get_show_card())) @@ -209,37 +226,39 @@ def print_hands(self, current_hand): else ""), end="") card_string = "" for hand in enumerate(self.game.player.hands): - card_string += "{}{} ".format("*" if hand[0] == current_hand else - "", + card_string += "{}{} ".format("*" if hand[0] == self.current_hand + else "", hand[1].get_card_strings(), end="") print(card_string) print("\nMoney: {}".format(self.game.player.money)) - def execute_selection(self, selection, actions, current_hand): + def execute_selection(self, selection, actions): """Takes the users selected action and performs it""" if selection == "H" and actions["hit"]: new_card = self.game.deck.deal() print("\nReceived {}\n".format(new_card)) - self.game.player.takes_hit(self.game.player.hands[current_hand], - new_card) + self.game.player.takes_hit(self.game.player.hands[ + self.current_hand], + new_card) elif selection == "D" and actions["double"]: new_card = self.game.deck.deal() print("\nReceived {}\n".format(new_card)) - self.game.player.doubles(self.game.player.hands[current_hand], + self.game.player.doubles(self.game.player.hands[self.current_hand], new_card) elif selection == "P" and actions["split"]: new_cards = [self.game.deck.deal(), self.game.deck.deal()] - self.game.player.splits(self.game.player.hands[current_hand], + self.game.player.splits(self.game.player.hands[self.current_hand], new_cards) elif selection == "R" and actions["surrender"]: - self.game.player.surrenders(self.game.player.hands[current_hand]) + self.game.player.surrenders(self.game.player.hands[ + self.current_hand]) - def offer_actions(self, current_hand): + def offer_actions(self): """Prints available actions to the screen and allows user to input their selected action.""" actions = self.game.get_available_actions( - self.game.player.hands[current_hand]) + self.game.player.hands[self.current_hand]) valid_selection_made = False while not valid_selection_made: valid_input = ["S"] From b8d1ba12142e6558e786ef8957101fd7b6a59995 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sat, 24 Jan 2015 20:14:02 -0500 Subject: [PATCH 39/50] Fixed pep8 errors. --- blackjack/game.py | 7 +++---- blackjack/game_options.py | 1 - blackjack/hand.py | 3 +-- blackjack/interface.py | 35 ++++++++++++++++++---------------- blackjack/player.py | 4 +++- blackjack/tests/test_dealer.py | 11 +++++++---- blackjack/tests/test_game.py | 35 +++++++++++++++++++--------------- blackjack/tests/test_hand.py | 1 + 8 files changed, 54 insertions(+), 43 deletions(-) diff --git a/blackjack/game.py b/blackjack/game.py index accaf5a..6d889cf 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -23,7 +23,6 @@ class Game: game and initialize the dealer appropriately * Receives wager from player and transfers winnings to player""" - def __init__(self, options, name): self.deck = Deck(options.number_of_decks) self.player = Player(name) @@ -48,7 +47,7 @@ def can_hit(self, hand): available if hitting split aces is disabled and player has split aces""" if not self.options.hit_split_aces and (len(self.player.hands) > 1 - and hand.cards[0].rank == "A"): + and hand.cards[0].rank == "A"): return False else: return True @@ -58,7 +57,7 @@ def can_split(self, hand): if not self.options.resplitting and len(self.player.hands) > 1: return False elif not self.options.resplit_aces and (len(self.player.hands) > 1 - and "A" in hand.get_ranks()): + and "A" in hand.get_ranks()): return False elif self.options.split_by_rank: return hand.get_ranks()[0] == hand.get_ranks()[1] @@ -100,7 +99,7 @@ def check_push(self, player_hand, dealer_hand): results in a push state""" return player_hand.get_value() == dealer_hand.get_value() - def compare_hands(self, player_hand, dealer_hand ): + def compare_hands(self, player_hand, dealer_hand): """Compares the player and dealer hand to resolve the winner""" return player_hand.get_value() > dealer_hand.get_value() diff --git a/blackjack/game_options.py b/blackjack/game_options.py index 01371c7..907983e 100644 --- a/blackjack/game_options.py +++ b/blackjack/game_options.py @@ -12,7 +12,6 @@ class GameOptions: * Rules are passed to the game during initialization """ - def __init__(self): self.number_of_decks = 1 self.hit_soft_17 = False diff --git a/blackjack/hand.py b/blackjack/hand.py index 32f771a..71722e5 100644 --- a/blackjack/hand.py +++ b/blackjack/hand.py @@ -15,7 +15,6 @@ class Hand: * Hand objects receive card objects from the deck. """ - def __init__(self, bet, cards=[]): self.cards = cards self.bet = bet @@ -49,7 +48,7 @@ def get_value(self): for card in self.cards: hand_value += card.value if ((hand_value > 21 and "A" in self.get_ranks()) or - hand_value <= 11 and "A" in self.get_ranks()): + hand_value <= 11 and "A" in self.get_ranks()): for card in self.cards: if hand_value > 21 and card.value == 11: card.swap_ace() diff --git a/blackjack/interface.py b/blackjack/interface.py index 661214c..53fa7a5 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -1,7 +1,10 @@ from game_options import GameOptions from random import choice + icons = ["♡", "♧", "♢", "♤"] + + class Interface: """ Interface class will provide all of the methods of interacting with the user. It will contain all of the methods for displaying to the @@ -29,7 +32,7 @@ def play_game(self, game): break player_choice = input("Play another hand? (Y/N) {} ".format( choice(icons))).upper() - if player_choice in ["Y","N"]: + if player_choice in ["Y", "N"]: break if player_choice == "N" or self.game.player.money < 5: break @@ -114,7 +117,7 @@ def evaluate_hands(self): elif self.game.check_push(self.game.player.hands[0], self.game.dealer.hand): self.game.payout(self.game.player.hands[0], - self.game.dealer.hand) + self.game.dealer.hand) print("Push.\n") self.print_dealer_hand() else: @@ -175,9 +178,9 @@ def resolve_blackjacks(self): if self.check_for_player_blackjack(): return True if (self.game.dealer.get_show_card().rank == "A" - and len(self.game.player.hands[0].cards) == 2 - and len(self.game.player.hands) == 1 - and self.game.player.money >= self.game.player.hands[0].bet): + and len(self.game.player.hands[0].cards) == 2 + and len(self.game.player.hands) == 1 + and self.game.player.money >= self.game.player.hands[0].bet): self.offer_insurance() if self.game.options.early_surrender: if self.offer_surrender(): @@ -202,13 +205,13 @@ def check_for_dealer_blackjack(self): def check_for_player_blackjack(self): """Checks to see if the player has blackjack""" if (self.game.player.hands[0].get_value() == 21 - and len(self.game.player.hands[0].cards) == 2 - and len(self.game.player.hands) == 1): + and len(self.game.player.hands[0].cards) == 2 + and len(self.game.player.hands) == 1): print("BLACKJACK!\n") if self.check_for_dealer_blackjack(): print("Dealer has BLACKJACK. Push...\n") self.game.payout(self.game.player.hands[0], - self.game.dealer.hand) + self.game.dealer.hand) return True else: print("Blackjack pays 3:2!\n") @@ -220,7 +223,7 @@ def check_for_player_blackjack(self): def print_hands(self): """Displays the dealer's and player's hands to the screen""" print("Dealer's Hand: [{}, [X]]".format( - self.game.dealer.get_show_card())) + self.game.dealer.get_show_card())) print("Player's Hand{}: ".format("s" if len(self.game.player.hands) > 1 else ""), end="") @@ -245,7 +248,7 @@ def execute_selection(self, selection, actions): new_card = self.game.deck.deal() print("\nReceived {}\n".format(new_card)) self.game.player.doubles(self.game.player.hands[self.current_hand], - new_card) + new_card) elif selection == "P" and actions["split"]: new_cards = [self.game.deck.deal(), self.game.deck.deal()] self.game.player.splits(self.game.player.hands[self.current_hand], @@ -258,7 +261,7 @@ def offer_actions(self): """Prints available actions to the screen and allows user to input their selected action.""" actions = self.game.get_available_actions( - self.game.player.hands[self.current_hand]) + self.game.player.hands[self.current_hand]) valid_selection_made = False while not valid_selection_made: valid_input = ["S"] @@ -274,7 +277,7 @@ def offer_actions(self): if actions["surrender"]: print("Surrende(R), ", end="") valid_input.append("R") - print ("(S)tand") + print("(S)tand") selection = input("{} ".format(choice(icons))).upper() valid_selection_made = selection in valid_input @@ -343,9 +346,9 @@ def get_bet(self): self.game.player.money)) bet = int(input("{} ".format(choice(icons)))) if (bet % 5 is not 0 - or bet < 0 - or bet > 20 - or bet > self.game.player.money): + or bet < 0 + or bet > 20 + or bet > self.game.player.money): raise ValueError else: return bet @@ -415,7 +418,7 @@ def options_menu_input(self, selection, options): if selection == "1": try: decks = int(input("Choose number of decks (8 max){} ".format( - choice(icons)))) + choice(icons)))) if decks < 1 or decks > 8: raise ValueError options.number_of_decks = decks diff --git a/blackjack/player.py b/blackjack/player.py index 6072a5a..d77fbf1 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -1,4 +1,6 @@ from hand import Hand + + class Player: """This is the player class, it contains a list of hands controlled by the player and keeps track of how much money the player has won. @@ -23,7 +25,7 @@ def __init__(self, name): def buys_insurance(self): """Modifys the player's money if they choose to buy insurance when offered""" - self.modify_money( int(-.5*self.hands[0].bet)) + self.modify_money(int(-.5*self.hands[0].bet)) self.insured = True def modify_money(self, value): diff --git a/blackjack/tests/test_dealer.py b/blackjack/tests/test_dealer.py index 8f157c1..c65ea29 100644 --- a/blackjack/tests/test_dealer.py +++ b/blackjack/tests/test_dealer.py @@ -11,7 +11,8 @@ def test_dealer_hit(): dealer.hand = hand - assert dealer.hit() == True + assert dealer.hit() + def test_dealer_does_not_hit(): hand = Hand(0, [Card("10", "clubs"), Card("7", "hearts")]) @@ -19,7 +20,8 @@ def test_dealer_does_not_hit(): dealer.hand = hand - assert dealer.hit() == False + assert not dealer.hit() + def test_dealer_hits_soft_17(): hand = Hand(0, [Card("6", "clubs"), Card("A", "hearts")]) @@ -27,10 +29,11 @@ def test_dealer_hits_soft_17(): dealer.hand = hand - assert dealer.hit() == True + assert dealer.hit() + def test_dealer_takes_hit(): options = GameOptions() new_game = Game(options, "Alan") - new_game.dealer.hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) + new_game.dealer.hand = Hand(10, [Card("2", "spades"), Card("J", "clubs")]) new_game.dealer.takes_hit(new_game.deck.deal()) diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index b7cb220..5cd6cbc 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -90,6 +90,7 @@ def test_can_double_normal(): hand.cards.append(Card("3", "diamonds")) assert not new_game.can_double(hand) + def test_can_double_9_10_11(): options = GameOptions() options.double_9_10_11 = True @@ -173,62 +174,66 @@ def test_split(): assert new_game.player.hands[1].cards[0].rank == "J" assert len(new_game.player.hands[1].cards) == 2 + def test_double_down(): options = GameOptions() new_game = Game(options, "Alan") - hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) + hand = Hand(10, [Card("2", "spades"), Card("J", "clubs")]) new_game.player.doubles(hand, new_game.deck.deal()) assert len(hand.cards) == 3 assert hand.bet == 20 assert new_game.player.money == 90 + def test_surrender(): options = GameOptions() new_game = Game(options, "Alan") - hand = Hand(10,[Card("2", "spades"), Card("J", "clubs")]) + hand = Hand(10, [Card("2", "spades"), Card("J", "clubs")]) new_game.player.surrenders(hand) + def test_payout(): options = GameOptions() new_game = Game(options, "Alan") new_game.create_hands(10) - new_game.player.hands[0] = Hand(10,[Card("4", "spades"), - Card("J", "clubs")]) - new_game.dealer.hand = Hand(10,[Card("3", "spades"), - Card("Q", "clubs")]) + new_game.player.hands[0] = Hand(10, [Card("4", "spades"), + Card("J", "clubs")]) + new_game.dealer.hand = Hand(10, [Card("3", "spades"), + Card("Q", "clubs")]) new_game.payout(new_game.player.hands[0], new_game.dealer.hand) assert new_game.player.money == 110 new_game.create_hands(10) - new_game.player.hands[0] = Hand(10,[Card("A", "spades"), - Card("J", "clubs")]) + new_game.player.hands[0] = Hand(10, [Card("A", "spades"), + Card("J", "clubs")]) new_game.payout_blackjack(new_game.player.hands[0]) assert new_game.player.money == 125 new_game.create_hands(10) assert new_game.player.money == 115 - new_game.player.hands[0] = Hand(10,[Card("2", "spades"), - Card("J", "clubs")]) - new_game.dealer.hand = Hand(0,[Card("A", "spades"), + new_game.player.hands[0] = Hand(10, [Card("2", "spades"), + Card("J", "clubs")]) + new_game.dealer.hand = Hand(0, [Card("A", "spades"), Card("J", "clubs")]) new_game.player.insured = True assert new_game.dealer.hand.get_value() == 21 new_game.payout(new_game.player.hands[0], new_game.dealer.hand) assert new_game.player.money == 125 + def test_get_available_actions(): options = GameOptions() new_game = Game(options, "Alan") - new_game.player.hands.append(Hand(10,[Card("2", "spades"), - Card("J", "clubs")])) + new_game.player.hands.append(Hand(10, [Card("2", "spades"), + Card("J", "clubs")])) new_game.dealer.hand = Hand(0, [Card("J", "spades"), Card("7", "Clubs")]) actions = new_game.get_available_actions(new_game.player.hands[0]) assert actions["hit"] assert actions["double"] assert not actions["split"] assert actions["surrender"] - new_game.player.hands[0] = Hand(10,[Card("J", "spades"), - Card("J", "clubs")]) + new_game.player.hands[0] = Hand(10, [Card("J", "spades"), + Card("J", "clubs")]) new_game.dealer.hand = Hand(0, [Card("A", "spades"), Card("7", "Clubs")]) actions = new_game.get_available_actions(new_game.player.hands[0]) diff --git a/blackjack/tests/test_hand.py b/blackjack/tests/test_hand.py index 04ee95a..3261685 100644 --- a/blackjack/tests/test_hand.py +++ b/blackjack/tests/test_hand.py @@ -58,6 +58,7 @@ def test_ace_value_incorrect(): assert hand.get_value() == 20 + def test_blackjack(): test_cards = [] test_cards.append(Card("A", "spades")) From 6c71598f5c63eb8c06995f77a650d32289493f00 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sun, 25 Jan 2015 12:40:29 -0500 Subject: [PATCH 40/50] Added save feature. --- blackjack/interface.py | 396 +++++++++++++++++++++++------------------ blackjack/main.py | 7 +- blackjack/player.py | 51 ++++++ 3 files changed, 275 insertions(+), 179 deletions(-) diff --git a/blackjack/interface.py b/blackjack/interface.py index 53fa7a5..c9b0a96 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -1,3 +1,4 @@ +import pickle from game_options import GameOptions from random import choice @@ -11,31 +12,112 @@ class Interface: console as well as receiving and checking input from the player. Responsibilities: - * Outputting game information and menus to the player + * Output game information and menus to the player * Receiving input from the player + * Control game flow """ def __init__(self): self.game = None - def play_game(self, game): - """Starts each hand an keeps the game going. Offers the user the - choice to play another hand or not.""" - self.game = game + def check_for_dealer_blackjack(self): + """Checks the dealer's hand for blackjack""" + return self.game.dealer.hand.get_value() == 21 - while self.game.player.money >= 5: - self.play_hand() - while True: - if self.game.player.money < 5: - print("You don't have enough money to play!") - input() - break - player_choice = input("Play another hand? (Y/N) {} ".format( - choice(icons))).upper() - if player_choice in ["Y", "N"]: - break - if player_choice == "N" or self.game.player.money < 5: - break + def check_for_player_blackjack(self): + """Checks to see if the player has blackjack""" + if (self.game.player.hands[0].get_value() == 21 + and len(self.game.player.hands[0].cards) == 2 + and len(self.game.player.hands) == 1): + print("BLACKJACK!\n") + if self.check_for_dealer_blackjack(): + print("Dealer has BLACKJACK. Push...\n") + self.game.payout(self.game.player.hands[0], + self.game.dealer.hand) + return True + else: + print("Blackjack pays 3:2!\n") + self.game.payout_blackjack(self.game.player.hands[0]) + return True + else: + return False + + def dealer_play(self): + """Dealer's play method. Goes through all dealer turns until the + hit() method of dealer determines it needs to stand or the dealer + busts""" + while self.game.dealer.hit(): + new_card = self.game.deck.deal() + print("Dealer hits, and recieves {}.\n".format(str(new_card))) + self.game.dealer.takes_hit(new_card) + + if self.game.check_bust(self.game.dealer.hand): + print("Dealer busts!\n") + for hand in self.game.player.hands: + if hand.get_value() <= 21: + self.game.payout(hand, self.game.dealer.hand) + return False + else: + return True + + def evaluate_hands(self): + """Evaluates the final hands of the player and dealer and makes + the appropriate calls the the playout method of the Game class""" + if len(self.game.player.hands) > 1: + self.evaluate_split_hands() + elif self.game.check_push(self.game.player.hands[0], + self.game.dealer.hand): + self.game.payout(self.game.player.hands[0], + self.game.dealer.hand) + print("Push.\n") + self.print_dealer_hand() + else: + if self.game.compare_hands(self.game.player.hands[0], + self.game.dealer.hand): + print("You win!\n") + self.game.payout(self.game.player.hands[0], + self.game.dealer.hand) + self.print_dealer_hand() + else: + print("Dealer wins.\n") + self.print_dealer_hand() + + def evaluate_split_hands(self): + """Method for evaluating multiple hands after a split.""" + for hand in enumerate(self.game.player.hands): + if self.game.check_bust(hand[1]): + print("Hand {} busted.".format(hand[0]+1)) + elif self.game.check_push(hand[1], self.game.dealer.hand): + print("Push on hand {}.".format(hand[0]+1)) + self.game.payout(hand[1], self.game.dealer.hand) + else: + if self.game.compare_hands(hand[1], self.game.dealer.hand): + print("Hand {} wins!".format(hand[0]+1)) + self.game.payout(hand[1], self.game.dealer.hand) + else: + print("Hand {} loses!".format(hand[0]+1)) + self.print_dealer_hand() + + def execute_selection(self, selection, actions): + """Takes the users selected action and performs it""" + if selection == "H" and actions["hit"]: + new_card = self.game.deck.deal() + print("\nReceived {}\n".format(new_card)) + self.game.player.takes_hit(self.game.player.hands[ + self.current_hand], + new_card) + elif selection == "D" and actions["double"]: + new_card = self.game.deck.deal() + print("\nReceived {}\n".format(new_card)) + self.game.player.doubles(self.game.player.hands[self.current_hand], + new_card) + elif selection == "P" and actions["split"]: + new_cards = [self.game.deck.deal(), self.game.deck.deal()] + self.game.player.splits(self.game.player.hands[self.current_hand], + new_cards) + elif selection == "R" and actions["surrender"]: + self.game.player.surrenders(self.game.player.hands[ + self.current_hand]) def initialize_hand(self): """Initializes the variables needed for the correct flow of the @@ -53,6 +135,74 @@ def initialize_hand(self): if len(self.game.deck.cards) < 30: self.game.reshuffle() + def offer_actions(self): + """Prints available actions to the screen and allows user to input + their selected action.""" + actions = self.game.get_available_actions( + self.game.player.hands[self.current_hand]) + valid_selection_made = False + while not valid_selection_made: + valid_input = ["S"] + if actions["hit"]: + print("(H)it, ", end="") + valid_input.append("H") + if actions["double"]: + print("(D)ouble Down, ", end="") + valid_input.append("D") + if actions["split"]: + print("S(P)lit, ", end="") + valid_input.append("P") + if actions["surrender"]: + print("Surrende(R), ", end="") + valid_input.append("R") + print("(S)tand") + selection = input("{} ".format(choice(icons))).upper() + valid_selection_made = selection in valid_input + + return selection, actions + + def offer_insurance(self): + """Method that allows the user to buy insurace if the dealer's upcard + is an Ace""" + print("Dealer has an Ace showing. Buy insurance?") + while True: + player_choice = input("(Y/N) {} ".format(choice(icons))).upper() + if player_choice == "Y": + self.game.player.buys_insurance() + break + elif player_choice == "N": + break + else: + print("Please enter Y or N.") + + def play_game(self, game): + """Starts each hand an keeps the game going. Offers the user the + choice to play another hand or not.""" + self.game = game + + while self.game.player.money >= 5: + self.play_hand() + while True: + if self.game.player.money < 5: + print("You don't have enough money to play!") + input() + break + player_choice = input("Play another hand? (Y/N) {} ".format( + choice(icons))).upper() + if player_choice in ["Y", "N"]: + break + if player_choice == "N" and self.game.player.money > 5: + save_choice = "" + while save_choice not in ["Y", "N"]: + save_choice = input( + "Would you like to save? (Y/N) {} ".format( + choice(icons))).upper() + if save_choice == "Y": + self.game.player.save_player_state(self.game.options) + break + else: + break + def play_hand(self): """Hand loop. Continues until the end of the hand. Prints updates about the status of the hand to the screen and makes method calls @@ -109,67 +259,27 @@ def players_turn(self, selection): else: return True - def evaluate_hands(self): - """Evaluates the final hands of the player and dealer and makes - the appropriate calls the the playout method of the Game class""" - if len(self.game.player.hands) > 1: - self.evaluate_split_hands() - elif self.game.check_push(self.game.player.hands[0], - self.game.dealer.hand): - self.game.payout(self.game.player.hands[0], - self.game.dealer.hand) - print("Push.\n") - self.print_dealer_hand() - else: - if self.game.compare_hands(self.game.player.hands[0], - self.game.dealer.hand): - print("You win!\n") - self.game.payout(self.game.player.hands[0], - self.game.dealer.hand) - self.print_dealer_hand() - else: - print("Dealer wins.\n") - self.print_dealer_hand() - - def evaluate_split_hands(self): - """Method for evaluating multiple hands after a split.""" - for hand in enumerate(self.game.player.hands): - if self.game.check_bust(hand[1]): - print("Hand {} busted.".format(hand[0]+1)) - elif self.game.check_push(hand[1], self.game.dealer.hand): - print("Push on hand {}.".format(hand[0]+1)) - self.game.payout(hand[1], self.game.dealer.hand) - else: - if self.game.compare_hands(hand[1], self.game.dealer.hand): - print("Hand {} wins!".format(hand[0]+1)) - self.game.payout(hand[1], self.game.dealer.hand) - else: - print("Hand {} loses!".format(hand[0]+1)) - self.print_dealer_hand() - - def dealer_play(self): - """Dealer's play method. Goes through all dealer turns until the - hit() method of dealer determines it needs to stand or the dealer - busts""" - while self.game.dealer.hit(): - new_card = self.game.deck.deal() - print("Dealer hits, and recieves {}.\n".format(str(new_card))) - self.game.dealer.takes_hit(new_card) - - if self.game.check_bust(self.game.dealer.hand): - print("Dealer busts!\n") - for hand in self.game.player.hands: - if hand.get_value() <= 21: - self.game.payout(hand, self.game.dealer.hand) - return False - else: - return True - def print_dealer_hand(self): """Prints the dealer's hand to the screen""" print("Dealer's final hand: {}\n".format( self.game.dealer.hand.get_card_strings())) + def print_hands(self): + """Displays the dealer's and player's hands to the screen""" + print("Dealer's Hand: [{}, [X]]".format( + self.game.dealer.get_show_card())) + + print("Player's Hand{}: ".format("s" if len(self.game.player.hands) > 1 + else ""), end="") + card_string = "" + for hand in enumerate(self.game.player.hands): + card_string += "{}{} ".format("*" if hand[0] == self.current_hand + else "", + hand[1].get_card_strings(), end="") + + print(card_string) + print("\nMoney: {}".format(self.game.player.money)) + def resolve_blackjacks(self): """Resolves all cases of either or both the player and dealer having blackjack and makes appropriate payouts. Also calls the offer @@ -198,105 +308,6 @@ def resolve_blackjacks(self): return False - def check_for_dealer_blackjack(self): - """Checks the dealer's hand for blackjack""" - return self.game.dealer.hand.get_value() == 21 - - def check_for_player_blackjack(self): - """Checks to see if the player has blackjack""" - if (self.game.player.hands[0].get_value() == 21 - and len(self.game.player.hands[0].cards) == 2 - and len(self.game.player.hands) == 1): - print("BLACKJACK!\n") - if self.check_for_dealer_blackjack(): - print("Dealer has BLACKJACK. Push...\n") - self.game.payout(self.game.player.hands[0], - self.game.dealer.hand) - return True - else: - print("Blackjack pays 3:2!\n") - self.game.payout_blackjack(self.game.player.hands[0]) - return True - else: - return False - - def print_hands(self): - """Displays the dealer's and player's hands to the screen""" - print("Dealer's Hand: [{}, [X]]".format( - self.game.dealer.get_show_card())) - - print("Player's Hand{}: ".format("s" if len(self.game.player.hands) > 1 - else ""), end="") - card_string = "" - for hand in enumerate(self.game.player.hands): - card_string += "{}{} ".format("*" if hand[0] == self.current_hand - else "", - hand[1].get_card_strings(), end="") - - print(card_string) - print("\nMoney: {}".format(self.game.player.money)) - - def execute_selection(self, selection, actions): - """Takes the users selected action and performs it""" - if selection == "H" and actions["hit"]: - new_card = self.game.deck.deal() - print("\nReceived {}\n".format(new_card)) - self.game.player.takes_hit(self.game.player.hands[ - self.current_hand], - new_card) - elif selection == "D" and actions["double"]: - new_card = self.game.deck.deal() - print("\nReceived {}\n".format(new_card)) - self.game.player.doubles(self.game.player.hands[self.current_hand], - new_card) - elif selection == "P" and actions["split"]: - new_cards = [self.game.deck.deal(), self.game.deck.deal()] - self.game.player.splits(self.game.player.hands[self.current_hand], - new_cards) - elif selection == "R" and actions["surrender"]: - self.game.player.surrenders(self.game.player.hands[ - self.current_hand]) - - def offer_actions(self): - """Prints available actions to the screen and allows user to input - their selected action.""" - actions = self.game.get_available_actions( - self.game.player.hands[self.current_hand]) - valid_selection_made = False - while not valid_selection_made: - valid_input = ["S"] - if actions["hit"]: - print("(H)it, ", end="") - valid_input.append("H") - if actions["double"]: - print("(D)ouble Down, ", end="") - valid_input.append("D") - if actions["split"]: - print("S(P)lit, ", end="") - valid_input.append("P") - if actions["surrender"]: - print("Surrende(R), ", end="") - valid_input.append("R") - print("(S)tand") - selection = input("{} ".format(choice(icons))).upper() - valid_selection_made = selection in valid_input - - return selection, actions - - def offer_insurance(self): - """Method that allows the user to buy insurace if the dealer's upcard - is an Ace""" - print("Dealer has an Ace showing. Buy insurance?") - while True: - player_choice = input("(Y/N) {} ".format(choice(icons))).upper() - if player_choice == "Y": - self.game.player.buys_insurance() - break - elif player_choice == "N": - break - else: - print("Please enter Y or N.") - def main_menu(self): """ Displays the title, programmer info, and main menu for the game. Takes user input and returns to the calling Game class""" @@ -326,9 +337,10 @@ def main_menu(self): print("\n", menu_text) print(info) print("Main Menu:") - print("♡ 1 - Play Blackjack!") - print("♧ 2 - Set Game Options") - print("♢ 3 - Quit") + print("♡ 1 - New Game") + print("♤ 2 - Continue Game") + print("♧ 3 - Set Game Options") + print("♢ 4 - Quit") print("-----------------------") selection = input("{} ".format(choice(icons))) @@ -342,9 +354,14 @@ def get_bet(self): print("How much would you like to wager on the next hand? ") print("Please wager in multiples of 5. Maximum bet is 20.") try: + print("Type \"O\" to go to the options menu.") print("You currently have {} dollars.".format( self.game.player.money)) - bet = int(input("{} ".format(choice(icons)))) + bet = input("{} ".format(choice(icons))) + if bet.upper() == "O": + self.options_menu(self.game.options) + continue + bet = int(bet) if (bet % 5 is not 0 or bet < 0 or bet > 20 @@ -403,7 +420,7 @@ def options_menu(self, options): options.double_9_10 else "Disabled")) print("R - Reset to Defaults") - print("Q - Return to Main Menu") + print("Q - Return to Previous Screen") selection = input("{} ".format(choice(icons))) if selection.upper() == "Q": @@ -457,3 +474,26 @@ def options_menu_input(self, selection, options): options = GameOptions() return options + + def load_game_menu(self, game): + try: + with open("saves.dat", "rb") as save_file: + data = pickle.load(save_file) + for player in enumerate(data): + print("{} - {} ${}".format(player[0]+1, + player[1]["name"], + player[1]["money"])) + except FileNotFoundError: + print("No save games found!") + input() + return + while True: + try: + selection = int(input("Select player to load {} ".format( + choice(icons)))) - 1 + game.player.load_player_state(data[selection]["hash"], game) + break + except (ValueError, IndexError): + print("Invalid selection.") + + return game diff --git a/blackjack/main.py b/blackjack/main.py index c8a297b..34644b5 100644 --- a/blackjack/main.py +++ b/blackjack/main.py @@ -20,8 +20,13 @@ def main(self): game = Game(options, name) interface.play_game(game) elif selection == "2": - options = interface.options_menu(options) + game = Game(options, "None") + game = interface.load_game_menu(game) + if game: + interface.play_game(game) elif selection == "3": + options = interface.options_menu(options) + elif selection == "4": break diff --git a/blackjack/player.py b/blackjack/player.py index d77fbf1..7a66f2e 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -1,4 +1,7 @@ from hand import Hand +from datetime import datetime +import pickle +import hashlib class Player: @@ -21,6 +24,7 @@ def __init__(self, name): self.money = 100 self.insured = False self.doubled = False + self.player_hash = None def buys_insurance(self): """Modifys the player's money if they choose to buy insurance when @@ -67,3 +71,50 @@ def reset_player(self): self.hands = [] self.insured = False self.doubled = False + + def save_player_state(self, options): + """Saves the player's progress for later play""" + if not self.player_hash: + self.player_hash = hashlib.md5(bytes(str(datetime.now()), + "UTF-8")).hexdigest() + player_data = {"name": self.name, + "money": self.money, + "hash": self.player_hash, + "options": options + } + try: + with open("saves.dat", "rb") as read_file: + data = pickle.load(read_file) + for saved_player in data: + if self.player_hash == saved_player["hash"]: + saved_player["money"] = self.money + saved_player["options"] = options + break + else: + data.append(player_data) + + with open("saves.dat", "wb") as write_file: + pickle.dump(data, write_file) + + except FileNotFoundError: + with open("saves.dat", "xb") as write_file: + data = [{"name": self.name, + "money": self.money, + "hash": self.player_hash, + "options": options}] + pickle.dump(data, write_file) + + def load_player_state(self, player_hash, game): + """Loads the player's previous information from saved game file.""" + try: + with open("saves.dat", "rb") as read_file: + data = pickle.load(read_file) + for saved_player in data: + if player_hash == saved_player["hash"]: + self.name = saved_player["name"] + self.money = saved_player["money"] + self.player_hash = saved_player["hash"] + game.options = saved_player["options"] + break + except IOError: + print("Error loading game...Sorry.") From 08a7378e06c8b585b1b62a326886c4197eb344b7 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sun, 25 Jan 2015 14:34:01 -0500 Subject: [PATCH 41/50] Added some colors to the terminal display --- blackjack/card.py | 5 ++--- blackjack/interface.py | 38 +++++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/blackjack/card.py b/blackjack/card.py index a4a3722..9f33bd9 100644 --- a/blackjack/card.py +++ b/blackjack/card.py @@ -20,11 +20,10 @@ class Card: * Cards are collected into decks of varying sizes * Cards are distributed to player and dealer hands """ - suits = {"spades": "♤", - "hearts": "♡", + "hearts": "\033[.31m♡\033[.37m", "clubs": "♧", - "diamonds": "♢"} + "diamonds": "\033[.31m♢\033[.37m"} def __init__(self, rank, suit): self.rank = rank diff --git a/blackjack/interface.py b/blackjack/interface.py index c9b0a96..3c7cb81 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -3,7 +3,7 @@ from random import choice -icons = ["♡", "♧", "♢", "♤"] +icons = ["\033[.31m♡\033[.37m", "♧", "\033[.31m♢\033[.37m", "♤"] class Interface: @@ -311,19 +311,23 @@ def resolve_blackjacks(self): def main_menu(self): """ Displays the title, programmer info, and main menu for the game. Takes user input and returns to the calling Game class""" - title_text = ("\n" - ".------..------..------..------..------." - ".------..------..------..------.\n" - "|B.--. ||L.--. ||A.--. ||C.--. ||K.--. |" - "|J.--. ||A.--. ||C.--. ||K.--. |\n" - "| :(): || :/\: || (\/) || :/\: || :/\: |" - "| :(): || (\/) || :/\: || :/\: |\n" - "| ()() || (__) || :\/: || :\/: || :\/: |" - "| ()() || :\/: || :\/: || :\/: |\n" - "| '--'B|| '--'L|| '--'A|| '--'C|| '--'K|" - "| '--'J|| '--'A|| '--'C|| '--'K|\n" - "`------'`------'`------'`------'`------'" - "`------'`------'`------'`------'") + title_text = ( + ".------..------..------..------..------..------..------..------." + ".------.\n|B.--. ||L.--. ||\033[.31mA\033[.37m.--. ||\033[.31mC" + "\033[.37m.--. ||\033[.31mK\033[.37m.--. ||J.--. ||\033[.31mA" + "\033[.37m.--. ||\033[.31mC\033[.37m.--. ||\033[.31mK\033[.37m.--." + " |\n| :(): || :/\: || \033[.31m(\/)\033[.37m || :\033[.31m/" + "\\\033[.37m: || :\033[.31m/\\\033[.37m: || :(): || \033[.31m(\/" + ")\033[.37m || :\033[.31m/\\\033[.37m: || :\033[.31m/\\\033[.37m" + ": |\n| ()() || (__) || :\033[.31m\/\033[.37m: || :\033[.31m\/" + "\033[.37m: || :\033[.31m\/\033[.37m: || ()() || :\033[.31m\/" + "\033[.37m: || :\033[.31m\/\033[.37m: || :\033[.31m\/\033[.37m: " + "|\n| '--'B|| '--'L|| '--'\033[.31mA\033[.37m|| '--'\033[.31mC" + "\033[.37m|| '--'\033[.31mK\033[.37m|| '--'J|| '--'\033[.31mA" + "\033[.37m|| '--'\033[.31mC\033[.37m|| '--'\033[.31mK\033[.37m|\n" + "`------'`------'`------'`------'`------'`------'`------'`------'" + "`------'") + info = ("Programmed by: Alan Grissett\n" "The Iron Yard - Durham\n" @@ -337,10 +341,10 @@ def main_menu(self): print("\n", menu_text) print(info) print("Main Menu:") - print("♡ 1 - New Game") + print("\033[.31m♡\033[.37m 1 - New Game") print("♤ 2 - Continue Game") - print("♧ 3 - Set Game Options") - print("♢ 4 - Quit") + print("\033[.31m♢\033[.37m 3 - Set Game Options") + print("♧ 4 - Quit") print("-----------------------") selection = input("{} ".format(choice(icons))) From 0da59036f7451cbe0ffc5be1a71d3c9800c6aca6 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sun, 25 Jan 2015 15:33:02 -0500 Subject: [PATCH 42/50] Forgot to implement early surrender option into game. Done now. --- blackjack/interface.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/blackjack/interface.py b/blackjack/interface.py index 3c7cb81..86fb8c4 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -175,6 +175,19 @@ def offer_insurance(self): else: print("Please enter Y or N.") + def offer_surrender(self): + """Used on early surrender games. Gives the player the option to + surrender before the dealer checks for blackjack.""" + while True: + player_choice = input("Surrender? (Y/N) {} ".format( + choice(icons))).upper() + if player_choice == "Y": + return True + elif player_choice == "N": + return False + else: + print("Invalid input.") + def play_game(self, game): """Starts each hand an keeps the game going. Offers the user the choice to play another hand or not.""" @@ -287,17 +300,18 @@ def resolve_blackjacks(self): are resolved.""" if self.check_for_player_blackjack(): return True - if (self.game.dealer.get_show_card().rank == "A" + if self.game.options.early_surrender: + if self.offer_surrender(): + self.game.player.surrenders(self.game.player.hands[0]) + print("You surrendered.\n") + print("Dealer had: {}\n".format( + self.game.dealer.hand.get_card_strings())) + return True + elif (self.game.dealer.get_show_card().rank == "A" and len(self.game.player.hands[0].cards) == 2 and len(self.game.player.hands) == 1 and self.game.player.money >= self.game.player.hands[0].bet): - self.offer_insurance() - if self.game.options.early_surrender: - if self.offer_surrender(): - self.game.player.surrenders(self.game.player.hands[0]) - print("You surrendered.\n") - print("Dealer had: {}\n".format( - self.game.dealer.hand.get_card_strings())) + self.offer_insurance() return True if self.check_for_dealer_blackjack(): @@ -328,7 +342,6 @@ def main_menu(self): "`------'`------'`------'`------'`------'`------'`------'`------'" "`------'") - info = ("Programmed by: Alan Grissett\n" "The Iron Yard - Durham\n" "January 2015\n") From c151e46fc26db67807f08cc10986f9a2f7732e63 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Sun, 25 Jan 2015 21:00:58 -0500 Subject: [PATCH 43/50] Added requirements.txt --- README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index 7be5ad6..d993059 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ To Do: + - Add class attributes + - Add methods to increment or decrement remaining money + - Add name attribute -/ - Add method to save player state so the game can be resumed with current ++ - Add method to save player state so the game can be resumed with current money amount maintained @@ -145,13 +145,3 @@ To Do: the console + - Add method to display win/lose text after each hand + - Add control game flow to the interface - - -/////: Very unlikely, but this class would replace the -Interface class to provide a graphical game experience. The interface -would allow for mouse input or keyboard input corresponding to available -menu options or avaiable actions within the game. The rest of the -functionality would be identical to interface. - -To Do: -* - The rest of the project From 13254bfa9cf5634e38d2a422eee785802a9a35fd Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Mon, 26 Jan 2015 07:22:26 -0500 Subject: [PATCH 44/50] Fixed bug that caused insurance to end the hand without reporting results regardless of whether the dealer had blackjack. --- blackjack/interface.py | 1 - requirements.txt | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 requirements.txt diff --git a/blackjack/interface.py b/blackjack/interface.py index 86fb8c4..e6db4d6 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -312,7 +312,6 @@ def resolve_blackjacks(self): and len(self.game.player.hands) == 1 and self.game.player.money >= self.game.player.hands[0].bet): self.offer_insurance() - return True if self.check_for_dealer_blackjack(): print("Dealer has blackjack!\n") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a1dac2f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pytest 2.6.4 From f66604d2b739575933931b7dc53dbc99e85124c1 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Mon, 26 Jan 2015 07:23:37 -0500 Subject: [PATCH 45/50] Restructured files to use __main__ from the root blackjack directory to launch the game. --- blackjack/main.py | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 blackjack/main.py diff --git a/blackjack/main.py b/blackjack/main.py deleted file mode 100644 index 34644b5..0000000 --- a/blackjack/main.py +++ /dev/null @@ -1,35 +0,0 @@ -from game import Game -from game_options import GameOptions -from interface import Interface - - -class Main: - - def main(self): - """This is the launching method for the game. It displays the main menu, - receives menu selection and launches the options menu and the game when - prompted.""" - interface = Interface() - options = GameOptions() - - while True: - selection = 0 - selection = interface.main_menu() - if selection == "1": - name = interface.get_name() - game = Game(options, name) - interface.play_game(game) - elif selection == "2": - game = Game(options, "None") - game = interface.load_game_menu(game) - if game: - interface.play_game(game) - elif selection == "3": - options = interface.options_menu(options) - elif selection == "4": - break - - -if __name__ == '__main__': - - Main().main() From 3e5704f45257caf474901d0bd869d78065f5c8f8 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Mon, 26 Jan 2015 07:24:24 -0500 Subject: [PATCH 46/50] Adding __main__ to git repository. --- __init__.py | 0 __main__.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 __init__.py create mode 100644 __main__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/__main__.py b/__main__.py new file mode 100644 index 0000000..95e04c0 --- /dev/null +++ b/__main__.py @@ -0,0 +1,35 @@ +from blackjack.game import Game +from blackjack.game_options import GameOptions +from blackjack.interface import Interface + + +class Main: + + def main(self): + """This is the launching method for the game. It displays the main menu, + receives menu selection and launches the options menu and the game when + prompted.""" + interface = Interface() + options = GameOptions() + + while True: + selection = 0 + selection = interface.main_menu() + if selection == "1": + name = interface.get_name() + game = Game(options, name) + interface.play_game(game) + elif selection == "2": + game = Game(options, "None") + game = interface.load_game_menu(game) + if game: + interface.play_game(game) + elif selection == "3": + options = interface.options_menu(options) + elif selection == "4": + break + + +if __name__ == '__main__': + + Main().main() From 398c340d69caf736127895e4ee922a5c5003cac9 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Mon, 26 Jan 2015 07:35:14 -0500 Subject: [PATCH 47/50] Updated file structure once again to allow running python -m blackjack from the main blackjack directory. Added test-requirements.txt file and a run-instructions.txt file --- __init__.py | 0 __main__.py => blackjack/__main__.py | 0 blackjack/deck.py | 2 +- blackjack/game.py | 8 ++++---- blackjack/interface.py | 2 +- blackjack/player.py | 2 +- blackjack/tests/test_card.py | 2 +- blackjack/tests/test_dealer.py | 10 +++++----- blackjack/tests/test_deck.py | 4 ++-- blackjack/tests/test_game.py | 8 ++++---- blackjack/tests/test_hand.py | 4 ++-- blackjack/tests/test_player.py | 2 +- requirements.txt | 1 - run-instructions.txt | 1 + test-requirements.txt | 1 + 15 files changed, 24 insertions(+), 23 deletions(-) delete mode 100644 __init__.py rename __main__.py => blackjack/__main__.py (100%) create mode 100644 run-instructions.txt create mode 100644 test-requirements.txt diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/__main__.py b/blackjack/__main__.py similarity index 100% rename from __main__.py rename to blackjack/__main__.py diff --git a/blackjack/deck.py b/blackjack/deck.py index 4e19d02..5285c10 100644 --- a/blackjack/deck.py +++ b/blackjack/deck.py @@ -1,5 +1,5 @@ from random import shuffle -from card import Card, ranks, suits +from blackjack.card import Card, ranks, suits class Deck: diff --git a/blackjack/game.py b/blackjack/game.py index 6d889cf..15a4d33 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -1,7 +1,7 @@ -from deck import Deck -from hand import Hand -from player import Player -from dealer import Dealer +from blackjack.deck import Deck +from blackjack.hand import Hand +from blackjack.player import Player +from blackjack.dealer import Dealer class Game: diff --git a/blackjack/interface.py b/blackjack/interface.py index e6db4d6..ac3f2b8 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -1,5 +1,5 @@ import pickle -from game_options import GameOptions +from blackjack.game_options import GameOptions from random import choice diff --git a/blackjack/player.py b/blackjack/player.py index 7a66f2e..230a1c8 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -1,4 +1,4 @@ -from hand import Hand +from blackjack.hand import Hand from datetime import datetime import pickle import hashlib diff --git a/blackjack/tests/test_card.py b/blackjack/tests/test_card.py index 9746a68..af13b99 100644 --- a/blackjack/tests/test_card.py +++ b/blackjack/tests/test_card.py @@ -1,4 +1,4 @@ -from card import Card +from blackjack.card import Card """Test functions for the card class""" diff --git a/blackjack/tests/test_dealer.py b/blackjack/tests/test_dealer.py index c65ea29..5f39fd1 100644 --- a/blackjack/tests/test_dealer.py +++ b/blackjack/tests/test_dealer.py @@ -1,8 +1,8 @@ -from dealer import Dealer -from card import Card -from hand import Hand -from game_options import GameOptions -from game import Game +from blackjack.dealer import Dealer +from blackjack.card import Card +from blackjack.hand import Hand +from blackjack.game_options import GameOptions +from blackjack.game import Game def test_dealer_hit(): diff --git a/blackjack/tests/test_deck.py b/blackjack/tests/test_deck.py index c2f8884..efa6411 100644 --- a/blackjack/tests/test_deck.py +++ b/blackjack/tests/test_deck.py @@ -1,5 +1,5 @@ -from deck import Deck -from card import Card +from blackjack.deck import Deck +from blackjack.card import Card def test_deck_length(): diff --git a/blackjack/tests/test_game.py b/blackjack/tests/test_game.py index 5cd6cbc..1196c5e 100644 --- a/blackjack/tests/test_game.py +++ b/blackjack/tests/test_game.py @@ -1,7 +1,7 @@ -from card import Card -from game_options import GameOptions -from game import Game -from hand import Hand +from blackjack.card import Card +from blackjack.game_options import GameOptions +from blackjack.game import Game +from blackjack.hand import Hand def test_create_hands(): diff --git a/blackjack/tests/test_hand.py b/blackjack/tests/test_hand.py index 3261685..94804c8 100644 --- a/blackjack/tests/test_hand.py +++ b/blackjack/tests/test_hand.py @@ -1,5 +1,5 @@ -from hand import Hand -from card import Card +from blackjack.hand import Hand +from blackjack.card import Card def test_card_list_holds_cards(): diff --git a/blackjack/tests/test_player.py b/blackjack/tests/test_player.py index 3a8c19d..e8bf1df 100644 --- a/blackjack/tests/test_player.py +++ b/blackjack/tests/test_player.py @@ -1,4 +1,4 @@ -from player import Player +from blackjack.player import Player def test_player_creation(): diff --git a/requirements.txt b/requirements.txt index a1dac2f..e69de29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +0,0 @@ -pytest 2.6.4 diff --git a/run-instructions.txt b/run-instructions.txt new file mode 100644 index 0000000..3e4fd86 --- /dev/null +++ b/run-instructions.txt @@ -0,0 +1 @@ +To run the game use the command "python -m blackjack" from the main blackjack directory. diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..8a08d48 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1 @@ +pytest=2.6.4 From 618f98eef7f26eca4df6bdf28781e59b85002818 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Wed, 28 Jan 2015 07:33:09 -0500 Subject: [PATCH 48/50] Updated a few things. Took out while True statements in places where I could. Moved a couple of deeper nested ifs into new functions. Changed all self.game.player.hand and self.game.dealer.hand references to the new game property self.game._hand --- README.md | 3 + blackjack/dealer.py | 4 ++ blackjack/deck.py | 3 + blackjack/game.py | 10 ++++ blackjack/interface.py | 131 ++++++++++++++++++++++------------------- blackjack/player.py | 3 + run-instructions.txt | 1 - 7 files changed, 93 insertions(+), 62 deletions(-) delete mode 100644 run-instructions.txt diff --git a/README.md b/README.md index d993059..85651f1 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ Python Development - Cohort 3 ? - Unsure if should be included in class \ - Planned But Low Priority ] + +Run Instructions: Run from the main blackjack directory using the command "python -m blackjack" + Blackjack Project The goal of this project is to complete a fully functional console based diff --git a/blackjack/dealer.py b/blackjack/dealer.py index d561cb0..0105e9f 100644 --- a/blackjack/dealer.py +++ b/blackjack/dealer.py @@ -29,6 +29,10 @@ def hit(self): else: return False + def get_dealer_hand(self): + """Returns the dealer's current hand object""" + return self.hand + def get_show_card(self): """Returns the dealer's show card for display in the game""" return self.hand.cards[0] diff --git a/blackjack/deck.py b/blackjack/deck.py index 5285c10..6a132e0 100644 --- a/blackjack/deck.py +++ b/blackjack/deck.py @@ -31,6 +31,9 @@ def __init__(self, decks=1): for deck in range(self.decks)] shuffle(self.cards) + def __len__(self): + return len(self.cards) + def deal(self): """Pops a single card from the end of the card list and returns it to the calling function""" diff --git a/blackjack/game.py b/blackjack/game.py index 15a4d33..b34220d 100644 --- a/blackjack/game.py +++ b/blackjack/game.py @@ -134,3 +134,13 @@ def payout_blackjack(self, player_hand): def reshuffle(self): """Creates a new deck for use by the game""" self.deck = Deck(self.options.number_of_decks) + + @property + def dealer_hand(self): + """Returns the dealer's hand object.""" + return self.dealer.get_dealer_hand() + + @property + def player_hands(self): + """Returns the list of players Hand objects.""" + return self.player.get_player_hands() diff --git a/blackjack/interface.py b/blackjack/interface.py index ac3f2b8..f9db5b5 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -22,22 +22,22 @@ def __init__(self): def check_for_dealer_blackjack(self): """Checks the dealer's hand for blackjack""" - return self.game.dealer.hand.get_value() == 21 + return self.game.dealer_hand.get_value() == 21 def check_for_player_blackjack(self): """Checks to see if the player has blackjack""" - if (self.game.player.hands[0].get_value() == 21 - and len(self.game.player.hands[0].cards) == 2 - and len(self.game.player.hands) == 1): + if (self.game.player_hands[0].get_value() == 21 + and len(self.game.player_hands[0].cards) == 2 + and len(self.game.player_hands) == 1): print("BLACKJACK!\n") if self.check_for_dealer_blackjack(): print("Dealer has BLACKJACK. Push...\n") - self.game.payout(self.game.player.hands[0], - self.game.dealer.hand) + self.game.payout(self.game.player_hands[0], + self.game.dealer_hand) return True else: print("Blackjack pays 3:2!\n") - self.game.payout_blackjack(self.game.player.hands[0]) + self.game.payout_blackjack(self.game.player_hands[0]) return True else: return False @@ -51,11 +51,11 @@ def dealer_play(self): print("Dealer hits, and recieves {}.\n".format(str(new_card))) self.game.dealer.takes_hit(new_card) - if self.game.check_bust(self.game.dealer.hand): + if self.game.check_bust(self.game.dealer_hand): print("Dealer busts!\n") - for hand in self.game.player.hands: - if hand.get_value() <= 21: - self.game.payout(hand, self.game.dealer.hand) + for hand in self.game.player_hands: + if not self.game.check_bust(hand): + self.game.payout(hand, self.game.dealer_hand) return False else: return True @@ -63,39 +63,49 @@ def dealer_play(self): def evaluate_hands(self): """Evaluates the final hands of the player and dealer and makes the appropriate calls the the playout method of the Game class""" - if len(self.game.player.hands) > 1: + if len(self.game.player_hands) > 1: self.evaluate_split_hands() - elif self.game.check_push(self.game.player.hands[0], - self.game.dealer.hand): - self.game.payout(self.game.player.hands[0], - self.game.dealer.hand) + elif self.game.check_push(self.game.player_hands[0], + self.game.dealer_hand): + self.game.payout(self.game.player_hands[0], + self.game.dealer_hand) print("Push.\n") self.print_dealer_hand() else: - if self.game.compare_hands(self.game.player.hands[0], - self.game.dealer.hand): - print("You win!\n") - self.game.payout(self.game.player.hands[0], - self.game.dealer.hand) - self.print_dealer_hand() - else: - print("Dealer wins.\n") - self.print_dealer_hand() + self.win_lose() + + def win_lose(self): + """Prints win lose message determined by player and dealer hands""" + if self.game.compare_hands(self.game.player_hands[0], + self.game.dealer_hand): + print("You win!\n") + self.game.payout(self.game.player_hands[0], + self.game.dealer_hand) + self.print_dealer_hand() + else: + print("Dealer wins.\n") + self.print_dealer_hand() + + def split_hand_win_lose(self, hand): + """Win_lose method that iterates through all active player hands and + displays appropriate message for each hand. Takes an enumerated + hand as argument""" + if self.game.compare_hands(hand[1], self.game.dealer_hand): + print("Hand {} wins!".format(hand[0]+1)) + self.game.payout(hand[1], self.game.dealer_hand) + else: + print("Hand {} loses!".format(hand[0]+1)) def evaluate_split_hands(self): """Method for evaluating multiple hands after a split.""" - for hand in enumerate(self.game.player.hands): + for hand in enumerate(self.game.player_hands): if self.game.check_bust(hand[1]): print("Hand {} busted.".format(hand[0]+1)) - elif self.game.check_push(hand[1], self.game.dealer.hand): + elif self.game.check_push(hand[1], self.game.dealer_hand): print("Push on hand {}.".format(hand[0]+1)) - self.game.payout(hand[1], self.game.dealer.hand) + self.game.payout(hand[1], self.game.dealer_hand) else: - if self.game.compare_hands(hand[1], self.game.dealer.hand): - print("Hand {} wins!".format(hand[0]+1)) - self.game.payout(hand[1], self.game.dealer.hand) - else: - print("Hand {} loses!".format(hand[0]+1)) + self.split_hand_win_lose(hand) self.print_dealer_hand() def execute_selection(self, selection, actions): @@ -103,20 +113,20 @@ def execute_selection(self, selection, actions): if selection == "H" and actions["hit"]: new_card = self.game.deck.deal() print("\nReceived {}\n".format(new_card)) - self.game.player.takes_hit(self.game.player.hands[ + self.game.player.takes_hit(self.game.player_hands[ self.current_hand], new_card) elif selection == "D" and actions["double"]: new_card = self.game.deck.deal() print("\nReceived {}\n".format(new_card)) - self.game.player.doubles(self.game.player.hands[self.current_hand], + self.game.player.doubles(self.game.player_hands[self.current_hand], new_card) elif selection == "P" and actions["split"]: new_cards = [self.game.deck.deal(), self.game.deck.deal()] - self.game.player.splits(self.game.player.hands[self.current_hand], + self.game.player.splits(self.game.player_hands[self.current_hand], new_cards) elif selection == "R" and actions["surrender"]: - self.game.player.surrenders(self.game.player.hands[ + self.game.player.surrenders(self.game.player_hands[ self.current_hand]) def initialize_hand(self): @@ -139,7 +149,7 @@ def offer_actions(self): """Prints available actions to the screen and allows user to input their selected action.""" actions = self.game.get_available_actions( - self.game.player.hands[self.current_hand]) + self.game.player_hands[self.current_hand]) valid_selection_made = False while not valid_selection_made: valid_input = ["S"] @@ -165,20 +175,19 @@ def offer_insurance(self): """Method that allows the user to buy insurace if the dealer's upcard is an Ace""" print("Dealer has an Ace showing. Buy insurance?") - while True: + player_choice = "" + while player_choice not in ["Y", "N"]: player_choice = input("(Y/N) {} ".format(choice(icons))).upper() if player_choice == "Y": self.game.player.buys_insurance() - break - elif player_choice == "N": - break else: print("Please enter Y or N.") def offer_surrender(self): """Used on early surrender games. Gives the player the option to surrender before the dealer checks for blackjack.""" - while True: + player_choice = "" + while player_choice not in ["Y", "N"]: player_choice = input("Surrender? (Y/N) {} ".format( choice(icons))).upper() if player_choice == "Y": @@ -195,15 +204,15 @@ def play_game(self, game): while self.game.player.money >= 5: self.play_hand() - while True: + player_choice = "" + while player_choice not in ["Y", "N"]: if self.game.player.money < 5: print("You don't have enough money to play!") input() break player_choice = input("Play another hand? (Y/N) {} ".format( choice(icons))).upper() - if player_choice in ["Y", "N"]: - break + if player_choice == "N" and self.game.player.money > 5: save_choice = "" while save_choice not in ["Y", "N"]: @@ -248,14 +257,14 @@ def players_turn(self, selection): """Allows the player to make game selections and determines if the dealer will need to take a turn""" dealers_turn = False - if self.game.check_bust(self.game.player.hands[self.current_hand]): - if len(self.game.player.hands) == 1: + if self.game.check_bust(self.game.player_hands[self.current_hand]): + if len(self.game.player_hands) == 1: print("You bust!\n") self.continue_hand = False return False - elif len(self.game.player.hands) == self.current_hand + 1: + elif len(self.game.player_hands) == self.current_hand + 1: print("You bust!\n") - for hand in self.game.player.hands: + for hand in self.game.player_hands: if hand.get_value() <= 21: dealers_turn = True return dealers_turn @@ -267,7 +276,7 @@ def players_turn(self, selection): self.continue_hand = False return False elif selection == "S" or selection == "D": - if len(self.game.player.hands) > self.current_hand + 1: + if len(self.game.player_hands) > self.current_hand + 1: self.current_hand += 1 else: return True @@ -275,17 +284,17 @@ def players_turn(self, selection): def print_dealer_hand(self): """Prints the dealer's hand to the screen""" print("Dealer's final hand: {}\n".format( - self.game.dealer.hand.get_card_strings())) + self.game.dealer_hand.get_card_strings())) def print_hands(self): """Displays the dealer's and player's hands to the screen""" print("Dealer's Hand: [{}, [X]]".format( self.game.dealer.get_show_card())) - print("Player's Hand{}: ".format("s" if len(self.game.player.hands) > 1 + print("Player's Hand{}: ".format("s" if len(self.game.player_hands) > 1 else ""), end="") card_string = "" - for hand in enumerate(self.game.player.hands): + for hand in enumerate(self.game.player_hands): card_string += "{}{} ".format("*" if hand[0] == self.current_hand else "", hand[1].get_card_strings(), end="") @@ -302,21 +311,21 @@ def resolve_blackjacks(self): return True if self.game.options.early_surrender: if self.offer_surrender(): - self.game.player.surrenders(self.game.player.hands[0]) + self.game.player.surrenders(self.game.player_hands[0]) print("You surrendered.\n") print("Dealer had: {}\n".format( - self.game.dealer.hand.get_card_strings())) + self.game.dealer_hand.get_card_strings())) return True elif (self.game.dealer.get_show_card().rank == "A" - and len(self.game.player.hands[0].cards) == 2 - and len(self.game.player.hands) == 1 - and self.game.player.money >= self.game.player.hands[0].bet): + and len(self.game.player_hands[0].cards) == 2 + and len(self.game.player_hands) == 1 + and self.game.player.money >= self.game.player_hands[0].bet): self.offer_insurance() if self.check_for_dealer_blackjack(): print("Dealer has blackjack!\n") - self.game.payout(self.game.player.hands[0], - self.game.dealer.hand) + self.game.payout(self.game.player_hands[0], + self.game.dealer_hand) return True return False diff --git a/blackjack/player.py b/blackjack/player.py index 230a1c8..9470ad8 100644 --- a/blackjack/player.py +++ b/blackjack/player.py @@ -26,6 +26,9 @@ def __init__(self, name): self.doubled = False self.player_hash = None + def get_player_hands(self): + return self.hands + def buys_insurance(self): """Modifys the player's money if they choose to buy insurance when offered""" diff --git a/run-instructions.txt b/run-instructions.txt deleted file mode 100644 index 3e4fd86..0000000 --- a/run-instructions.txt +++ /dev/null @@ -1 +0,0 @@ -To run the game use the command "python -m blackjack" from the main blackjack directory. From 471a9cbbbb673aaa39f4989450df3299e00be083 Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Wed, 28 Jan 2015 07:35:50 -0500 Subject: [PATCH 49/50] Added custom __len__ function to deck to make it easier to check cards remaining in the deck. --- blackjack/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blackjack/interface.py b/blackjack/interface.py index f9db5b5..eefe7a8 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -142,7 +142,7 @@ def initialize_hand(self): self.dealers_turn = False self.continue_hand = True - if len(self.game.deck.cards) < 30: + if len(self.game.deck) < 30: self.game.reshuffle() def offer_actions(self): From 61a7d5588cd2309f15dc51d427c410ad63c954de Mon Sep 17 00:00:00 2001 From: Alan Grissett Date: Wed, 28 Jan 2015 07:40:04 -0500 Subject: [PATCH 50/50] Simplified user input section of play_game method in interface. --- blackjack/interface.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/blackjack/interface.py b/blackjack/interface.py index eefe7a8..17280de 100644 --- a/blackjack/interface.py +++ b/blackjack/interface.py @@ -221,10 +221,8 @@ def play_game(self, game): choice(icons))).upper() if save_choice == "Y": self.game.player.save_player_state(self.game.options) - break - else: - break - + break + def play_hand(self): """Hand loop. Continues until the end of the hand. Prints updates about the status of the hand to the screen and makes method calls