CS 112
Spring 2018

Old version

This is the CS 112 site as it appeared on May 11, 2018.

Problem Set 2

due by 11:59 p.m. on Tuesday, February 6, 2018

Preliminaries

In your work on this assignment, make sure to abide by the collaboration policies of the course.

If you have questions while working on this assignment, please come to office hours, post them on Piazza, or email cs112-staff@cs.bu.edu.

Make sure to submit your work on Apollo, following the procedures found at the end of the assignment.


Part I

40 points total

This part of the assignment consists of a series of short-answer questions. You will be able to check your answers to at least some of these questions in DrJava, but we encourage you to try answering them on your own and convincing yourself of the correctness of your answers before you attempt to test them in DrJava. These questions are similar to ones that could appear on the midterms and final exam, which you will have to take without the use of a computer!

Problem 1: Understanding code that uses an array

8 points total; individual-only

Using folders

Don’t forget that we strongly encourage you to create a separate folder for each assignment, so that you can more easily keep track of your work. For example, you could create a folder called ps2 for your work on this assignment, and put all of the files for PS 2 in that folder.

Open up a text editor (e.g., Notepad or TextEdit) and create a plain-text file named ps2pr1.txt. Put all of your work for this problem in that file.

  1. (3 points) What is the output of the following lines of code?

    int[] a = {3, 6, 9, 12, 15};
    int[] b = {3, 6, 9, 12, 15};
    int[] c = b;
    c[3] = 10;
    System.out.println(a[3] + " " + b[3] + " " + c[3]);
    

    After giving the output, explain briefly why it makes sense.

  2. (3 points) Consider the following static method, which operates on an array of integers:

    public static void mystery(int[] arr) {
        for (int i = 0; i < arr.length / 2; i++) {
            int n = arr.length - 1 - i;
            int val = arr[n];
            arr[n] = arr[i];
            arr[i] = val;
            // What values do the variables have here,
            // for each value of the loop variable `i`?
        }
    }
    

    Consider the following code fragment, which calls this method:

    int[] values = {0, 1, 2, 3, 4, 5, 6, 7};
    mystery(values);
    

    Copy the table shown below into your text file for this problem, and complete it to illustrate the execution of the above method call.

    mystery's variables
      i  |  n  | val | arr 
    -------------------------------------------
      -  |  -  |  -  | {0, 1, 2, 3, 4, 5, 6, 7}
      0  |     |     |
    

    We have started the table for you. The first row shows what the array looks like before the start of the for loop. For each value of the loop variable i, include a row in the table that shows the values of the other variables at the end of the iteration of the loop for that value of i. Note that you should write the value of arr as an array literal that shows the current contents of the array, even though the variable itself actually holds a reference to the array.

  3. (2 points) After running the code fragment from part 2, we then execute the following statement:

    System.out.println(Arrays.toString(values));
    

    Do we see the original array, or do we see the changes made by the call to the mystery() method? Explain briefly why your answer makes sense.

    (Note: If you want to check your answer, you’ll need to import thejava.util package so that you can use the Arrays.toString() method.)

Problem 2: Two-dimensional arrays

8 points total; pair-optional

This is one of only two problems in this assignment that you may complete with a partner. See the rules for working with a partner on pair-optional problems for details about how this type of collaboration must be structured.

Open up a text editor (e.g., Notepad or TextEdit) and create a plain-text file named ps2pr2.txt. Put all of your work for this problem in that file.

Overview
Two-dimensional arrays in Java are extremely similar to two-dimensional lists in Python.

In Python, a 2-D list is a list of lists:

grid = [ [2, 4],
         [5, 7],
         [8, 1] ]

In the same way, a 2-D array in Java is an array of arrays:

int[][] grid = { {2, 4},
                 {5, 7},
                 {8, 1} };

(Note: When we declare a variable for a 2-D array, the type of the elements is followed by two pairs of square brackets: int[][])

Here is a comparison of other key operations for 2-D sequences:

operation

in Python

in Java

determining the number of rows

len(grid)

grid.length

determining the number of elements in row r

len(grid[r])

grid[r].length

determining the number of columns in a rectangular grid/matrix (in which all rows have the same length)

len(grid[0])

grid[0].length

accessing the element at row r, column c

grid[r][c]

grid[r][c]

Problems
Assume that you have a variable twoD that refers to a two-dimensional array of integers. Here is another example of such an array:

int[][] twoD = { {1, 2, 3},
                 {4, 5, 6},
                 {7, 8, 9} };

