Concepts / Using Static Subroutines in Programs

Using Static Subroutines in Programs

The GuessingGame program places the logic for one game in the static subroutine playGame(), and main() calls it inside a do-while loop so the user can play again.

  • Programming

Why One Game Gets Its Own Block

This article studies a complete Java program called GuessingGame. The computer picks a secret number between 1 and 100, and the user tries to guess it. After each game, the program asks whether the user wants to play again. Although the program is short, it brings together several ideas from this chapter: a static subroutine that holds the logic for one game, a main() method that calls that subroutine, a random number, a loop that keeps asking for input, and a conditional test that compares the guess with the secret number.

The central idea of the program is that the logic for one game lives in a single named block, playGame(), and main() decides when to run it. Before reading on, pause and ask yourself: if a program needs the same job done in several places, would you rather keep the instructions in one place or in every place they are needed? The main idea of subroutines is an answer to that question.

What do you think happens?

If a program contains a method named playGame() but nothing in the program ever writes playGame() as a call, what happens when the program runs?

  • playGame() runs once automatically at the start

  • playGame() runs once automatically at the end

  • playGame() does not run at all

Reveal answer
Answer:

playGame() does not run at all.

A subroutine runs when the program calls it. Defining a subroutine only gives it a name and a body. Without a call statement that reaches it, its instructions are never executed.

Tracing the Order of main and playGame

The GuessingGame class contains two static methods. The main() method prints an introduction, then runs a do-while loop. Inside that loop, a single statement calls playGame(), which plays one game. The source comments this line as calling the subroutine to play one game. When playGame() finishes and returns, control goes back to main(), which asks the user whether to play again and reads the answer with TextIO.getlnBoolean(). If the user chooses to play again, the do-while loop repeats and playGame() is called once more.

main() startsPrint introductiondo-while loop beginsCall playGame()playGame() plays one gameplayGame() returnsAsk play againTextIO.getlnBoolean()Play again?main() endsfirsttheneach passreturnsback in mainanswer readyesno
In what order do main() and playGame() run, and where does control go after playGame() returns when the user chooses to play again?

Trace the order by reading the flowchart from top to bottom. main() runs first and prints the introduction. The do-while loop then calls playGame(), and the rest of main() waits until playGame() returns. Only after the game ends does main() ask the play-again question. A yes answer sends control back to the top of the do-while loop, which calls playGame() again with a brand-new game. A no answer lets main() finish.

Anatomy of the playGame Header

java
Definition

A subroutine is a block of code that can be called multiple times from different parts of a program. In this header, the name is playGame() and the body is the code between its braces.

  • static means the method belongs to the class and can be called directly from main(), which is also static.
  • void means the subroutine does not return a value to its caller.
  • The parentheses are empty, so playGame() takes no input values from the caller in this program.

Local Variables Inside playGame

The first lines of playGame() declare three variables. Each one has a comment in the source that explains its role. computersNumber holds the random number picked by the computer. usersGuess holds a number entered by the user as a guess. guessCount holds the number of guesses the user has made so far.

VariableWhat it storesWhen it changes
computersNumberThe secret number picked by the computer, an integer from 1 to 100Assigned once at the start of each game
usersGuessThe most recent guess typed by the userReplaced each time the loop reads a new guess
guessCountHow many guesses the user has made so farReset to 0 at the start, increased by one per guess

The three local variables of playGame()

Because these variables are declared inside playGame(), they belong to that one subroutine. They exist while the game is being played and are not visible to main(). This is why each call to playGame() can start a fresh game with its own secret number and its own guess count, without any extra bookkeeping in main().

Picking the Secret Number

The computer's number is assigned with a single statement, and the source explains that the value is a randomly chosen integer between 1 and 100, inclusive. The expression that produces it is worth reading piece by piece, because each part does one job. The expression is (int)(100 * Math.random()) + 1.

Math.random()0.0 up to, not including1.0100 * Math.random()0.0 up to, not including100.0(int) castwhole numbers 0 to 99+ 1whole numbers 1 to 100multiply by 100drop decimal partshift up by 1
How does Math.random()'s range from 0 up to but not including 1 get mapped onto the integers 1 through 100 inclusive?
Worked example

Tracing one value through the expression

Suppose Math.random() happens to return 0.537. Trace the value through (int)(100 * Math.random()) + 1.

  1. 1
    Multiply

    100 * 0.537 gives 53.7.

  2. 2
    Cast to int

    (int) keeps only the whole-number part, so 53.7 becomes 53.

  3. 3
    Add 1

    53 + 1 gives 54, which is inside the range 1 to 100.

Result

The secret number would be 54. The +1 is what moves the range up from 0, so the smallest possible result is 1 and the largest is 100.

The example above uses a value of 0.537 chosen for illustration, not a value the program actually produces. The same steps apply to any value Math.random() returns, which is what allows the expression to cover the whole range of 1 to 100.

