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 value-type elements. This section uses random-number generation and an array of reference-type elementsnamely, objects representing playing cardsto develop a class that simulates card shuffling and dealing. This class can then be used to implement applications that play card games. The exercises at the end of the chapter use the techniques developed here to build a poker application.
We first develop class Card (Fig. 8.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. 8.10), which creates a deck of 52 playing cards in which each element is a Card object. Then we build a test application (Fig. 8.11) that demonstrates class DeckOfCards's card shuffling and dealing capabilities.
Figure 8.9. Card class represents a playing card.
1 // Fig. 8.9: Card.cs 2 // Card class represents a playing card. 3 public class Card 4 { 5 private string face; // face of card ("Ace", "Deuce", ...) 6 private string suit; // suit of card ("Hearts", "Diamonds", ...) 7 8 // two-parameter constructor initializes card's face and suit 9 public Card( string cardFace, string cardSuit ) 10 { 11 face = cardFace; // initialize face of card 12 suit = cardSuit; // initialize suit of card 13 } // end two-parameter Card constructor 14 15 // return string representation of Card 16 public override string ToString() 17 { 18 return face + " of " + suit; 19 } // end method ToString 20 } // end class Card |
Figure 8.10. DeckOfCards class represents a deck of playing cards.
(This item is displayed on pages 349 - 350 in the print version)
1 // Fig. 8.10: DeckOfCards.cs 2 // DeckOfCards class represents a deck of playing cards. 3 using System; 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 const 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.Next( 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 8.11. Card shuffling and dealing application.
(This item is displayed on page 351 in the print version)
1 // Fig. 8.11: DeckOfCardsTest.cs 2 // Card shuffling and dealing application. 3 using System; 4 5 public class DeckOfCardsTest 6 { 7 // execute application 8 public static void Main( string[] args ) 9 { 10 DeckOfCards myDeckOfCards = new DeckOfCards(); 11 myDeckOfCards.Shuffle(); // place Cards in random order 12 13 // print all 52 Cards in the order in which they are dealt 14 for ( int i = 0; i < 13; i++ ) 15 { 16 // deal and print 4 Cards 17 Console.WriteLine( "{0,-20}{1,-20}{2,-20}{3,-20}", 18 myDeckOfCards.DealCard(), myDeckOfCards.DealCard(), 19 myDeckOfCards.DealCard(), myDeckOfCards.DealCard() ); 20 } // end for 21 } // end Main 22 } // end class DeckOfCardsTest
|
Class Card
Class Card (Fig. 8.9) contains two string instance variablesface and suitthat are used to store references to the face value and suit name for a specific Card. The constructor for the class (lines 913) receives two strings that it uses to initialize face and suit. Method ToString (lines 1619) creates a string consisting of the face of the card, the string " of " and the suit of the card. Recall from Chapter 7 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 in many cases when the object is used where a string is expected (e.g., when WriteLine outputs the object with a format item or when the object is concatenated to a string using the + operator). For this behavior to occur, ToString must be declared with the header exactly as shown in line 16 of Fig. 8.9. We will explain the purpose of the override keyword in more detail when we discuss inheritance in Chapter 10.
Class DeckOfCards
Class DeckOfCards (Fig. 8.10) declares an instance-variable array named deck that contains Card objects (line 7). Like simple-type array declarations, the declaration of an array of objects includes the type of the elements in the array, followed by square brackets and the name of the array variable (e.g., Card[] deck). Class DeckOfCards also declares int instance variable currentCard (line 8), representing the next Card to be dealt from the deck array, and 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", then 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. 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.
Shuffling and Dealing Cards
The application of Fig. 8.11 demonstrates the card dealing and shuffling capabilities of class DeckOfCards (Fig. 8.10). Line 10 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 11 invokes myDeckOfCards's Shuffle method to rearrange the Card objects. The for statement in lines 1420 deals all 52 Cards in the deck and prints them in four columns of 13 Cards each. Lines 1719 deal and print four Card objects, each obtained by invoking myDeckOfCards's DealCard method. When WriteLine outputs a Card with string formatting, the Card's ToString method (declared in lines 1619 of Fig. 8.9) is invoked implicitly. Because the field width is negative, the result is output left justified in a field of width 20.
foreach Statement
|