Case Study: Card Shuffling and Dealing Simulation
Case Study Card Shuffling and Dealing Simulation
The examples in the chapter thus far have used arrays containing elements of primitive types. Recall from Section 7.2 that the elements of an array can be either primitive types or reference types. This section uses random number generation and an array of reference-type elements, namely objects representing playing cards, to develop a class that simulates card shuffling and dealing. This class can then be used to implement applications that play specific card games. The exercises at the end of the chapter use the classes developed here to build a simple poker application.
We first develop class Card (Fig. 7.9), which represents a playing card that has a face (e.g., "Ace", "Deuce", "Three", ..., "Jack", "Queen", "King") and a suit (e.g., "Hearts", "Diamonds", "Clubs", "Spades"). Next, we develop the DeckOfCards class (Fig. 7.10), which creates a deck of 52 playing cards in which each element is a Card object. We then build a test application (Fig. 7.11) that demonstrates class DeckOfCards's card shuffling and dealing capabilities.
Figure 7.9. Card class represents a playing card.
(This item is displayed on page 299 in the print version)
1 // Fig. 7.9: Card.java 2 // Card class represents a playing card. 3 4 public class Card 5 { 6 private String face; // face of card ("Ace", "Deuce", ...) 7 private String suit; // suit of card ("Hearts", "Diamonds", ...) 8 9 // two-argument constructor initializes card's face and suit 10 public Card( String cardFace, String cardSuit ) 11 { 12 face = cardFace; // initialize face of card 13 suit = cardSuit; // initialize suit of card 14 } // end two-argument Card constructor 15 16 // return String representation of Card 17 public String toString() 18 { 19 return face + " of " + suit; 20 } // end method toString 21 } // end class Card |
Figure 7.10. DeckOfCards class represents a deck of playing cards that can be shuffled and dealt one at a time.
(This item is displayed on pages 299 - 300 in the print version)
1 // Fig. 7.10: DeckOfCards.java 2 // DeckOfCards class represents a deck of playing cards. 3 import java.util.Random; 4 5 public class DeckOfCards 6 { 7 private Card deck[]; // array of Card objects 8 private int currentCard; // index of next Card to be dealt 9 private final int NUMBER_OF_CARDS = 52; // constant number of Cards 10 private Random randomNumbers; // random number generator 11 12 // constructor fills deck of Cards 13 public DeckOfCards() 14 { 15 String faces[] = { "Ace", "Deuce", "Three", "Four", "Five", "Six", 16 "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King" }; 17 String suits[] = { "Hearts", "Diamonds", "Clubs", "Spades" }; 18 19 deck = new Card[ NUMBER_OF_CARDS ]; // create array of Card objects 20 currentCard = 0; // set currentCard so first Card dealt is deck[ 0 ] 21 randomNumbers = new Random(); // create random number generator 22 23 // populate deck with Card objects 24 for ( int count = 0; count < deck.length; count++ ) 25 deck[ count ] = 26 new Card( faces[ count % 13 ], suits[ count / 13 ] ); 27 } // end DeckOfCards constructor 28 29 // shuffle deck of Cards with one-pass algorithm 30 public void shuffle() 31 { 32 // after shuffling, dealing should start at deck[ 0 ] again 33 currentCard = 0; // reinitialize currentCard 34 35 // for each Card, pick another random Card and swap them 36 for ( int first = 0; first < deck.length; first++ ) 37 { 38 // select a random number between 0 and 51 39 int second = randomNumbers.nextInt( NUMBER_OF_CARDS ); 40 41 // swap current Card with randomly selected Card 42 Card temp = deck[ first ]; 43 deck[ first ] = deck[ second ]; 44 deck[ second ] = temp; 45 } // end for 46 } // end method shuffle 47 48 // deal one Card 49 public Card dealCard() 50 { 51 // determine whether Cards remain to be dealt 52 if ( currentCard < deck.length ) 53 return deck[ currentCard++ ]; // return current Card in array 54 else 55 return null; // return null to indicate that all Cards were dealt 56 } // end method dealCard 57 } // end class DeckOfCards |
Figure 7.11. Card shuffling and dealing.
(This item is displayed on page 302 in the print version)
1 // Fig. 7.11: DeckOfCardsTest.java 2 // Card shuffling and dealing application. 3 4 public class DeckOfCardsTest 5 { 6 // execute application 7 public static void main( String args[] ) 8 { 9 DeckOfCards myDeckOfCards = new DeckOfCards(); 10 myDeckOfCards.shuffle(); // place Cards in random order 11 12 // print all 52 Cards in the order in which they are dealt 13 for ( int i = 0; i < 13; i++ ) 14 { 15 // deal and print 4 Cards 16 System.out.printf( "%-20s%-20s%-20s%-20s ", 17 myDeckOfCards.dealCard(), myDeckOfCards.dealCard(), 18 myDeckOfCards.dealCard(), myDeckOfCards.dealCard() ); 19 } // end for 20 } // end main 21 } // end class DeckOfCardsTest
|
Class Card
Class Card (Fig. 7.9) contains two String instance variablesface and suitthat are used to store references to the face name and suit name for a specific Card. The constructor for the class (lines 1014) receives two Strings that it uses to initialize face and suit. Method toString (lines 1720) creates a String consisting of the face of the card, the String " of " and the suit of the card. Recall from Chapter 6 that the + operator can be used to concatenate (i.e., combine) several Strings to form one larger String. Card's toString method can be invoked explicitly to obtain a string representation of a Card object (e.g., "Ace of Spades"). The toString method of an object is called implicitly when the object is used where a String is expected (e.g., when printf outputs the object as a String using the %s format specifier or when the object is concatenated to a String using the + operator). For this behavior to occur, toString must be declared with the header shown in Fig. 7.9.
Class DeckOfCards
Class DeckOfCards (Fig. 7.10) declares an instance variable array named deck of Card objects (line 7). Like primitive-type array declarations, the declaration of an array of objects includes the type of the elements in the array, followed by the name of the array variable and square brackets (e.g., Card deck[]). Class DeckOfCards also declares an integer instance variable currentCard (line 8) representing the next Card to be dealt from the deck array and a named constant NUMBER_OF_CARDS (line 9) indicating the number of Cards in the deck (52).
The class's constructor instantiates the deck array (line 19) to be of size NUMBER_OF_CARDS. When first created, the elements of the deck array are null by default, so the constructor uses a for statement (lines 2426) to fill the deck array with Cards. The for statement initializes control variable count to 0 and loops while count is less than deck.length, causing count to take on each integer value from 0 to 51 (the indices of the deck array). Each Card is instantiated and initialized with two Stringsone from the faces array (which contains the Strings "Ace" tHRough "King") and one from the suits array (which contains the Strings "Hearts", "Diamonds", "Clubs" and "Spades"). The calculation count % 13 always results in a value from 0 to 12 (the 13 indices of the faces array in lines 1516), and the calculation count / 13 always results in a value from 0 to 3 (the four indices of the suits array in line 17). When the deck array is initialized, it contains the Cards with faces "Ace" through "King" in order for each suit.
Method shuffle (lines 3046) shuffles the Cards in the deck. The method loops through all 52 Cards (array indices 0 to 51). For each Card, a number between 0 and 51 is picked randomly to select another Card. Next, the current Card object and the randomly selected Card object are swapped in the array. This exchange is performed by the three assignments in lines 4244. The extra variable temp temporarily stores one of the two Card objects being swapped. The swap cannot be performed with only the two statements
deck[ first ] = deck[ second ]; deck[ second ] = deck[ first ];
If deck[ first ] is the "Ace" of "Spades" and deck[ second ] is the "Queen" of "Hearts", after the first assignment, both array elements contain the "Queen" of "Hearts" and the "Ace" of "Spades" is losthence, the extra variable temp is needed. After the for loop terminates, the Card objects are randomly ordered. A total of only 52 swaps are made in a single pass of the entire array, and the array of Card objects is shuffled!
Method dealCard (lines 4956) deals one Card in the array. Recall that currentCard indicates the index of the next Card to be dealt (i.e., the Card at the top of the deck). Thus, line 52 compares currentCard to the length of the deck array. If the deck is not empty (i.e., currentCard is less than 52), line 53 returns the top Card and increments currentCard to prepare for the next call to dealCardotherwise, null is returned. Recall from Chapter 3 that null represents a "reference to nothing."
Shuffling and Dealing Cards
The application of Fig. 7.11 demonstrates the card dealing and shuffling capabilities of class DeckOfCards (Fig. 7.10). Line 9 creates a DeckOfCards object named myDeckOfCards. Recall that the DeckOfCards constructor creates the deck with the 52 Card objects in order by suit and face. Line 10 invokes myDeckOfCards's shuffle method to rearrange the Card objects. The for statement in lines 1319 deals all 52 Cards in the deck and prints them in four columns of 13 Cards each. Lines 1618 deal and print four Card objects, each obtained by invoking myDeckOfCards's dealCard method. When printf outputs a Card with the %-20s format specifier, the Card's toString method (declared in lines 1720 of Fig. 7.9) is implicitly invoked, and the result is output left justified in a field of width 20.