CS 161 Lab #7

March 7th

Goals:

This week it's lots more practice with loops. We'll use loops to investigate the behavior of Die instances, and there's even an excuse to use an ArrayList again! For situations where a count-controlled loop is called for, it would be good practice to start using for loops and reserve while for event-controlled loops. You can look back at some of the code we wrote in class for inspiration if needed. (For example, Notebook.java and DieRoller.java.)

Getting Started

  1. Take a moment to introduce yourself to your partner(s). After social pleasantries are complete, pick one member of the team to be the "typer". They'll share their screen while editing the lab code in BlueJ. (I think BlueJ works better for these interactions than Eclipse. It's easier to see when sharing screens, and makes it easier to quickly test individual methods than Eclipse does.) Group members should contribute equally while working through the problems below and discuss all code to be written, though only the "typer" will be able to edit code. Resist the temptation to have both members work simultaneously in BlueJ — you're much more likely to "drift apart" over the course of the lab if you do so. The goal here is to have a partner who's engaged on exactly the same step of the lab as you are.

    Consider letting the less experienced member of the group do the typing if it seems like there's a mismatch in experience or comfort levels. That will help make sure they don't get left behind. Worst case, flip a coin to see who does the typing. Or have Java generate a random boolean value. (Type "(new java.util.Random()).nextBoolean()" in the codepad without the double quotes.)

    At the conclusion of the lab, make arrangements for the typer to share a copy of the code with the other member(s) of the group if desired. (E.g. email it, or put it on a shared Google drive, etc.) My solutions to the lab will get posted as well.

  2. Download the LoopsLab project and extract its contents, then start BlueJ and open the project.

Directions:

  1. Note that the Die code from class is part of this project. You won't need to make any changes to that code. All of your work will be in the LoopPractice class. There's only one method in it at the moment, and it's broken. Take a look at the rollNTimes method and see if you can spot the bug without running it. If it's not clear what's wrong with it, try running it and see if that helps. Fix the method and verify that it works before moving on. (You'll need to create a Die instance on the Object Bench before you can test it, since you need to pass in a Die along with the number of times you'd like to see it rolled.)
  2. Style alert: That code you just fixed used a while loop to implement a count-controlled loop. Now that we know about for loops, try to rewrite the method to use a for loop instead of a while.
  3. One way to convince ourselves that the Die is truly random is to roll it a bunch of times and see if the average of the rolls is what we'd expect. Finish the definition of averageRoll. It takes a Die and the number of rolls to be performed, rolls the Die the specified number of times, and returns the average of the rolled values. You'll need to keep track of the number of rolls, and their sum, as you go. Both should be ints, but you'll need to cast one as a double when doing the math on them if you want the average to be precise. Test your method on Die instances with various numbers of sides. Is the average reasonable?
  4. When we were testing the code in the Die's roll method, it took a bit of work to verify it was picking random numbers within the proper range. Another way to test the Die class would be to make a bunch of rolls and verify that the smallest result is a 1 and the largest corresponds to the number of sides on the Die. Finish the definition of findMinAndMax, which takes a Die and the desired number of rolls. It should roll the Die the specified number of times, keeping track of the largest and smallest values rolled. The method should print the values after making the rolls. For example:
    > Die d = new Die();
    > LoopPractice loopy = new LoopPractice();
    > loopy.findMinAndMax(d, 4);
    After 4 rolls, the min was 3 and the max was 5
    > loopy.findMinAndMax(d, 200);
    After 200 rolls, the min was 1 and the max was 6
    > d = new Die(10);
    > loopy.findMinAndMax(d, 200);
    After 200 rolls, the min was 1 and the max was 10
    
  5. The problem with the previous method is that it doesn't guarantee that we find the largest and smallest possible values, no matter how many rolls we make. (We might get really unlucky and not roll a 6 after dozens or even hundreds of rolls.) We'll write an improved version that doesn't stop until it has rolled both a 1 and the largest value possible on a given Die: Finish the implementation of rollUntilExtremesFound. It takes a Die, and keeps rolling it until it has produced both a 1 and the largest possible value. (There's a getNumSides() method in the Die class.) When the method is finished, it should print how many rolls it took to generate both values. (Hint: One way to do this is to use a pair of boolean variables — one that records whether you've seen a 1 and the other records whether you've seen the max value — and use them both in the boolean expression in the loop.)
    > Die d = new Die();
    > LoopPractice loopy = new LoopPractice();
    > loopy.rollUntilExtremesFound(d);
    It took 29 rolls until we saw both a 1 and a 6
    > d = new Die(10);
    > loopy.rollUntilExtremesFound(d);
    It took 9 rolls until we saw both a 1 and a 10
    
  6. The previous method verifies that the Die's largest and smallest possible values eventually get rolled, but what about all the rest of the values? Finish the definition of rollUntilAllValuesFound. It takes a Die instance and rolls it until we've seen it return all possible values from 1 to the Die's number of sides. One way to do this would be to use an ArrayList of integers to keep track of which values you've seen. On each new roll, check whether the rolled value is already in the list. If not, add it. Keep going until the list gets to the proper length. When you've found them all, print the ArrayList and the number of rolls it took. (Hint, there's a contains method in the ArrayList class that's worth reading up on. Also, don't forget that you can just print an entire ArrayList.) Recall that you can't store primitive types in an ArrayList, so you can't do ArrayList<int>, but you can make a list containing instances of the Integer wrapper class (ArrayList<Integer>).
    > Die d = new Die();
    > LoopPractice loopy = new LoopPractice();
    > loopy.rollUntilAllValuesFound(d);
    It took 17 rolls until all values were produced: [4, 6, 3, 5, 2, 1]
    > d = new Die(8);
    It took 22 rolls until all values were produced: [7, 6, 1, 5, 8, 3, 4, 2]
    
If time permits, consider trying one of the following exercises:


Brad Richards, 2024