On methods that return booleans

I have seen many students confused about methods that return boolean values, and using these values. This FAQ is going to focus on methods that return booleans (true/false).

This guide is structured by starting with a few questions that have been asked, and then a few common problems. The objective of the guide is to give you a few more ideas of patterns/ strategies you can use to work with booleans. You will still need to work out how to apply the ideas here to your assignment, but this should give you an awareness of the things you can do.

Question 1: Why do we even write code that is in more than one method? Doesn't this just make the code more difficult to write, due to having to worry about parameters and return values?

I've seen a few students wondering this. I've been asked a few times if the given template has to be followed. i.e. can't the playRound(...) method be removed, and the entire program written with just the playGame(...) method? Yes, it can. No, you should not do it. It is terrible design and will lose you marks. You need to learn to understand parameters and return values, and by not doing the assignment the way we intended, we can’t give you full marks!

Question 2: Can I use a variable outside of the methods? Can I change the return type to something else? No, and no. The first is terrible design, and the second is problematic in that most students won’t be wanting to do it in a way that is good design. Please do not make any modifications to the template.

Problem 1: What is the point of methods that can return booleans? How can we use them?

Lets look at an example. Remember that exercise where you had to print out whether or not the word the user entered had 7 letters in it and started with P? It was called "word game". What if we wanted to extend that into a (somewhat pointless) word game where the user has to keep guessing words until they guess a word that matches the pattern (starts with p and has 7 letters). We want to design our program nicely, using 2 methods. A good way of doing this is to break it down in a similar way to your GuessingGame assignment. We should use a method that plays a single round (in the case taking a single guess and determining whether or not it was right), and then another method that controls the overall game. The following code is one way of writing such a program.

public class WordGameExtended{
    public void playWordGame() {
        int numberOfGuesses = 0;
        while (true) { 
             // Use a “forever” loop
             String guess = UI.askString("What word would you like to guess?");

             numberOfGuesses = numberOfGuesses + 1; 
             // Each loop iteration means another guess
             boolean hasWon = playWordRound(guess);
             if (hasWon) {                       
                   // equivalent to (hasWon == true)
                   break; //Break out of the loop
             }
        }
        UI.println("You won, after making " + 
                       numberOfGuesses + " guesses");
    }

    public boolean playWordRound(String word) {
        if (word.startsWith("p") && word.length() == 7) {
           return true;
        }
        else {
            return false;
        }
    }
}

The playWordRound(...) method’s job is to say whether or not a given word meets the constraints. It does this by taking the word that was guessed as a parameter, and determines whether or not it meets the constraint. If it does meet the constraint, true is returned. Otherwise, false is returned. This method can be used by other methods to determine whether or not

Remember how the Math.random(...) function can be used by your code to get a random number, without you having to know how to actually generate a random number? This is very similar. Other parts of your code now have this useful method that tells them whether or not a given word meets the constraint.

playWordGame() has to take guesses from the user, and determine whether or not they meet the constraint. It needs to keep track of how many rounds have been played so far (as at the end it needs to say how long the guess took), and it needs to check one word each iteration around the loop. Because playWordRound(...) returns a boolean, we can put this result into a variable of type boolean. When hasWon eventually has true in it (because the user guessed correctly, and playWordRound(...) returned true), we want to break out of the loop.

One other design decision worth noting: We didn’t HAVE to use a parameter for playWordRound(...). It would have been fine for playWordRound(...) to make the call to UI.askString(....) in order to get a word. The only difference in the code would be that playWordGame(...) would no longer have to pass in a word. In terms of design, the way I went about it is probably better, as it means that playWordRound(...) would work even if words were not obtained using UI.askString(...). In the next example, we get words using UI.next(). We can still use the same method!

Your GuessingGame assignment is a little more complicated than this. One thing that makes it more difficult is that we need to do a little bit more with the return values from playRound(...) than this example does. Another issue is that your playRound(...) method has to have a loop in it (as opposed to a straight forward if-else) because a round contains multiple guesses.. In the next parts of this guide, we will look at examples that should help you understand these concepts.

Problem 2: How can we use boolean return values? How do I keep track of counts?

In the previous example, you saw a very simple usage of a boolean value that was returned from the playRound(...) method. We put it into a variable, and then used it to determine whether or not our loop should continue.

