[ Exercises | Chapter Index | Main Index ]

Solution for Programmming Exercise 5.5


This page contains a sample solution to one of the exercises from Introduction to Programming Using Java.


Exercise 5.5:

Write a program that lets the user play Blackjack. The game will be a simplified version of Blackjack as it is played in a casino. The computer will act as the dealer. As in the previous exercise, your program will need the classes defined in Card.java, Deck.java, Hand.java, and BlackjackHand.java. (This is the longest and most complex program that has come up so far in the exercises.)

You should first write a subroutine in which the user plays one game. The subroutine should return a boolean value to indicate whether the user wins the game or not. Return true if the user wins, false if the dealer wins. The program needs an object of class Deck and two objects of type BlackjackHand, one for the dealer and one for the user. The general object in Blackjack is to get a hand of cards whose value is as close to 21 as possible, without going over. The game goes like this.

Two notes on programming: At any point in the subroutine, as soon as you know who the winner is, you can say "return true;" or "return false;" to end the subroutine and return to the main program. To avoid having an overabundance of variables in your subroutine, remember that a function call such as userHand.getBlackjackValue() can be used anywhere that a number could be used, including in an output statement or in the condition of an if statement.

Write a main program that lets the user play several games of Blackjack. To make things interesting, give the user 100 dollars, and let the user make bets on the game. If the user loses, subtract the bet from the user's money. If the user wins, add an amount equal to the bet to the user's money. End the program when the user wants to quit or when she runs out of money.

Here is an applet that simulates the program you are supposed to write. It would probably be worthwhile to play it for a while to see how it works.


Discussion

Let's start by designing the main program. We want to give the user $100 for betting on the games. Then, the user plays Blackjack games until the user runs out of money or until the user wants to quit. We could ask the user after each game whether she wants to continue. But instead of this, I just tell the user to enter a bet amount of 0 if she wants to quit. We need variables to represent the amount of money that the user has and the amount that the user bets on a given game. Let money and bet be variables of type int to represent these quantities. Then, we can write an algorithm for the main program:

Let money = 100
while (true):
    Input the user's bet
    if the bet is 0:
        Break out of the loop
    User plays a game of Blackjack
    if the user won the game
        Pay off the user's bet (money = money + bet)
    else
        Collect the user's bet (money = money - bet)
    If the user is out of money:
        Break out of the loop.

Since the Blackjack game will take place in a subroutine, we need some way for the main() routine to find out whether the user won. The exercise says that the subroutine should be a function that returns a boolean value with this information. We should record the return value and test it to see whether the user won. The other point that needs some refinement is inputting the user's bet. We better make sure that the user's bet is a reasonable amount, that is, something between 0 and the amount of money the user has. So, the algorithm can be refined.

Let money = 100
while (true):
    do {
        Ask the user to enter a bet
        Let bet = the user's response
    } while bet is < 0 or > money
    if  bet is 0:
        Break out of the loop
    Let userWins = playBlackjack()
    if userWins:
        Pay off the user's bet (money = money + bet)
    else
        Collect the user's bet (money = money - bet)
    If money == 0:
        Break out of the loop.

This algorithm can be translated into the main() routine in the program given below.


Of course, the major part of the problem is to write the playBlackjack() routine. Fortunately, the exercise gives what amounts to a fairly detailed outline of the algorithm. Things are a little complicated because the game can end at various points along the way. When this happens, the subroutine ends immediately, and any remaining steps in the algorithm are skipped. In outline, the game goes like this:

Create and shuffle a deck of cards
Create two BlackjackHands, userHand and dealerHand
Deal two cards into each hand
Check if dealer has blackjack (if so, game ends)
Check if user has blackjack (if so, game ends)
User draws cards (if user goes over 21, game ends)
Dealer draws cards
Check for winner

The last three steps need to be expanded, again using the information stated in the exercise. The user can draw several cards, so we need a loop. The loop ends when the user wants to "stand". In the loop, if the value of userHand goes over 21, then the whole subroutine ends. The dealer simply draws cards in a loop as long as the value of dealerHand is 16 or less. Again, if the value goes over 21, the whole subroutine ends. In the last step, we determine the winner by comparing the values of the two hands. With these refinements, the algorithm becomes

Create and shuffle a deck of cards
Create two BlackjackHands, userHand and dealerHand
Deal two cards into each hand
if dealer has blackjack
    User loses and the game ends now
If user has blackjack
    User wins and the game ends now
Repeat:
    Ask whether user wants to hit or stand
    if user stands:
        break out of loop
    if user hits:
        Give user a card
        if userHand.getBlackjackValue() > 21:
            User loses and the game ends now
