7.9 Case Study: A Game of Chance; Introducing Enumerations

One popular game of chance is the dice game known as “craps,” which is played in casinos and back alleys throughout the world. The rules of the game are straightforward:

You roll two dice. Each die has six faces, which contain one, two, three, four, five and six spots, respectively. After the dice have come to rest, the sum of the spots on the two upward faces is calculated. If the sum is 7 or 11 on the first throw, you win. If the sum is 2, 3 or 12 on the first throw (called “craps”), you lose (i.e., “the house” wins). If the sum is 4, 5, 6, 8, 9 or 10 on the first throw, that sum becomes your “point.” To win, you must continue rolling the dice until you “make your point” (i.e., roll that same point value). You lose by rolling a 7 before making your point.

The app in Fig. 7.8 simulates the game of craps, using methods to define the logic of the game. The Main method (lines 24–80) calls the static RollDice method (lines 83–94) as needed to roll the two dice and compute their sum. The four sample outputs show winning on the first roll, losing on the first roll, winning on a subsequent roll and losing on a subsequent roll, respectively. Variable randomNumbers (line 8) is declared static, so it can be created once during the program’s execution and used in method RollDice.

Fig. 7.8 Craps class simulates the dice game craps.

Alternate View

  1   // Fig. 7.8: Craps.cs
  2   // Craps class simulates the dice game craps.
  3   using System;
  4
  5   class Craps
  6   {
  7      // create random-number generator for use in method RollDice
  8      private static Random randomNumbers = new Random();
  9
 10      // enumeration with constants that represent the game status
 11      private enum Status {Continue, Won, Lost}                   
 12
 13      // enumeration with constants that represent common rolls of the dice
 14      private enum DiceNames
 15      {                     
 16         SnakeEyes = 2,     
 17         Trey = 3,          
 18         Seven = 7,         
 19         YoLeven = 11,      
 20         BoxCars = 12       
 21      }                     
 22
 23      // plays one game of craps
 24      static void Main()
 25      {
 26         // gameStatus can contain Continue, Won or Lost
 27         Status gameStatus = Status.Continue;
 28         int myPoint = 0; // point if no win or loss on first roll
 29
 30         int sumOfDice = RollDice(); // first roll of the dice
 31
 32         // determine game status and point based on first roll
 33         switch ((DiceNames) sumOfDice)
 34         {
 35            case DiceNames.Seven: // win with 7 on first roll   
 36            case DiceNames.YoLeven: // win with 11 on first roll
 37               gameStatus = Status.Won;                         
 38               break;
 39            case DiceNames.SnakeEyes: // lose with 2 on first roll
 40            case DiceNames.Trey: // lose with 3 on first roll     
 41            case DiceNames.BoxCars: // lose with 12 on first roll 
 42               gameStatus = Status.Lost;                          
 43               break;
 44            default: // did not win or lose, so remember point  
 45               gameStatus = Status.Continue; // game is not over
 46               myPoint = sumOfDice; // remember the point       
 47               Console.WriteLine($"Point is {myPoint}");        
 48               break;
 49         }
 50
 51         // while game is not complete
 52         while (gameStatus == Status.Continue) // game not Won or Lost
 53         {
 54            sumOfDice = RollDice(); // roll dice again
 55
 56            // determine game status
 57            if (sumOfDice == myPoint) // win by making point
 58            {
 59               gameStatus = Status.Won;
 60            }
 61            else
 62            {
 63               // lose by rolling 7 before point
 64               if (sumOfDice == (int) DiceNames.Seven)
 65               {
 66                  gameStatus = Status.Lost;
 67               }
 68            }
 69         }
 70
 71         // display won or lost message
 72         if (gameStatus == Status.Won)
 73         {
 74            Console.WriteLine("Player wins");
 75         }
 76         else
 77         {
 78            Console.WriteLine("Player loses");
 79         }
 80      }
 81
 82      // roll dice, calculate sum and display results
 83      static int RollDice()
 84      {
 85         // pick random die values
 86         int die1 = randomNumbers.Next(1, 7); // first die roll
 87         int die2 = randomNumbers.Next(1, 7); // second die roll
 88
 89         int sum = die1 + die2; // sum of die values
 90
 91         // display results of this roll
 92         Console.WriteLine($"Player rolled {die1} + {die2} = {sum}");
 93         return sum; // return sum of dice
 94      }
 95   }

Player rolled 2 + 5 = 7
Player wins

Player rolled 2 + 1 = 3
Player loses
Player rolled 4 + 6 = 10

Point is 10
Player rolled 1 + 3 = 4
Player rolled 1 + 3 = 4
Player rolled 2 + 3 = 5
Player rolled 4 + 4 = 8
Player rolled 6 + 6 = 12
Player rolled 4 + 4 = 8
Player rolled 4 + 5 = 9
Player rolled 2 + 6 = 8
Player rolled 6 + 6 = 12
Player rolled 6 + 4 = 10
Player wins

Player rolled 2 + 4 = 6
Point is 6
Player rolled 3 + 1 = 4
Player rolled 5 + 5 = 10
Player rolled 6 + 1 = 7
Player loses

7.9.1 Method RollDice

In the rules of the game, the player must roll two dice on the first roll and must do the same on all subsequent rolls. We declare method RollDice (lines 83–94) to roll the dice and compute and display their sum. Method RollDice is declared once, but it’s called from two places (lines 30 and 54) in method Main, which contains the logic for one complete game of craps. Method RollDice takes no arguments, so it has an empty parameter list. Each time it’s called, RollDice returns the sum of the dice as an int. Although lines 86 and 87 look the same (except for the die names), they do not necessarily produce the same result. Each of these statements produces a random value in the range 1–6. Variable randomNumbers (used in lines 86–87) is not declared in the method. Rather it’s declared as a private static variable of the class and initialized in line 8. This enables us to create one Random object that’s reused in each call to RollDice.

