Homework 8: Blackjack

A. Goals

The purpose of this assignment is to practice using ArrayLists and Interfaces. The specific goals are to:

B. Background

Blackjack is a popular card game, common as a table game at casinos. The goal of blackjack is to draw cards, scoring as close to 21 points without going over.

Blackjack uses a standard deck of 52 cards, with 4 possible suits, Hearts, Diamonds, Clubs, and Spades, and 13 possible values. These are shown in the image below.

Each card has a "score". All the cards in a hand are added up in hopes of reaching 21 without going over.

In blackjack, a player plays against a dealer, or "house". Often the dealer plays on behalf of a managing casino. If a player's hand is closer to 21 than the dealer without going over, the player wins. If the player goes over 21, or is further from 21 than the house, they lose. In the case both the player and the house have the same score under or equal to 21, it's called a "push", or a tie.

The best situation for the player is to get dealt an Ace and a card worth 10 points. If this happens, the player already has 21 points with two cards, and automatically wins (even if the dealer also has 21). This hand is called a blackjack.

In most cases of blackjack, both the player and the dealer start with 2 cards in their hand, with the player having both cards face up, so their value and suit is visible, while the dealer shows only one card face up, and the other "hidden" face down. The player then decides to "hit" (draw another card to add to their hand) or "stand" (draw no more cards and end their turn. Once you stand, you can no longer draw any cards. However, if you "hit", and your total goes over 21, you lose automatically, and the dealer never has to take their turn, winning automatically.

In blackjack the player must always play their turn before the dealer. This means the decision for the player to "hit" is based on the risk of going over 21. If a player has a 17, for example, it is unlikely they should "hit", as there is roughly a 10/13 chance that the player will "bust" (go over 21). Likewise, if you have a total score of 5, you absolutely should hit, since it's almost certain the dealer will have a higher score just with two cards alone. That is, unless you like to live dangerously.

Because the dealer always goes after the player, the "house" (or casino) has a slight advantage, winning about 5% more often than the player. Hey, gambling is a business.

C. Your program

In this assignment, you will write a Card, Hand, and Deck class. Each class implements an interface, HW8Card, HW8Hand and HW8Deck respectively. You will implement all of the methods in the interfaces in their respective class files.

D. Designing the Requirement and Interface






1. Card Class

In this section, you will write a new java file, Card.java, implementing HW8Card.

A. HW8Card interface

hw8.zip contains the HW8Card interface that represents a standard card from a deck of 52. The API is as follows:

public interface HW8Card
------------------------------------------------------------------------------------------------------
       
 String getSuit()                    // return the suit of the card (i.e. "Hearts", "Spades", etc.)
 String getValue()                   // return the value of the card (i.e. "2", "10", "J", "A"
    int getBlackjackScore()          // returns the blackjack score of a card. Number cards are worth
                                                  // their number. Face cards worth 10. Aces worth 1 (not 11)
boolean isAce()                      // return true if card is ace, false otherwise.
boolean equals(Object o)             // return true if this and o is a Card and both the calling instance
                                                  // and o have the same Suit and Value.
 String toString()                   // Prints card as a string (i.e. "A of Spades", "2 of Diamonds")

B. The Card class

You are given method stubs for all the above methods. Implement these by determining what fields you need and then writing the methods.

In addition, you must write a constructor that takes in a value and a suit.

It is vital that your toString() method be correct. This means a two of hearts should print, exactly:

2 of Hearts

Remember that an Ace is "A", so for an ace of clubs, you should print.

A of Clubs

The same is true of other "face" cards, so Jack, Queen, and King should be J, Q, and K, respectively.

2. Hand Class

Implement the HW8Hand class by writing Hand.java

A. Hand Fields

The hand class will have a single field, an ArrayList<HW8Card> that contains the cards in the hand. Remember that because Card implements the HW8Card interface, every instance of Card is an instance of a HW8Card. Thus, we can fill an ArrayList of HW8Cards with Cards

Required Methods:

public interface HW8Hand
------------------------------------------------------------------------------------------------------
       
   void addCard(HW8Card card) // Adds a card object to the hand
    int getHardScore()        // Gets the blackjack score of the hand with all aces treated as 1 point
    int getSoftScore()        // Gets the blackjack score of the hand with the FIRST ace treated as 11.
                                     //   (If there are no aces, gives same result as getHardScore
    int getScore()            // Returns the best score possible for the hand (between Soft and Hard score)
boolean isBust()                  // Returns true if all possible scores are greater than 21
    int getNumCards()         // Return the number of cards in the hand
HW8Card getDealerShowing()    // Return the SECOND (index 1) card in the hand.
 String toString()            // Prints the hand (see below for details)

The hand should have a zero-argument constructor that initializes the instance's ArrayList.

To add a card, use the List function add(E e), where E is the type HW8Card.

For the scoring functions, consider the hand "Ace of Hearts, Ace of Diamonds, 9 of Hearts". The hard score of this hand is 1 + 1 + 9, or 11, since every Ace is treated as worth 1 point. The soft score is 21, since the first ace is treated as 11, so 11 + 1 + 9. This means the overall score of the hand is 21.

By contrast, consider a hand of three kings. Here, both the hard and the soft score are 30, since there is no Ace, meaning the overall score is 30. Since every score is greater than 21, this hand is bust, meaning the holder of this hand loses.

Don't overthink getDealerShowing(). Both player and dealer hands are represented with the HW8Hand. This method should simply use the List get(int i) method to return the second card in the hand (that is, the card at index 1). Don't worry about "proving" the hand belongs to a dealer. Just simply return the second card. The game handles this function correctly.




Finally, make sure the toString() method works. This method is used in our grading tests. For example, assume the hand in the picture above (with the cards indexed from left to right) should print exactly: "2 of Spades, 5 of Spades, 2 of Hearts, 7 of Hearts". Note the comma followed by the single space between every card. Also note that there is no comma or whitespace after the last card.

3. Deck class

Implement the HW8Deck class in a file called Deck.java

A. Deck Fields

The deck class, like hand, will have a single field, an ArrayList<HW8Card> that contains the cards in the Deck. Remember that because Card implements the HW8Card interface, every instance of Card is an instance of a HW8Card. Thus, we can fill an ArrayList of HW8Cards with Cards.

Required Methods:

public interface HW8Deck
------------------------------------------------------------------------------------------------------
       
   void resetDeck()           // Resets the deck of cards to an initial state of 52 Cards. These cards
                                     // should NOT be shuffled.
   void shuffle()             // randomizes the order of the cards in the deck.
HW8Card drawCard()            // Remove the first card from the deck and return it. Throw an IllegalStateException
                                     // if the deck is empty (has zero cards left).
 String toString              // Returns a string of the form "Deck has [x] cards"

resetDeck should reset the deck back to a starting state (as though you just opened a fresh pack of 52 cards). This means you will have one card for each value of each suit. Example, 2 of Spades, 2 of Hearts, 2 of Clubs, 2 of Diamonds, 3 of Spades, 3 of Hearts, 3 of Clubs, 3 of Diamonds, etc. For all values 2-10, J, Q, K, and A. Do not write 52 lines of code. Find a systematic way to do this. Write resetDeck() before writing and testing any other methods

The constructor of deck should have zero arguments, and should both initialize the ArrayList of cards and fill the deck with 52 standard cards. Do not write duplicate code from resetDeck. Use the method resetDeck in the constructor.

For the shuffle method, use the static function Collections.shuffle(List l). This function randomizes the order of elements in a list in place, and returns nothing.

For drawCard, consider the functions we went over in class related to the List interface and use the correct one to draw one card from the top of the deck. After you draw a card, the number of cards in the deck should decrease by 1. If there are no cards to draw, throw an IllegalStateException

The toString must return a string containing the number of cards in the deck. DO NOT return a string containing the String representations of all the cards in the deck. Simply say the size of the deck.

A. Final Testing

Before submitting, run Blackjack.java. This is a fully functional Blackjack game. All commands are entered with "y/n" (y for yes, n for no).

A. Readme

Complete readme_blackjack.txt in the same way that you have done for previous assignments.

B. Submission

Submit Card.java, Hand.java, Deck.java and readme_blackjack.txt on the course website.

Do not submit your Blackjack.java, HW8Card.java, HW8Hand.java, or HW8Deck.java files.