while  dealerHand.getBlackJackValue() <= 16 :
    Give dealer a card
    if dealerHand.getBlackjackValue() > 21:
        User wins and game ends now
if dealerHand.getBlackjackValue() >= userHand.getBlackjackValue()
    User loses
else
    User wins

This is ready to be translated into Java. One point of coding is the question of how to deal a card to the user or to the dealer. If deck refers to the object of type Deck, then the function call deck.dealCard() returns the card we want. We can add the card to a hand with the addCard() instance method from the Hand class. We can do this in one step, if we want. For example, to deal two cards into each hand, we just have to say

dealerHand.addCard( deck.dealCard() );
dealerHand.addCard( deck.dealCard() );
userHand.addCard( deck.dealCard() );
userHand.addCard( deck.dealCard() );

Of course, a lot of output statements have to be added to the algorithm to keep the user informed about what is going on. For example, I expanded the step where it says "Ask whether user wants to hit or stand" to

Display all the cards in the user's hand
Display the user's total
Display the dealers face-up card, i.e. dealerHand.getCard(0)
Ask if user wants to hit or stand
Get user's response, and make sure it's legal

The last step listed here expands to a loop that ends when the user inputs a valid response, 'H' or 'S'. The first step uses a for loop

for ( int i = 0;  i < userHand.getCardCount();  i++ )
     TextIO.putln("   " + userHand.getCard(i));

to display the cards in the user's hand. The function call userHand.getCardCount() gives the number of cards in the hand. The cards are numbered from 0 to userHand.getCardCount() - 1, and userHand.getCard(i) is the card in the i-position. Of course, to produce code like this, you have to make sure that you are familiar with the methods in the classes that you are using.

Although there are many other details to get right, it's mostly routine from here on. I encourage you to read the entire program below and make sure that you understand it.


The Solution

/**
 * This program lets the user play Blackjack.  The computer
 * acts as the dealer.  The user has a stake of $100, and
 * makes a bet on each game.  The user can leave at any time,
 * or will be kicked out when he loses all the money.
 * House rules:  The dealer hits on a total of 16 or less
 * and stands on a total of 17 or more.  Dealer wins ties.
 * A new deck of cards is used for each game.
 */

public class Blackjack {