In your answers below, you may assume that the array is rectangular – i.e., that all rows have the same number of elements.

  1. (2 points) Write an assignment statement that replaces the value of 6 in the above array with a value of 12.

  2. (3 points) Write a code fragment that prints the values in the leftmost column of the array to which twoD refers. Print each value on its own line. For the array above, the following values would be printed:

    1
    4
    7
    

    Make your code general enough to work on any two-dimensional array named twoD that has at least one value in each row.

  3. (3 points) Write a code fragment that prints the values in the anti-diagonal of the array to which twoD refers – i.e., the values along a diagonal line from the bottom-left corner to the upper-right corner. Print each value on its own line. For the array above, the following values would be printed:

    7
    5
    3
    

    Make your code general enough to work on any two-dimensional array in which the dimensions are equal – i.e., the number of rows is the same as the number of columns.

Problem 3: A class that needs your help

12 points total; individual-only

Open up a text editor (e.g., Notepad or TextEdit) and create a plain-text file named ps2pr3.txt. Put all of your work for this problem in that file.

Consider the following class, which is intended to serve as a blueprint for objects that encapsulate two pieces of data: a person’s name (which should not be allowed to have the special value null) and the person’s age (which must be non-negative).

public class Participant {
    String name;
    int age;
}
  1. (8 points) This class does not employ appropriate encapsulation. Rewrite it to prevent direct access to the internals of a Participant object while allowing indirect access through appropriate methods. Your rewritten version should include:

    • whatever changes are needed to the field declarations to prevent them from being accessed outside the class
    • methods that can be used to obtain the value of each field (call them getName and getAge)
    • methods that can be used to change the value of each field (call them setName and setAge). These methods should ensure that age is always non-negative, and that name is not null. Attempts to assign an invalid value should produce an IllegalArgumentException, which you can do as follows:

      throw new IllegalArgumentException();
      
    • a constructor that initializes the fields of a newly created Participant object to the values specified by the parameters of the constructor. Attempts to assign an invalid value should produce an IllegalArgumentException. Take advantage of the error-checking code that is already present in the methods that you defined for changing the values of the fields.

    No other methods are required.

  2. (4 points) Now imagine that you’re writing client code for your revised Participant class – i.e., code in a different class that uses Participant objects.

    Write a single line of client code to accomplish each of the following tasks:

    1. Construct a Participant object for a person named Tom Brady who is 35 years old, and assign it to a properly declared variable named qb.

    2. The Tom Brady (the one who is participating in his 8th Super Bowl this week) is actually 40 years old! Change the age in the Participant object that you created in part (a). You should not create a new object; you should change the internals of the existing object.

      Important: As a result of your changes from part 1, clients no longer have direct access to the fields in a Participant object. As a result, you will need to make an appropriate method call to accomplish the change to the age.

    3. Get the name from the Participant object that you created in part (a), and assign it to a properly declared variable named mvp. Here again, you will need to make an appropriate method call.

Problem 4: Static vs. non-static methods

12 points total; 6 points each part; individual-only

Open up a text editor (e.g., Notepad or TextEdit) and create a plain-text file named ps2pr4.txt. Put all of your work for this problem in that file.

When designing a blueprint class, we can include both non-static and static methods. A non-static method is required if the method must have access to the fields of a particular called object. However, if a method does not need a called object – i.e., if all of the information that it needs is supplied by its parameters – then we typically make it static. Non-static methods must be called on an object of the class. Static methods may be called on an object of the class, but it is better style to call them using the name of the class instead.

Consider the following simple class, which is a blueprint class for Grade objects that encapsulate both the number of points earned (a floating-point number) and a late penalty, if any. The late-penalty percentage is stored as an integer (e.g., 10 for a 10% late penalty).

public class Grade {
    private double points;
    private int latePenalty;

    public Grade(double pts, int penalty) {
        this.points = pts;
        this.latePenalty = penalty;
    }

    // other methods are omitted for the sake of brevity
}

Assume that you are considering adding two methods to this class.

  1. The first method is called addExtraCredit. It takes a floating-point number called amount and increases the points component of a Grade object by the specified amount. It does not return anything.

    1. What type of method should addExtraCredit be — static or non-static? Explain briefly.
    2. Give an appropriate header for this method. You do not need to define the body of the method.
    3. Give an example of how you would call this method from outside the Grade class. If you need a Grade object to call the method, assume that the variable g represents that object, and that the object has already been created.
  2. The second method is called computePercent. It takes two floating-point numbers – one called pointsEarned and another called possiblePoints – and it returns pointsEarned as a percentage of possiblePoints. For example, if pointsEarned is 30.0 and possiblePoints is 50.0, the method should return 60.0, because 30.0 is 60 percent of 50.0.

    1. What type of method should computePercent be — static or non-static? Explain briefly.
    2. Give an appropriate header for this method. You do not need to define the body of the method.
    3. Give an example of how you would call this method from outside the Grade class. If you need a Grade object to call the method, assume that the variable g represents that object.

