Solution for
Programming Exercise 5.5
THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to
the following exercise from this on-line
Java textbook.
Exercise 5.5
Write a program that let's 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.
First, two cards are dealt into each player's hand. If the dealer's hand has a
value of 21 at this point, then the dealer wins. Otherwise, if the user has 21, then the user
wins. (This is called a "Blackjack".) Note that the dealer wins on a tie,
so if both players have Blackjack, then the dealer wins.
Now, if the game has not ended, the user gets a chance to add some cards to her hand. In
this phase, the user sees her own cards and sees one of the dealer's two cards.
(In a casino, the dealer deals himself one card face up and one card face down. All the user's cards
are dealt face up.) The user makes a decision whether to "Hit", which means to
add another card to her hand, or to "Stand", which means to stop taking cards.
If the user Hits, there is a possibility that the user will go over 21. In that case,
the game is over and the user loses. If not, then the process continues. The
user gets to decide again whether to Hit or Stand.
If the user Stands, the game will end, but first the dealer gets a chance to draw cards.
The dealer only follows rules, without any choice. The rule is that as long as the
value of the dealer's hand is less than or equal to 16, the dealer Hits (that is, takes
another card). The user should see all the dealer's cards at this point. Now, the winner
can be determined: If the dealer has gone over 21, the user wins. Otherwise,
if the dealer's total is greater than or equal to the user's total, then the dealer wins.
Otherwise, the user wins.
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()
static boolean playBlackjack() {
// Let the user play one game of Blackjack.
// Return true if the user wins, false if the user loses.
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
]