CS 161 Assignment #7

Due Friday, March 29th by 11:59pm
(Not accepted after 3/31)

Introduction

The assignment this time around is to write a program that can investigate the difficulty of guessing numeric "passwords". Imagine that you're consulting for a company that makes digital safes with combinations consisting of sequences of integers. The difficulty of guessing a combination depends on how long the key sequence is (how many numbers it contains), and the range of values used for each number in the key (between zero and some upper limit).

Your job is to help estimate the "strength" of various combination configurations. Sure, you could just calculate the total number of possible combinations with a bit of math and use that to estimate the strength of a configuration, but it'll be more fun to write a program to test it! Your program will generate random combinations, and count how many attempts it take before it correctly guesses a specific combination. This will also give us a chance to practice some more with projects consisting of multiple classes, and with topics we've seen so far like loops and equals methods.

The Combination class

Your program will consist of two parts: A Combination class that represents the sequence of integers that make up a combination (via an array), and a ComboGuesser class that repeatedly creates Combination instances and compares them to the "right answer" until it guesses the combination: I've created a new project to get you started, but it only contains the beginnings of the Combination class. You'll need to create a new class for ComboGuesser when the time comes, but I'm recommending that you start by getting the Combination class working before writing the ComboGuesser class. Your Combination class should contain the following methods:
  1. A constructor that takes two integer arguments: The length of the combination instance to be constructed, and the maximum value for each of the numbers in the combination. It should initialize the Combination instance to hold random values within the appropriate range for each of the values in the sequence. (As shown in the interactions below, the random values should include 0 and the specified maximum in their range.)
  2. A getMax method that returns the largest possible value that could be found in the combination. (Note, this isn't the largest value that is in the combination — it's the maximum value passed to the constructor.)
  3. A getLength method that returns the length of the combination.
  4. A method called toString that builds and returns a string containing the values in the combination. For full credit, the values should be separated by commas, with square brackets around the entire thing, and you should write the code for creating the string yourself. (If you poke around enough you could find a built-in method that formats it like that for you, but it'll be good practice to write it yourself.) There should not be a comma after the last value. For example, if the combination contains the values 1, 2, and 3, the output from your method should be "[1, 2, 3]". The sample interactions below illustrate the creation of several different Combination objects, and output from toString for each:
  5. > Combination c = new Combination(4, 2);
    > c.getMax()
      2   (int)
    > c.getLength()
      4   (int)
    > c.toString()
      "[2, 1, 2, 0]"  (String)
    > c = new Combination(4, 2);
    > c.toString()
      "[1, 1, 0, 2]"  (String)
    > c = new Combination(6, 10);
    > c.toString()
      "[1, 6, 10, 8, 7, 0]"  (String)
    > c = new Combination(1, 2);
    > c.toString()
      "[2]"  (String)
    

  6. A setValue method that takes two integers: The (zero-based) index of the value within the sequence to be changed, and the new value to be stored there. The value should only be changed if the index is valid, and the specified value is within the allowed range.

    > Combination c = new Combination(4, 2);
    > c.toString()
      "[1, 0, 0, 2]"  (String)
    > c.setValue(0,2);
    > c.toString()
      "[2, 0, 0, 2]"  (String)
    > c.setValue(3,1);
    > c.toString()
      "[2, 0, 0, 1]"  (String)
    > c.setValue(3,3);
    > c.toString()
      "[2, 0, 0, 1]"  (String)
    > c.setValue(4,0);
    > c.toString()
      "[2, 0, 0, 1]"  (String)
    

  7. A method called equals that takes a Combination instance as its argument, and returns a boolean value: If the Combination instance passed in has the same length and contents as the instance on which the method is invoked, the method should return true. It should return false if the length or contents differ. For full credit, your method should use a loop to compare the contents of the two arrays within the two instances, assuming they have the same length.

    > Combination c1 = new Combination(4, 2);
    > Combination c2 = new Combination(4, 2);
    > c1.toString()
    "[0, 0, 0, 1]" (String)
    > c2.toString()
    "[2, 0, 2, 1]" (String)
    > c1.equals(c2)
    false  (boolean)
    > c1.equals(c1)
    true  (boolean)
    > c2.setValue(0,0);
    > c2.setValue(2,0);
    > c2.toString()
    "[0, 0, 0, 1]" (String)
    > c1.equals(c2)
    true  (boolean)
    

The ComboGuesser class

After testing your Combination class thoroughly, create a new ComboGuesser class. The ComboGuesser class will create and remember a specific Combination (we'll call it the "key"), then repeatedly create random Combination instances until it finds one that matches. (Creating random Combination instances should be easy now, since there's a constructor in the Combination class that does just that. If you find yourself calling nextInt in any of the methods below, you're on the wrong track!) Your ComboGuesser class should contain the following methods:
  1. A constructor that takes two int inputs: The length of the combination sequence to be tested, and the maximum value in each position. It generates a random Combination instance as the key.
  2. A constructor that takes one input: A specific Combination instance to use as the key. This constructor would be called if the user already had a Combination instance that they wanted you to guess, so you don't need to create a random one yourself when this constructor is called.
  3. A method called guessCombo that generates random Combination instances until it finds one that matches the key. (It doesn't need any inputs.) It should then print information about the key and the number of guesses required, as shown below.

The sample outputs below show ComboGuesser instances searching for matches. The first ComboGuesser, g1, creates a random key of length 5. The call to guessCombo() ends up generating roughly 8 million random Combination instances before it finds one that matches the random key. (The output from guessCombo would appear in the terminal window, not the codepad.) After that, the interactions show a Combination, key, being created and used as the key for a second ComboGuesser object to guess. (Using setValue to set values within the key to the maximimum and minimum possible values helps ensure thorough testing of guessCombo.)

> ComboGuesser g1 = new ComboGuesser(5, 20);
> g1.guessCombo();
It took 8160758 guesses to guess
[13, 10, 4, 3, 11]
> Combination key = new Combination(5, 10);
> key.toString()
"[5, 3, 9, 4, 4]"  (String)
> key.setValue(0, 10);
> key.setValue(4, 0);
> key.toString()
"[10, 3, 9, 4, 0]"  (String)
> ComboGuesser g2 = new ComboGuesser(key);
> g2.guessCombo();
It took 116561 guesses to guess
[10, 3, 9, 4, 0]

Extending the Assignment

Style Guide

Before you submit your assignment, go through the checklist below and make sure your code conforms to the style guide.

Grading

This assignment will be graded out of a total of 100 points:

Submitting

Before submitting, make sure you've added your name to the comment at the top of the class file, and written comments for each method. Test your methods thoroughly. When you're convinced that it's ready to go, submit the assignment using BlueJ's submission tool as described here.


Brad Richards, 2024