Part II

60 points total

Problem 5: Array-processing methods

24 points total; 8 points each part; individual-only

In a class named ArrayMethods, implement each of the methods described below.

Important guidelines:

  • Your methods must have the exact headers that we have specified, or we won’t be able to test them. Note in particular that the case of the letters matters.

  • If you are asked to write a method that returns a value, make sure that it returns the specified value, rather than printing it.

  • You should not use any Java classes that we have not covered in the lecture notes.

  • Each of your methods should be preceded by a comment that explains what your methods does and what its inputs are. You should also include a comment at the top of the file, similar to the one that we included in Methods5.java from Problem Set 1.

  • More generally, use good programming style. Use appropriate indentation, select descriptive variable names, insert blank lines between logical parts of your program, and add comments as necessary to explain what your code does. See the coding conventions for more detail.

  1. Write a method with the header

    public static void swapAdjacent(int[] values)
    

    that takes a reference to an array of integers values and swaps adjacent pairs of elements: values[0] with values[1], values[2] with values[3], etc.

    For example, after implementing this method and compiling your ArrayMethods class, you should see the following when you test the method from the Interactions Pane in DrJava:

    > int[] a1 = {0, 2, 4, 6, 8, 10};
    > ArrayMethods.swapAdjacent(a1);
    > a1
    { 2, 0, 6, 4, 10, 8 }
    

    (Note that if you evaluate an array variable in the Interactions Pane, DrJava is nice enough to show you the contents of the array, even though printing that same variable would show something based on the array’s memory address.)

    In an odd-length array, the last element should not be moved:

    > int[] a2 = {1, 2, 3, 4, 5, 6, 7};
    > ArrayMethods.swapAdjacent(a2);
    > a2
    { 2, 1, 4, 3, 6, 5, 7 }
    

    Special case: If the parameter is null, the method should throw an IllegalArgumentException.

  2. Write a method with the header

    public static int[] copyReplace(int[] values, int oldVal, int newVal)
    

    that takes a reference to an array of integers values and two integers oldVal and newVal, and that creates and returns a new array that is a copy of the original array, but in which all occurrences of the value oldVal are replaced with the value newVal.

    Important:

    • You may not use any of the built-in methods for copying an array.

    • Your method must not modify the original array.

    Examples:

    > int[] a1 = {2, 5, 4, 2, 7, 4, 2};
    > int[] a2 = ArrayMethods.copyReplace(a1, 4, 0);
    > a2
    { 2, 5, 0, 2, 7, 0, 2 }  
    > int[] a3 = ArrayMethods.copyReplace(a1, 2, -1);
    > a3
    { -1, 5, 4, -1, 7, 4, -1 }  
    > a1
    { 2, 5, 4, 2, 7, 4, 2 }
    

    Special case: If the parameter is null, the method should throw an IllegalArgumentException.

  3. Write a method with the header

    public static int maxSorted(int[] values)
    

    that takes a reference to an array of integers and returns the length of the longest sorted sequence of integers in the array. For example:

    > int[] a1 = {3, 8, 6, 14, -3, 0, 14, 207, 98, 12};
    > ArrayMethods.maxSorted(a1)
    4
    

    The above call should return 4, because the longest sorted sequence in the array has four values in it (the sequence -3, 0, 14, 207). Note that the sorted sequence could contain duplicates. For example:

    > int[] a2 = {17, 42, 3, 5, 5, 5, 8, 4, 6, 1, 19};
    > ArrayMethods.maxSorted(a2)
    5
    

    This call should return 5 for the length of the sequence 3, 5, 5, 5, 8.

    Special cases:

    • If the parameter is null, the method should throw an IllegalArgumentException.

    • Your method should return 0 if passed an empty array (i.e., an array of length 0).

    • Your method should return 1 if passed an array with one element or an array in which all of the elements are in decreasing order. It should be possible to get a return value of 1 for these cases without needing to explicitly check for them.

Problem 6: Our Rectangle class revisited

12 points total; 4 points each part; pair-optional