Reading and Counting Guesses in the Loop

After setting up the game, playGame() sets guessCount to 0, prints a prompt, and enters a while (true) loop. The source explains that this loop repeatedly asks the user for input. Inside the loop, TextIO.getInt() reads the user's guess and stores it in usersGuess, and guessCount++ adds one to the count of guesses. The comparison between the guess and the secret number follows in the same loop body.

Calls That Start Fresh Each Time

Because playGame() is a named block, main() can call it at several points during one run. Each call starts with its own local variables. When the first call finishes, control returns to the line in main() that made the call, and the second call later begins from the top of playGame() with a new secret number and a guess count of 0. The following small program is a generated illustration of the same pattern, with one static subroutine called at two points.

Code walkthrough
public class Greeting {
    static void sayHello() {
        System.out.println("Hello!");
    }

    public static void main(String[] args) {
        sayHello();
        sayHello();
    }
}
  1. 1
    The class Greeting contains two static methods, just as GuessingGame contains main() and playGame().
  2. 2
    sayHello() holds one printed line. Defining it does not print anything.
  3. 3
    The first call inside main() runs the body and prints Hello!.
  4. 4
    The second call runs the same body again and prints Hello! a second time.
  • sayHello() is defined once, as a static method.
  • main() calls sayHello() at two different points.
  • The body of sayHello() runs each time it is called.
Output
Hello!
Hello!
main()playGame() call 1its own secret number andcountplayGame() call 2fresh secret number andcount of 0first callreturnssecond callreturns
When main() calls playGame() several times, how does each call start fresh and then return control to the caller?

Mistakes When Defining and Calling Subroutines

  • Common mistake

    Expecting a defined subroutine to run by itself

    What it looks like

    Writing a complete static void playGame() method but never writing playGame() inside main().

    Why it fails

    A subroutine runs when the program calls it. Its definition is only a named block of instructions waiting to be used.

    Do this instead

    Place a call statement such as playGame() at each point where the game should be played.

  • Common mistake

    Assuming a subroutine's local variables are visible to main()

    What it looks like

    Trying to print guessCount from main() after playGame() returns.

    Why it fails

    The variables are declared inside playGame(), so they belong to that subroutine and are not visible to main().

    Do this instead

    Keep the work that needs those variables inside playGame(), or pass the result back through a mechanism designed for that purpose.

  • Common mistake

    Confusing the guessing range with the raw Math.random() output

    What it looks like

    Assuming (int)(100 * Math.random()) alone gives a value from 1 to 100.

    Why it fails

    Without the +1, the result starts at 0, so the range is shifted down by one.

    Do this instead

    Add 1 to the cast result to get a value from 1 to 100 inclusive.

  • Common mistake

    Forgetting that each call starts a fresh game, so a secret number from one game is not reused in the next

Where ThreeN2 Uses Subroutines

The source points to a second program, ThreeN2.java, introduced in Section 4.4 as an improved version of the 3N+1 program. It is described as a program that uses subroutines and prints its output in neat columns. The source gives two reasons for this design. The first is code organization: separating work into named blocks makes the program easier to read and manage. The second is output formatting: the subroutines are part of how the results are printed in neat columns.

The source does not list the individual subroutines inside ThreeN2.java or show their code, so this article does not describe them in detail. When you open the file yourself, look for the places where the program calls a named block of code, and compare them with the places where it prints results.

Practice Tracing Calls

EASY

A program defines static void showBanner() that prints a line of stars. main() calls showBanner() once, then prints the word Done, then calls showBanner() again. Write the output in order, and state which statement in main() causes each banner to print.

Hints
  • Defining showBanner() prints nothing by itself.
  • Each call to showBanner() runs the body once, and control then returns to main().
MEDIUM

Using the expression (int)(100 * Math.random()) + 1, state the smallest and largest values it can produce, and explain why the +1 is needed.

Hints
  • Consider the range that Math.random() covers before the multiplication.
  • Think about what (int) does to a decimal number.

Key Takeaways from the Trace

  1. 1
    A subroutine is a named block of code that runs only when a program calls it, and it can be called many times from different points.
  2. 2
    In GuessingGame, main() prints an introduction and then calls playGame() inside a do-while loop so the user can play again.
  3. 3
    The local variables computersNumber, usersGuess, and guessCount belong to playGame(), so each call starts a fresh game with a new secret number and a guess count of 0.
  4. 4
    The expression (int)(100 * Math.random()) + 1 produces an integer from 1 to 100 inclusive, and the +1 moves the range up from 0.

At a glance

  • A subroutine is a named block of code that runs only when a program calls it, so it can be reused at many points.
  • The GuessingGame program places the logic for one game in playGame(), and main() calls it inside a do-while loop.
  • Local variables declared inside playGame() start fresh on each call, giving every game its own secret number and guess count.
  • The expression (int)(100 * Math.random()) + 1 produces a random integer from 1 to 100 inclusive.