Let"s discuss the declaration of class Craps in Fig. 6.9. 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 6881) to roll the dice and compute and print their sum. Method rollDice is declared once, but it is called from two places (lines 26 and 50) in method play, 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 is called, rollDice returns the sum of the dice, so the return type int is indicated in the method header (line 68). Although lines 71 and 72 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 16. Note that randomNumbers (used in lines 7172) is not declared in the method. Rather it is declared as a private instance variable of the class and initialized in line 8. This enables us to create one Random that is reused in each call to rollDice.
--------------------------------------------------------------------------------
[Page 251]
--------------------------------------------------------------------------------
[Page 252]
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 play (lines 2165) uses local variable myPoint (line 23) to store the "point" if the player does not win or lose on the first roll, local variable gameStatus (line 24) to keep track of the overall game status and local variable sumOfDice (line 26) to maintain the sum of the dice for the most recent roll. Note that myPoint is initialized to 0 to ensure that the application will compile. If you do not initialize myPoint, the compiler issues an error, because myPoint is not assigned a value in every branch of the switch statement, and thus the program could try to use myPoint before it is assigned a value. By contrast, gameStatus does not require initialization because it is assigned a value in every branch of the switch statementthus, it is guaranteed to be initialized before it is used.
--------------------------------------------------------------------------------
[Page 253]
Note that local variable gameStatus is declared to be of a new type called Status, which we declared at line 11. Type Status is declared as a private member of class Craps, because Status will be used only in that class. Status is a programmer-declared type called an enumeration, which, in its simplest form, declares a set of constants represented by identifiers. An enumeration is a special kind of class that is introduced by the keyword enum (new to J2SE 5.0) and a type name (in this case, Status). As with any class, braces ({ and }) delimit the body of an enum declaration. Inside the braces is a comma-separated list of enumeration constants, each representing a unique value. The identifiers in an enum must be unique. (You will learn more about enumerations in Chapter 8.)
--------------------------------------------------------------------------------
[Page 254]
Good Programming Practice 6.3
Use only uppercase letters in the names of constants. This makes the constants stand out in a program and reminds the programmer that enumeration constants are not variables.
Variables of type Status can be assigned only one of the three constants declared in the enumeration or a compilation error will occur. When the game is won, the program sets local variable gameStatus to Status.WON (lines 33 and 54). When the game is lost, the program sets local variable gameStatus to Status.LOST (lines 38 and 57). Otherwise, the program sets local variable gameStatus to Status.CONTINUE (line 41) to indicate that the dice must be rolled again.
Good Programming Practice 6.4
Using enumeration constants (like Status.WON, Status.LOST and Status.CONTINUE) rather than literal integer values (such as 0, 1 and 2) can make programs easier to read and maintain.
Line 26 in method play calls rollDice, which picks two random values from 1 to 6, s 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 play next enters the switch statement at lines 2945, which uses the sumOfDice value from line 26 to determine whether the game has been won or lost, or whether it should continue with another roll. The sums of the dice that would result in a win or loss on the first roll are declared as public final static int constants in lines 1418. These are used in the cases of the switch statement. The identifier names use casino parlance for these sums. Note that these constants, like enum constants, are declared with all capital letters by convention, to make them stand out in the program. Lines 3134 determine whether the player won on the first roll with SEVEN (7) or YO_LEVEN (11). Lines 3539 determine whether the player lost on the first roll with SNAKE_EYES (2), TREY (3), or BOX_CARS (12). After the first roll, if the game is not over, the default case (lines 4044) saves sumOfDice in myPoint (line 42) and s the point (line 43).
If we are still trying to "make our point" (i.e., the game is continuing from a prior roll), the loop in lines 4858 executes. Line 50 rolls the dice again. In line 53, if sumOfDice matches myPoint, line 54 sets gameStatus to Status.WON, then the loop terminates because the game is complete. In line 56, if sumOfDice is equal to SEVEN (7), line 57 sets gameStatus to Status.LOST, and the loop terminates because the game is complete. When the game completes, lines 6164 a message indicating whether the player won or lost and the program terminates.
Note the use of the various program-control mechanisms we have discussed. The Craps class uses three methodsmain, play (called from main) and rollDice (called twice from play)and the switch, while, if...else and nested if control statements. Note also the use of multiple case labels in the switch statement to execute the same statements for sums of SEVEN and YO_LEVEN (lines 3132) and for sums of SNAKE_EYES, TREY and BOX_CARS (lines 3537).
You might be wondering why we declared the sums of the dice as public final static int constants rather than as enum constants. The answer lies in the fact that the program must compare int sumOfDice (line 26) to these constants to determine the outcome of each roll. Suppose we were to declare enum Sum containing constants (e.g., Sum.SNAKE_EYES) representing the five sums used in the game, then use these constants in place of the final variables in the cases of the switch statement (lines 2945). Doing so would prevent us from using sumOfDice as the switch statement"s controlling expressionJava does not allow an int to be compared to an enumeration constant. To achieve the same functionality as the current program, we would have to use a variable currentSum of type Sum as the switch"s controlling expression. Unfortunately, Java does not provide an easy way to convert an int value to a particular enum constant. To translate an int into an enum constant would require a separate switch statement. Clearly this would be cumbersome and not improve the readability of the program (thus defeating the purpose of using an enum), so we are better off using public final static int constants to represent the sums of the dice.