This is one of only two problems in this assignment that you may complete with a partner. See the rules for working with a partner on pair-optional problems for details about how this type of collaboration must be structured.

This problem asks you to modify an existing blueprint class.

The guidelines from the previous problem also apply here.

Begin by downloading this file: Rectangle.java, which contains a version of the Rectangle class that we discussed in lecture. Open it in DrJava, and add your methods to the class that we have given you in that file.

  1. Add a non-static method named rotate that rotates a Rectangle object 90 degrees by swapping its width and height values. For example, if a Rectangle‘s dimensions are 10x30, then calling the rotate method would change its dimensions to 30x10.

    Because the method should change the internals of the called object, it doesn’t need to – and should not! – return a value.

  2. Add a non-static method named largerThan that takes another Rectangle object and determines if the called Rectangle object (i.e., the one on which the method is invoked) has a larger area than the other Rectangle object – returning true if the called object has a larger area and false otherwise.

    Special case: If a null value is passed into the method, it should throw an IllegalArgumentException.

    Important:

    • For full credit, you should take full advantage of the existing Rectangle methods to get the information that you need in this method. Do not reinvent the wheel!

    • Make sure that you return a boolean literal (true or false) and not the string "true" or the string "false".

  3. Add code to the bottom of the existing main() method that makes at least two test calls to each of your new methods.

    • If the method returns a value, you should assign that value to an appropriately declared variable, and then print the value of that variable.

    • If the method does not return a value, print the Rectangle object both before and after each call to that method.

Problem 7: A blueprint class for Card objects

24 points; individual-only

In this problem you will create a class called Card that serves as a blueprint for objects that can be used to represent a single playing card for a card game called Buno (pronounced B-U-no). You can think of it as BU’s version of a popular card game whose name does not begin with B!

The sections below outline the steps that you should take.

The guidelines from Problem 5 also apply here.

  1. (0 points) Download Card.java, and open it in DrJava. This file includes some starter code for the class you will write. Read through this code before continuing — including all of the comments that we have provided.

    You will note that we have provided two class constants – global variables whose value cannot change. Here is the first one:

    public static final int MIN_VALUE = 0;
    

    In general, a class constant is defined near the top of the class, outside of any method. Its definition includes several key components:

    • the keyword static, which means that the variable belongs to the class as a whole. This distinguishes it from a field, which is a non-static variable that is inside every object of the class. Every object gets its own separate set of the fields, but there is only one copy of a static variable, and it is is shared by all objects of the class.

    • the keyword final, which is what makes this variable a constant. The value that we assign to the class constant here is its final value, and we cannot assign a new value to it somewhere else.

    • the type declaration, like that of any other variable

    • the variable name, which we capitalize so that it will be obvious that it is a constant

    • the initialization of the variable, which we must do here, because we can’t change a class constant outside of its definition.

    We have provided two class constants. Read the accompanying comments to see what they represent.

  2. (1 point) Add a third class constant for the maximum value that a Card can have, which is 9.

  3. (5 points) Add a static method called isValidColor. It should take the name of a color as a parameter, and it should return true if the specified color is valid (i.e., if it is one of the colors listed in the COLORS array), and false otherwise. For example:

    • isValidColor("red") should return true, because “red” appears in the COLORS array
    • isValidColor("orange") should return false, because “orange” does not appear in the COLORS array.

    Notes:

    • The case of the letters matters. For example, isValidColor("Red") should return false, because "Red" begins with an upper-case R, and the version in the COLORS array begins with a lower-case r.
    • You may not use any built-in methods to determine whether the color is in the COLORS array. Rather, you should write the code needed to process this array and see if the color is present.
    • In addition, you should not make any assumptions about the actual values in the COLORS array. If we change the set of colors, your method should still work.
    • This method is a helper method that will be used by one or more of the other methods that you write.
    • This method (unlike the others you will write) is static, because it does not need to access the fields in the object. Rather, we pass it all of the information that it needs as a parameter.
  4. Define the fields (3 points)
    Each Card object should encapsulate two pieces of state:

    • the card’s color (a string)
    • the card’s value (an integer).

    For example, here is what a Card object representing a red 7 would look like in memory:

    (To simplify matters, we have shown the string “red” inside the field, even though in reality the memory address of the string would be stored.)

    For now, you only need to define the fields, making sure to protect them from direct access by client code. In subsequent sections, you will write constructors that assign values to the fields, and that ensure that only valid values are allowed.

  5. Implement mutators for the fields (4 points)
    Next, add the following mutator methods:

    • setColor, which takes a String representing a color and sets the value of the Card object’s color field to the specified color. Attempts to assign an invalid color should produce an IllegalArgumentException. Take advantage of your isValidColor method when checking for invalid colors.

    • setValue, which takes an integer and sets the value of the Card object’s value field to the specified number. Attempts to assign an invalid value (one that is outside the range specified by the MIN_VALUE and MAX_VALUE constants) should produce an IllegalArgumentException.

    Make sure that your methods are non-static, because they need access to the fields in the called object. Use the class constants when checking for invalid values.

  6. Implement a constructor (3 points)
    Next, add a constructor that takes a string for the Card‘s color and an integer for the Card‘s value (in that order), and initializes the values of the fields. Your constructor should call the mutators that you defined in part 3 so that you can take advantage of the error-checking code that is already present in those methods.

  7. Implement accessors for the fields (2 points)
    Next, define the following accessor methods:

    • getColor, which returns the string representing the Card object’s color.
    • getValue, which returns the integer representing the Card object’s value.

    Make sure that your methods are non-static, because they need access to the fields in the called object.

    Once you have completed parts 1–6, you can test them using the first client program that we’ve given you. See below for more detail.

  8. Define the toString method (2 points)
    Write a toString method that returns a String representation of the Card object that can be used when printing it or concatenating it to a String. We discuss this type of method in the lecture notes, and we provide an example in our Rectangle class. The returned String should be of the form color value (e.g., "blue 3" or "red 10").

  9. Define methods for comparing Card objects (4 points)
    Finally, define the following two instance methods for comparing Card objects:

    • matches, which takes another Card object as a parameter and returns true if the called Card object matches the color and/or value of the other Card object, and false if it doesn’t match either the color or the value. If a value of null is passed in for the parameter, the method should return false.

    • equals, which takes another Card object as a parameter and determines if it is equivalent to the called object, returning true if it is equivalent and false if it is not equivalent. Two Card objects should be considered equivalent if their color and value are the same. If a value of null is passed in for the parameter, the method should return false.

