Generating Random Numbers in Java
The exercise splits the program into main(), which repeats games, and the static subroutine playGame(), which plays one round.
A Guessing Game Built from Two Methods
This exercise is a small Java program in which the computer picks a secret number between 1 and 100 and the user tries to guess it. The program is built from ideas you have already seen: method calls, loops, and conditional decisions. Rather than reading the code first, treat the program as a question. Ask yourself which part of the program decides when a game starts, when it ends, and when the whole session stops.
What do you think happens?
Inside playGame(), the variable computersNumber is set by a random expression. Could it ever hold 0 or 101?
Reveal answer
Neither is possible. The computer's number is always an integer from 1 to 100 inclusive.
The source states that the expression (int)(100 * Math.random()) + 1 produces a randomly chosen integer between 1 and 100, inclusive. The rest of this article explains the arithmetic that guarantees that range.
Splitting Responsibilities Between main() and playGame()
The program has two parts that matter most for this lesson. The main() method prints a greeting, repeatedly plays a game, and asks the user whether to play again. The playGame() method holds the logic for one complete round. Keep these two roles separate in your mind, because most debugging questions about this program come down to asking which of the two parts produced a given result.
The keyword static is what allows main() to call playGame() directly without creating an object first. The return type void means playGame() does not hand a value back to its caller. Its job is to play the game and print the results. Because the whole game lives inside its own subroutine, main() reads almost like a plain list of steps: greet the user, play a game, ask whether to continue, and say goodbye. The details of how a single game works are hidden inside playGame().
main()is the outer driver: it repeats whole gamesplayGame()is a static subroutine with void return type- The do-while loop calls
playGame()once before any question is asked
Tracing One Round Inside playGame()
Trace the program by keeping a running record of which method is currently executing. Execution starts in main(), which prints the two greeting lines and then reaches the do-while loop. When it reaches the call to playGame(), control moves into that subroutine. There the local variables are declared and the computer's number is chosen. Control returns to main() only after playGame() finishes. Then main() asks the play-again question and reads the answer.
Following one round from call to return
Trace what happens between the moment main() reaches playGame() and the moment main() asks the play-again question.
- 1Step 1: main() makes the call
Execution is inside the do-while body in
main(). It reaches the statementplayGame(), and control transfers into that subroutine. - 2Step 2: playGame() chooses the secret number
Inside
playGame(), the local variablecomputersNumberis assigned the value of the random expression. This is the only place in the source-described flow where the secret number is set. - 3Step 3: playGame() finishes the round
The round plays out inside
playGame()and results are printed. Only whenplayGame()finishes does control go back tomain(). - 4Step 4: main() asks the question
Back in
main(),System.out.printdisplays the play-again question, andTextIO.getlnBoolean()reads the answer intoplayAgain.
Each round is a self-contained trip into playGame(), and main() regains control at a single well-defined point.
Turning Math.random() into a Whole Number
Inside playGame(), the computer's secret number is stored in a variable named computersNumber. It is assigned using Math.random(), which generates a random double between 0.0 and 1.0. A double is a number with a fractional part, so Math.random() on its own cannot give you a whole number from 1 to 100. The program has to scale and shift the value into the range it needs.
The source uses this pattern with a maximum of 100 and a minimum of 1, giving the assignment computersNumber = (int)(100 * Math.random()) + 1;. Each of the three parts does a specific job. The multiplication by 100 stretches the random value across a larger span, from 0.0 up to but not including 100.0. The (int) cast discards the fractional part, leaving a whole number from 0 to 99. Adding 1 shifts the result so that the lowest possible value becomes 1 instead of 0.
computersNumber = (int)(100 * Math.random()) + 1computersNumber = (int)(100 * Math.random()) + 1- 1
Math.random()returns a double r with 0.0 less than or equal to r and r less than 1.0 - 2100 * r is a double in the range from 0.0 up to but not including 100.0
- 3(int) cast truncates the fractional part, so the result is an integer from 0 to 99
- 4Adding 1 shifts the range so the final value is an integer from 1 to 100 inclusive
The diagram above shows the idea. Each slice of width 0.01 in the random value becomes one of the 100 possible results once it is multiplied by 100, truncated, and shifted by 1. The first slice, from 0.00 up to 0.01, produces 1. The last slice, from 0.99 up to 1.00, produces 100. Because Math.random() never returns exactly 1.0, the top of the range is never reached, so the value 1.0 cannot make the result 101. The middle slots follow the same pattern and are omitted from the figure.
Why the Do-While Loop Plays at Least Once
main() does not play just one game. It uses a do-while loop to repeat playGame() until the user chooses to stop. A do-while loop executes its block of code at least once before the condition is checked. The body runs, then the condition is tested. If the condition is still true, the body runs again.
In the exercise, the body of the loop calls playGame() and then asks, using System.out.print, whether the user would like to play again. The answer is read into the boolean variable playAgain with TextIO.getlnBoolean(). The loop condition is while (playAgain). The condition is checked after each iteration, so the program always plays at least one game, and it plays another only when the user's answer was true. The first game always starts before the play-again question is asked.
do {
playGame();
System.out.print("Would you like to play again? ");
playAgain = TextIO.getlnBoolean();
} while (playAgain);- 1The do keyword starts the body, so there is no check before the first pass.
- 2
playGame()plays one full round and control returns here when it finishes. - 3The play-again question is printed and the user's answer is stored in
playAgain. - 4The while (
playAgain) condition is tested only after the body has run. - 5If
playAgainis true, control jumps back to the top of the body; if false, the loop ends.
Mistakes Beginners Make When Tracing This Program
Common mistake Thinking Math.random() returns a whole number from 1 to 100
What it looks likePrinting Math.random() directly and expecting a value like 37
Why it failsMath.random()returns a double from 0.0 up to but not including 1.0, so it has a fractional part.Do this insteadApply the scale and shift: (int)(100 * Math.random()) + 1.
Common mistake Dropping the + 1 from the expression
What it looks likecomputersNumber = (int)(100 * Math.random()); which can produce 0Why it failsWithout the shift, the lowest value from the cast is 0, not 1.
Do this insteadKeep + 1 so the range becomes 1 to 100 inclusive.
Common mistake Assuming a while loop and a do-while loop behave identically
Why it failsA do-while loop checks its condition after each iteration, so its body runs at least once.
Do this insteadUse the do-while form when the first pass must always happen, as the first game does here.
Common mistake Looking for the bug in the wrong method
Why it failsA wrong secret number points at
playGame(), a game that never starts points at the call inmain(), and wrong play-again behavior points at the loop condition inmain().Do this insteadFind the boundary between main() and playGame() first, then narrow down which side produced the result.
Tracing a Run to Find Where It Diverges
Suppose you run the program and a round ends in an unexpected way. Start by asking where the result diverged from what you expected. If the value of computersNumber was wrong, the problem is likely in the random number expression inside playGame(). If the game never started, look at the call from main(). If the program asked the play-again question and then behaved wrongly, look at the do-while condition in main(). Locating the boundary between the two methods is usually the fastest way to narrow the problem.
A friend says the expression (int)(100 * Math.random()) + 1 can never produce 50. Using the slot idea from earlier, explain whether that claim is true. Then describe which random values would produce 50.
Hints
- Work out which range of
Math.random()values gives an integer of 49 after the cast, since adding 1 then gives 50. - The slot for result 50 covers random values from 0.49 up to but not including 0.50.
Predict the flow of control for this scenario: the user answers false to the first play-again question. Write the order in which main() and playGame() run from the start of the program until the goodbye message is printed.
Hints
- Count how many times
playGame()is called before the condition is tested. - The condition is tested after the body, so the answer to the question determines whether the loop repeats.
Key Takeaways
- The program splits work between
main(), which repeats games and asks the play-again question, and the static void subroutineplayGame(), which plays one round. Math.random()returns a double from 0.0 up to but not including 1.0, and (int)(100 *Math.random()) + 1 scales, truncates, and shifts it to an integer from 1 to 100 inclusive.- A do-while loop runs its body at least once and checks its condition after each iteration, which is why the first game always starts before the play-again question is asked.
- When a result looks wrong, identify which method and which line produced it, starting from the boundary between
main()andplayGame().