   public static void main(String[] args) {
   
      int money;          // Amount of money the user has.
      int bet;            // Amount user bets on a game.
      boolean userWins;   // Did the user win the game?
      
      TextIO.putln("Welcome to the game of blackjack.");
      TextIO.putln();
      
      money = 100;  // User starts with $100.
   
      while (true) {
          TextIO.putln("You have " + money + " dollars.");
          do {
             TextIO.putln("How many dollars do you want to bet?  (Enter 0 to end.)");
             TextIO.put("? ");
             bet = TextIO.getlnInt();
             if (bet < 0 || bet > money)
                 TextIO.putln("Your answer must be between 0 and " + money + '.');
          } while (bet < 0 || bet > money);
          if (bet == 0)
             break;
          userWins = playBlackjack();
          if (userWins)
             money = money + bet;
          else
             money = money - bet;
          TextIO.putln();
          if (money == 0) {
             TextIO.putln("Looks like you've are out of money!");
             break;
          }
      }
      
      TextIO.putln();
      TextIO.putln("You leave with $" + money + '.');
   
   } // end main()
   
 
   /**
    * Let the user play one game of Blackjack, with the computer as dealer.
    * @return true if the user wins the game, false if the user loses.
    */  
   static boolean playBlackjack() {

      Deck deck;                  // A deck of cards.  A new deck for each game.
      BlackjackHand dealerHand;   // The dealer's hand.
      BlackjackHand userHand;     // The user's hand.
      
      deck = new Deck();
      dealerHand = new BlackjackHand();
      userHand = new BlackjackHand();

      /*  Shuffle the deck, then deal two cards to each player. */
      
      deck.shuffle();
      dealerHand.addCard( deck.dealCard() );
      dealerHand.addCard( deck.dealCard() );
      userHand.addCard( deck.dealCard() );
      userHand.addCard( deck.dealCard() );
      
      TextIO.putln();
      TextIO.putln();
      
      /* Check if one of the players has Blackjack (two cards totaling to 21).
         The player with Blackjack wins the game.  Dealer wins ties.
      */
      
      if (dealerHand.getBlackjackValue() == 21) {
           TextIO.putln("Dealer has the " + dealerHand.getCard(0)
                                   + " and the " + dealerHand.getCard(1) + ".");
           TextIO.putln("User has the " + userHand.getCard(0)
                                     + " and the " + userHand.getCard(1) + ".");
           TextIO.putln();
           TextIO.putln("Dealer has Blackjack.  Dealer wins.");
           return false;
      }
      
      if (userHand.getBlackjackValue() == 21) {
           TextIO.putln("Dealer has the " + dealerHand.getCard(0)
                                   + " and the " + dealerHand.getCard(1) + ".");
           TextIO.putln("User has the " + userHand.getCard(0)
                                     + " and the " + userHand.getCard(1) + ".");
           TextIO.putln();
           TextIO.putln("You have Blackjack.  You win.");
           return true;
      }
      
      /*  If neither player has Blackjack, play the game.  First the user 
          gets a chance to draw cards (i.e., to "Hit").  The while loop ends 
          when the user chooses to "Stand".  If the user goes over 21,
          the user loses immediately.
      */
      
      while (true) {
          
           /* Display user's cards, and let user decide to Hit or Stand. */

           TextIO.putln();
           TextIO.putln();
           TextIO.putln("Your cards are:");
           for ( int i = 0; i < userHand.getCardCount(); i++ )
              TextIO.putln("    " + userHand.getCard(i));
           TextIO.putln("Your total is " + userHand.getBlackjackValue());
           TextIO.putln();
           TextIO.putln("Dealer is showing the " + dealerHand.getCard(0));
           TextIO.putln();
           TextIO.put("Hit (H) or Stand (S)? ");
           char userAction;  // User's response, 'H' or 'S'.
           do {
              userAction = Character.toUpperCase( TextIO.getlnChar() );
              if (userAction != 'H' && userAction != 'S')
                 TextIO.put("Please respond H or S:  ");
           } while (userAction != 'H' && userAction != 'S');

           /* If the user Hits, the user gets a card.  If the user Stands,
              the loop ends (and it's the dealer's turn to draw cards).
           */

           if ( userAction == 'S' ) {
                   // Loop ends; user is done taking cards.
               break;
           }
           else {  // userAction is 'H'.  Give the user a card.  
                   // If the user goes over 21, the user loses.
               Card newCard = deck.dealCard();
               userHand.addCard(newCard);
               TextIO.putln();
               TextIO.putln("User hits.");
               TextIO.putln("Your card is the " + newCard);
               TextIO.putln("Your total is now " + userHand.getBlackjackValue());
               if (userHand.getBlackjackValue() > 21) {
                   TextIO.putln();
                   TextIO.putln("You busted by going over 21.  You lose.");
                   TextIO.putln("Dealer's other card was the " 
                                                      + dealerHand.getCard(1));
                   return false;  
               }
           }
           
      } // end while loop
      
      /* If we get to this point, the user has Stood with 21 or less.  Now, it's
         the dealer's chance to draw.  Dealer draws cards until the dealer's
         total is > 16.  If dealer goes over 21, the dealer loses.
      */

      TextIO.putln();
      TextIO.putln("User stands.");
      TextIO.putln("Dealer's cards are");
      TextIO.putln("    " + dealerHand.getCard(0));
      TextIO.putln("    " + dealerHand.getCard(1));
      while (dealerHand.getBlackjackValue() <= 16) {
         Card newCard = deck.dealCard();
         TextIO.putln("Dealer hits and gets the " + newCard);
         dealerHand.addCard(newCard);
         if (dealerHand.getBlackjackValue() > 21) {
            TextIO.putln();
            TextIO.putln("Dealer busted by going over 21.  You win.");
            return true;
         }
      }
      TextIO.putln("Dealer's total is " + dealerHand.getBlackjackValue());
      
      /* If we get to this point, both players have 21 or less.  We
         can determine the winner by comparing the values of their hands. */
      
      TextIO.putln();
      if (dealerHand.getBlackjackValue() == userHand.getBlackjackValue()) {
         TextIO.putln("Dealer wins on a tie.  You lose.");
         return false;
      }
      else if (dealerHand.getBlackjackValue() > userHand.getBlackjackValue()) {
         TextIO.putln("Dealer wins, " + dealerHand.getBlackjackValue() 
                          + " points to " + userHand.getBlackjackValue() + ".");
         return false;
      }
      else {
         TextIO.putln("You win, " + userHand.getBlackjackValue() 
                          + " points to " + dealerHand.getBlackjackValue() + ".");
         return true;
      }

   }  // end playBlackjack()


} // end class Blackjack

[ Exercises | Chapter Index | Main Index ]