7.9.2 Method Main’s Local Variables

The game is reasonably involved. The player may win or lose on the first roll or may win or lose on any subsequent roll. Method Main (lines 24–80) uses local variable gameStatus (line 27) to keep track of the overall game status, local variable myPoint (line 28) to store the “point” if the player does not win or lose on the first roll and local variable sumOfDice (line 30) to maintain the sum of the dice for the most recent roll. Variable myPoint is initialized to 0 to ensure that the app will compile. If you do not initialize myPoint, the compiler issues an error, because myPoint is not assigned a value in every case of the switch statement—thus, the app could try to use myPoint before it’s definitely assigned a value. By contrast, gameStatus does not require initialization because it’s assigned a value in every branch of the switch statement—thus, it’s guaranteed to be initialized before it’s used. However, as good practice, we initialize it anyway.

7.9.3 enum Type Status

Local variable gameStatus (line 27) is declared to be of a new type called Status, which we declared in line 11. Status is a user-defined type called an enumeration, which declares a set of constants represented by identifiers. An enumeration is introduced by the keyword enum and a type name (in this case, Status). As with a class, braces ({ and }) delimit the body of an enum declaration. Inside the braces is a comma-separated list of enumeration constants—by default, the first constant has the value 0 and each subsequent constant’s value is incremented by 1. The enum constant names must be unique, but the value associated with each constant need not be. Type Status is declared as a private member of class Craps, because Status is used only in that class.

Variables of type Status should be assigned only one of the three constants declared in the enumeration. When the game is won, the app sets local variable gameStatus to Status.Won (lines 37 and 59). When the game is lost, the app sets gameStatus to Status.Lost (lines 42 and 66). Otherwise, the app sets gameStatus to Status.Continue (line 45) to indicate that the dice must be rolled again.

Good Programming Practice 7.2

Using enumeration constants (like Status.Won, Status.Lost and Status.Continue) rather than literal integer values (such as 0, 1 and 2) can make code easier to read and maintain.

7.9.4 The First Roll

Line 30 in method Main calls RollDice, which picks two random values from 1 to 6, displays the value of the first die, the value of the second die and the sum of the dice, and returns the sum of the dice. Method Main next enters the switch statement at lines 33–49, which uses the sumOfDice value to determine whether the game has been won or lost, or whether it should continue with another roll.

7.9.5 enum Type DiceNames

The sums of the dice that would result in a win or loss on the first roll are declared in the DiceNames enumeration in lines 14–21. These are used in the switch statement’s cases. The identifier names use casino parlance for these sums. In the DiceNames enumeration, we assign a value explicitly to each identifier name. When the enum is declared, each constant in the enum declaration is a constant value of type int. If you do not assign a value to an identifier in the enum declaration, the compiler will do so. If the first enum constant is unassigned, the compiler gives it the value 0. If any other enum constant is unassigned, the compiler gives it a value one higher than that of the preceding enum constant. For example, in the Status enumeration, the compiler implicitly assigns 0 to Status.Continue, 1 to Status.Won and 2 to Status.Lost.

7.9.6 Underlying Type of an enum

You could also declare an enum’s underlying type to be byte, sbyte, short, ushort, int,


private enum MyEnum : typeName {Constant1, Constant2, ...}

where typeName represents one of the integral simple types.

7.9.7 Comparing Integers and enum Constants

If you need to compare a simple integral type value to the underlying value of an enumeration constant, you must use a cast operator to make the two types match—there are no implicit conversions between enum and integral types. In the switch expression (line 33), we use the cast operator to convert the int value in sumOfDice to type DiceNames and compare it to each of the constants in DiceNames. Lines 35–36 determine whether the player won on the first roll with Seven (7) or YoLeven (11). Lines 39–41 determine whether the player lost on the first roll with SnakeEyes (2), Trey (3) or BoxCars (12). After the first roll, if the game is not over, the default case (lines 44–48) saves sumOfDice in myPoint (line 46) and displays the point (line 47).

Additional Rolls of the Dice

If we’re still trying to “make our point” (i.e., the game is continuing from a prior roll), the loop in lines 52–69 executes. Line 54 rolls the dice again. If sumOfDice matches myPoint in line 57, line 59 sets gameStatus to Status.Won, and the loop terminates because the game is complete. In line 64, we use the cast operator (int) to obtain the underlying value of DiceNames.Seven so that we can compare it to sumOfDice. If sumOfDice is equal to Seven (7), line 66 sets gameStatus to Status.Lost, and the loop terminates because the game is over. When the game completes, lines 72–79 display a message indicating whether the player won or lost, and the app terminates.

Control Statements in the Craps Example

Note the use of the various program-control mechanisms we’ve discussed. The Craps class uses two methods—Main and RollDice (called twice from Main)—and the switch, while, ifelse and nested if control statements. Also, notice that we use multiple case labels in the switch statement to execute the same statements for sums of Seven and YoLeven (lines 35–36) and for sums of SnakeEyes, Trey and BoxCars (lines 39–41).

Code Snippets for Auto-Implemented Properties

Visual Studio has a feature called code snippets that allows you to insert predefined code templates into your source code. One such snippet enables you to easily create a switch statement with cases for all possible values for an enum type. Type switch in the C# code then press Tab twice. If you specify a variable of an enum type in the switch statement’s expression and press Enter, a case for each enum constant will be generated automatically.

To get a list of all available code snippets, type Ctrl + k, Ctrl + x. This displays the Insert Snippet window in the code editor. You can navigate through the Visual C# snippet folders with the mouse to see the snippets. This feature also can be accessed by right clicking in the source code editor and selecting the Insert Snippet… menu item.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset