A popular game of chance is a 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.
Figure 6.7 simulates the game of craps, using methods to implement the game’s logic. The
mainmethod (lines 21–65) calls therollDicemethod (lines 68–81) as necessary to roll the dice and compute their sum. The sample outputs show winning and losing on the first roll, and winning and losing on a subsequent roll.
Random randomNumbers = new Random( seedValue );
randomNumbers.set( seedValue );
Error-Prevention Tip 6.1
While developing a program, create theRandomobject with a specific seed value to produce a repeatable sequence of numbers each time the program executes. If a logic error occurs, fix the error and test the program again with the same seed value—this allows you to re- construct the same sequence of numbers that caused the error. Once the logic errors have been removed, create theRandomobject without using a seed value, causing theRandom
object to generate a new sequence of random numbers each time the program executes.
1 // Fig. 6.7: Craps.java
2 // Craps class simulates the dice game craps.
3 import java.util.Random;
Fig. 6.7 | Crapsclass simulates the dice game craps. (Part 1 of 3.)
6.9 Case Study: A Game of Chance; Introducing Enumerations 131
4
5 public class Craps 6 {
7 // create random number generator for use in method rollDice 8 private static final Random randomNumbers = new Random();
9 10 11 12
13 // constants that represent common rolls of the dice 14
15 16 17 18 19
20 // plays one game of craps
21 public static void main( String[] args )
22 {
23 int myPoint = 0; // point if no win or loss on first roll 24
25 26 27
28 // determine game status and point based on first roll 29 switch ( sumOfDice )
30 {
31 32 33
34 break;
35 36 37 38
39 break;
40 41 42
43 System.out.printf( "Point is %d\n", myPoint );
44 break; // optional at end of switch 45 } // end switch
46
47 // while game is not complete
48 while ( ) // not WON or LOST
49 {
50 51
52 // determine game status
53 if ( sumOfDice == myPoint ) // win by making point
54 ;
Fig. 6.7 | Crapsclass simulates the dice game craps. (Part 2 of 3.)
// enumeration with constants that represent the game status private enum Status { CONTINUE, WON, LOST };
private static final int SNAKE_EYES = 2;
private static final int TREY = 3;
private static final int SEVEN = 7;
private static final int YO_LEVEN = 11;
private static final int BOX_CARS = 12;
Status gameStatus; // can contain CONTINUE, WON or LOST int sumOfDice = rollDice(); // first roll of the dice
case SEVEN: // win with 7 on first roll case YO_LEVEN: // win with 11 on first roll
gameStatus = Status.WON;
case SNAKE_EYES: // lose with 2 on first roll case TREY: // lose with 3 on first roll case BOX_CARS: // lose with 12 on first roll
gameStatus = Status.LOST;
default: // did not win or lose, so remember point gameStatus = Status.CONTINUE; // game is not over myPoint = sumOfDice; // remember the point
gameStatus == Status.CONTINUE
sumOfDice = rollDice(); // roll dice again
gameStatus = Status.WON
132 Chapter 6 Methods: A Deeper Look
55 else
56 if ( sumOfDice == SEVEN ) // lose by rolling 7 before point 57
58 } // end while 59
60 // display won or lost message
61 if ( )
62 System.out.println( "Player wins" );
63 else
64 System.out.println( "Player loses" );
65 } // end main 66
67 // roll dice, calculate sum and display results 68
69 {
70 // pick random die values
71 int die1 = 1 + randomNumbers.nextInt( 6 ); // first die roll 72 int die2 = 1 + randomNumbers.nextInt( 6 ); // second die roll 73
74 int sum = die1 + die2; // sum of die values 75
76 // display results of this roll
77 System.out.printf( "Player rolled %d + %d = %d\n", 78 die1, die2, sum );
79 80
81 } // end method rollDice 82 } // end class Craps
Player rolled 5 + 6 = 11 Player wins
Player rolled 5 + 4 = 9 Point is 9
Player rolled 4 + 2 = 6 Player rolled 3 + 6 = 9 Player wins
Player rolled 1 + 2 = 3 Player loses
Player rolled 2 + 6 = 8 Point is 8
Player rolled 5 + 1 = 6 Player rolled 2 + 1 = 3 Player rolled 1 + 6 = 7 Player loses
Fig. 6.7 | Crapsclass simulates the dice game craps. (Part 3 of 3.)
gameStatus = Status.LOST;
gameStatus == Status.WON
public static int rollDice()
return sum; // return sum of dice
6.9 Case Study: A Game of Chance; Introducing Enumerations 133
MethodrollDice
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 methodrollDice(Fig. 6.7, lines 68–81) to roll the dice and compute and print their sum. MethodrollDiceis declared once, but it’s called from two places (lines 26 and 50) inmain, which contains the logic for one complete game of craps. MethodrollDicetakes no arguments, so it has an empty parameter list.
Each time it’s called,rollDicereturns the sum of the dice, so the return typeintis indi- cated 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 1–6. VariablerandomNumbers(used in lines 71–72) isnotdeclared in the method. Instead it’s declared as aprivate static finalvariable of the class and initialized in line 8. This enables us to create oneRandomobject that’s reused in each call torollDice. If there were a program that contained multiple instances of class
Craps, they’d all share this oneRandomobject.
Methodmain’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. Methodmain(lines 21–65) uses local variablemyPoint(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 variablesumOfDice (line 26) to hold the sum of the dice for the most recent roll. VariablemyPointis initialized to0to ensure that the application will compile. If you do not initializemyPoint, the com- piler issues an error, becausemyPointis not assigned a value ineverycaseof theswitch statement, and thus the program could try to usemyPointbefore it’s assigned a value. By contrast,gameStatusisassigned a value ineverycaseof theswitchstatement—thus, it’s guaranteed to be initialized before it’s used and does not need to be initialized.
enumTypeStatus
Local variablegameStatus(line 24) is declared to be of a new type calledStatus(declared at line 11). TypeStatusis aprivatemember of classCraps, becauseStatuswill be used only in that class.Statusis a type called anenumeration, which, in its simplest form, de- clares a set of constants represented by identifiers. An enumeration is a special kind of class that’s introduced by the keywordenumand a type name (in this case,Status). As with classes, braces delimit anenumdeclaration’s body. Inside the braces is a comma-separated list ofenumeration constants, each representing a unique value. The identifiers in anenum must be unique. You’ll learn more about enumerations in Chapter 8.
Variables of typeStatuscan be assigned only the three constants declared in the enu- meration (line 11) or a compilation error will occur. When the game is won, the program sets local variablegameStatustoStatus.WON(lines 33 and 54). When the game is lost, the program sets local variablegameStatustoStatus.LOST(lines 38 and 57). Otherwise, the program sets local variablegameStatustoStatus.CONTINUE(line 41) to indicate that the game is not over and the dice must be rolled again.
Good Programming Practice 6.1
By convention, we use only uppercase letters in the names of enumeration constants. This makes them stand out and reminds you that they’re not variables.
134 Chapter 6 Methods: A Deeper Look
Logic of themainMethod
Line 26 inmaincallsrollDice, which picks two random values from 1 to 6, displays the values of the first die, the second die and their sum, and returns the sum. Methodmain next enters theswitchstatement (lines 29–45), which uses thesumOfDicevalue from line 26 to determine whether the game has been won or lost, or should continue with another roll. The values that result in a win or loss on the first roll are declared aspublic static final intconstants in lines 14–18. The identifier names use casino parlance for these sums. These constants, likeenumconstants, are declared by convention with all capital let- ters, to make them stand out in the program. Lines 31–34 determine whether the player won on the first roll withSEVEN(7) orYO_LEVEN(11). Lines 35–39 determine whether the player lost on the first roll withSNAKE_EYES(2),TREY(3), orBOX_CARS(12). After the first roll, if the game is not over, the defaultcase (lines 40–44) sets gameStatus toSta-
tus.CONTINUE, savessumOfDiceinmyPointand displays the point.
If we’re still trying to “make our point” (i.e., the game is continuing from a prior roll), lines 48–58 execute. Line 50 rolls the dice again. IfsumOfDicematchesmyPoint(line 53), line 54 setsgameStatustoStatus.WON, then the loop terminates because the game is com- plete. IfsumOfDiceisSEVEN(line 56), line 57 setsgameStatustoStatus.LOST, and the loop terminates because the game is complete. When the game completes, lines 61–64 dis- play a message indicating whether the player won or lost, and the program terminates.
The program uses the various program-control mechanisms we’ve discussed. The
Craps class uses two methods—main androllDice(called twice from main)—and the
switch,while,if…elseand nestedifcontrol statements. Note also the use of multiple
caselabels in theswitchstatement to execute the same statements for sums ofSEVENand
YO_LEVEN(lines 31–32) and for sums ofSNAKE_EYES,TREYandBOX_CARS(lines 35–37).
Why Some Constants Are Not Defined asenumConstants
You might be wondering why we declared the sums of the dice aspublic final static intconstants rather than asenumconstants. The reason is that the program must compare theintvariablesumOfDice(line 26) to these constants to determine the outcome of each roll. Suppose we declared enum Sumcontaining constants (e.g.,Sum.SNAKE_EYES) repre- senting the five sums used in the game, then used these constants in theswitchstatement (lines 29–45). Doing so would prevent us from usingsumOfDiceas theswitchstatement’s controlling expression, because Javadoes not allow anintto be compared to an enumeration constant. To achieve the same functionality as the current program, we would have to use a variablecurrentSumof typeSumas theswitch’s controlling expression. Unfortunately, Java does not provide an easy way to convert anintvalue to a particularenumconstant.
This could be done with a separateswitchstatement. Clearly this would be cumbersome and not improve the program’s readability (thus defeating the purpose of using anenum).