Client programs and testing your code
To help you in testing your Card class, we have created two sample client programs:

Put the client programs in the same folder as your Card.java file. Make sure not to open the clients until the necessary parts have been completed.

In addition to using the client programs, we recommend that you perform additional testing on your own. You can do so in the Interactions Pane, or by adding code to one of the clients, or by adding a main method to the Card class.

If you are unable to get a given method to compile, make sure to comment out the body of the method (keeping the method header and possibly a dummy return value) so that we’ll still be able to test your other methods.


Submitting Your Work

You should use Apollo to submit the following files:

Warnings

  • Make sure to use these exact file names, or Apollo will not accept your files. If Apollo reports that a file does not have the correct name, you should rename the file using the name listed in the assignment or on the Apollo upload page.

  • Make sure that you do not try to submit a .class file or a file with a ~ character at the end of its name.

  • Before submitting your files, make sure that your BU username is visible at the top of the Apollo page. If you don’t see your username, click the Log out button and login again.

  • If you make any last-minute changes to one of your Java files (e.g., adding additional comments), you should compile and run the file in DrJava after you make the changes to ensure that it still runs correctly. Even seemingly minor changes can cause your code to become unrunnable.

  • If you encounter problems with Apollo, click the Log Out button (if you are already logged in), close your browser, and try again. If possible, you may also want to wait an hour or two before retrying. If you are unable to submit and it is close to the deadline, email your homework before the deadline to cs112-staff@cs.bu.edu

Here are the steps:

  1. Login to Apollo, using the link in the left-hand navigation bar. You will need to use your Kerberos user name and password.
  2. Check to see that your BU username is at the top of the Apollo page. If it isn’t, click the Log out button and login again.
  3. Find the appropriate problem set section on the main page and click Upload files.
  4. For each file that you want to submit, find the matching upload section for the file. Make sure that you use the right section for each file. You may upload any number of files at a time.
  5. Click the Upload button at the bottom of the page.
  6. Review the upload results. If Apollo reports any issues, return to the upload page by clicking the link at the top of the results page, and try the upload again, per Apollo’s advice.
  7. Once all of your files have been successfully uploaded, return to the upload page by clicking the link at the top of the results page. The upload page will show you when you uploaded each file, and it will give you a way to view or download the uploaded file. Click on the link for each file so that you can ensure that you submitted the correct file.

Warning

Apollo will automatically close submissions for a given file when its final deadline has passed. We will not accept any file after Apollo has disabled its upload form, so please check your submission carefully following the instructions in Step 7 above.