Some students have been confused about how they can get counts into their playGame(...) method from playRound(...). But it turns out that we can do more with booleans than you might have initially thought!

Lets write a program that gets the user to make word guesses until they are bored and enter “done”. and at the end it prints out what how many guesses were right, how many guesses were wrong, and what percentage of guesses were right.

We can do this using the same playRound(...) method as before that takes a word for its parameter, and returns whether or not the word meets the constraint. In particular, focus on how

Not yet tested: This guide was written in a frantic hurry! It probably doesn’t compile. Will check it later.

public void doStatisticsOnGuesses() {
    double correctGs = 0;
    double wrongGs = 0;
    String guess = UI.next();
    while (!guess.equals(“done”)) {
        boolean guessResult = playWordRound(guess);
        if (guessResult) {
            correctGs =  correctGs + 1;
        }
        else { //It must be false
            wrongGs = wrongGs  + 1;
        }

        //Read the guess ready for the next round
        guess = UI.next(); 
    }
    double totalGs = correctGs + wrongGs;
    UI.println(“Total number of guesses: “ + totalGs);
    UI.println(“Correct guesses: “ + correctGs);
    UI.println(“Wrong guesses: “ + wrongGs);
    double percentageCorrect = correctGs / totalGs * 100;
    UI.println(“Percentage correct: “ + percentageCorrect + “%”); 
}

Exactly the same method as before! This is a good example of code reuse. We aren’t using playWordGame(...) anymore, but we don’t have to rewrite code that can check if a word matches the constraints.

public boolean playWordRound(String word) {
     if (word.startsWith("p") && word.length() == 7) {
         return true;
     }
     else {
          return false;
     }
}
 

The key thing to notice is that playWordRound(...) simply returns a boolean, and then doStatisticsOnGuesses(...) is able to use these return values to do a lot more than you might have initially thought could be done.

Of course there were other ways of writing this code. We could have tracked how many correct guesses there were, and how many guesses there were in total. Then we could have calculated the wrong guesses by subtracting the correct guesses from the total. This is common in programming; there is nearly never one right answer.

Problem 3: When do we actually need to return a value?

I have seen numerous examples in the online help where EVERY guess that is made results in a value returned in the playRound(...) method. This means that the loop can never iterate more than once, which is not what we want!

Recall that when a value is returned, the method stops, and the value is returned back to the method that called it. The method that called it just wants to get one result from calling the method.The purpose of return values is to return a value once the method is finished doing its job.

For the playRound(...) method, you only want to return a value when the round is over. What does this actually mean for your code design?

Lets look at another example.

This time, we want to write a program that tracks weights packed into a bag. We want to fill the bag so that it contains at least 100 KG’s. We should do this by making the user enter numbers until the bag is full. The weights are added to the total. If this is done successfully, true should be returned. If the user attempts to add an item that is over 50 KG’s, false should be returned.

So, in summary we want to add numbers until either of the two conditions is met:
  1. The total weight is greater than or equal to 100 KG’s (successful, return true)
  2. An item over 50 KG’s is added to the bag (failure, return false)
In all other cases we do not want to return a value, as the method isn’t yet finished doing its job.

We are assuming that no non numerical input is entered (extension question: how could we modify this code so that it would not crash if non numbers, e.g. words, were entered?)

public boolean packWeights() {
    UI.println(“Enter weights: “);
    double totalWeight = 0;
    while (true) {                    
         // A “forever” loop; we break out of the loop when done
        double nextWeight = UI.nextInt();
        if (nextWeight >= 50) { 
              //This weight is too heavy!
              return false;
        else {
              totalWeight = totalWeight + nextWeight;
        }
        if (totalWeight > = 100) { 
            // Perfect!
            return true;
        }
   }
}

In the case of a weight not being over 50 KG’s, and not bringing the total over 100 KG’s, we do not return a value as we are not yet finished. We simply update the totalWeight variable and go around the loop again.

Your playRound(...) method for the GuessingGame also needs to only return a value when it is finished. It should return true when the user actually wins, and false when they run out of guesses. In all the other cases, it should simply update its variables it is using to keep track of the game, NOT returning a value. Once the user has either won or lost the round, then it should return a value.

Another method could then call packWeights(...), and put its return value into a variable, like the above code examples did.