Skip to content

Homework 2

This homework has two parts. Each will be implemented in a separate file as a separate class. Submit only the two .java files you complete. You will need to download the hw2_templates zip file which contains the two interfaces and implementing class files that you'll need.
Note that two of the files have the same names as those in HW1 so be careful to keep these apart (otherwise the older ones will get overwritten).


Can Haz Maths?

First, you will complete a class called HazMath (in the HazMath.java file) that implements the interface Mathematical (in the Mathematical.java file). These should have the following definitions:

  • public boolean isPrime(int n)

    You cannot change the signature for the method. This method is similar to the ones we've discussed in the lab and will return true or false depending on if the passed in integer values is prime or not, respectively. Return false if the invoker passes in a number less than 1. A prime number is one that is not evenly divisible by any other number. For example, if we have number 2, it should return True, and if we have number 55, it will return False. Some sample assertions (Note that for these assertions we've not added a. message, which is optional):

    assert isPrime(2);
    assert isPrime(53);
    assert !isPrime(55);
    assert !isPrime(24);
    assert !isPrime(-37337);
    

    Prime numbers form the basis of cryptography! Read into why this is a little bit. Really cool stuff.

  • public int sumOfSums(int n)
    You cannot change the signature for the method. This method takes an integer, n, and computes the sum of each of the sums of each integer between 1 and n. For example, the sum of the sums of 3 is (1) + (1 + 2) + (1 + 2 + 3) = 10. Some sample assertions:

    assert sumOfSums(1) == 1;
    assert sumOfSums(3) == 10;
    assert sumOfSums(6) == 56;
    assert sumOfSums(25) == 2925;
    assert sumOfSums(-5) == 0;
    

2048 Left Shift

Similar to in HW1, you'll implement a left shift of decimals in a string, with combining of multiple like digits into a single digit of double the value. The main difference between HW1 and this is that now the string representing a row in 2048 can be of any length! You'll implement a Combine class in a comparable .java file.

As before, the input string can only include the characters '_', '2', and '4'. Unlike before, it can be of any length (including length 0). Any '2's that have no other characters or any number of '_'s between them combine to become a '4'(i.e. two '2's disappear, and a '4' is left in one of their space). Likewise, '4's become '8's. Any digit that is the result of a combination cannot be combined with another digit. All digits are combined in this way and shifted to the far left so that any '_' appear on the right of the string. The length of the string remains unchanged.

Your classes will implement the Combinable interface. Note that we're attaching an updated version so be sure to use the new one. You will implement multiple methods from the interface Combinable:

  • public boolean validateChar(char ch)

    The requirements remain unchanged from HW1.

  • public boolean validateRow(String s)

    The string must include only the characters discussed above, but it can be of any length. Return true if it includes only valid characters, false otherwise. You must invoke validateChar to determine validity of each character.

  • public String repeat(char ch, int num)

    Create a string that consists of num, ch characters.

  • public String eliminateUnderscores(String s)

    Given a String s, return a new String that has all '_' characters removed. You cannot use the replace method in the String Class. Note that the returned string will be of the same or lesser length than the argument.

  • public String moveLeft(String s)

    All non-'_' characters in the input string are shifted to the left of the string, leaving only '_'s on the right-hand side of the string. The resulting "left-shifted" String is returned. It is highly suggested that you first use eliminateUnderscores to cluster all the non-'_' characters, and then to use repeat to concatenate the appropriate number of '_' on to the right of the String that is returned. Most other ways to implement this method are far more complicated.

    You must invoke the validateRow method to test if
    the input String is valid. If it is not, return the input string directly without change.

  • public String combineLeft(String s)

    Given the input string, the output should be a string of the same length, with all '_'s removed from the left side of the string, and with all pairs of like digits with only '_'s in between them, combined into a digit of twice the value. Return the resulting String.

    It is highly suggested that you use the moveLeft method to help you with this implementation. When generating your new string, it is much easier to be able to ignore all '_' between digits.

    You must invoke the validateRow method to test if the input String is valid. If it is not, return the input string directly without change.

Some example assertions:

assert validateChar('_');
assert !validateChar(' ');

assert validateRow("2222");
assert validateRow("_24_");
assert !validateRow("2234_");
assert validateRow("");
assert !validateRow("*");

assert "***".equals(repeat('*', 3));
assert "".equals(repeat('*', 0));
assert "44444444".equals(repeat('4', 8));

assert "4".equals(eliminateUnderscores("__4__"));
assert "244".equals(eliminateUnderscores("2_4_4_"));
assert "".equals(eliminateUnderscores("___"));
assert "242442".equals(eliminateUnderscores("242442"));

assert "4____".equals(moveLeft("__4__"));
assert "244___".equals(moveLeft("2_4_4_"));
assert "___".equals(moveLeft("___"));
assert "_3_".equals(moveLeft("_3_")); 
assert "242442".equals(moveLeft("242442"));

assert "484___".equals(combineLeft("224422"));
assert "484________".equals(combineLeft("2_2_4_4_2_2"));
assert "242424__".equals(combineLeft("242_42_4"));
assert "828____".equals(combineLeft("__44244"));
assert "".equals(combineLeft(""));
assert "22344".equals(combineLeft("22344"));

assert "88__________".equals(combineLeft(combineLeft("_2_22_222_4_")));

An Implementation
Strategy and General Advice:

Please reread the "course philosophies" on the main course's webpage. You always want to implement and test the smallest chunk of code you can. If you try and write the whole program, and then start debugging/testing, your homework will take a ton of time. I'm breaking up these homework assignments into separate methods explicitly so that you can implement the methods one by one, and test them before moving on to the next. If you implement a method and test it (so you have confidence it works), then you should think "this method provides me the functionality to do X" for whatever X the method is implemented to do. Now implementing the next methods that might use that method are easier to think about because if you need that functionality X, then you can just invoke the method! Easy-peasy. Once you have the repeat method done, when you need to generate a number of '_', you don't need to think about a loop, only that you need to invoke that method! This is abstraction.

You know variables, conditionals, loops, math, and methods. Those are the building blocks for every single program that is written! With practice, you'll be able to master these building blocks and create awesome programs. But if the core concepts for all programming can be learned in a single semester, what are you possibly going to learn in subsequent CS classes??? Put another way, what do you think defines a "good" software developer from one that is not?

Programs very quickly become too complicated for even a good programmer to understand. Our short-term memory holds 7 ± 2 items. Every conditional, variable, and loop takes up one of those! No wonder we can't keep the whole program in our heads!!! "Good" programmers, they are good at using abstraction to hide complexity. Methods are the main tool we have right now for abstraction. A method hides its own, possibly complex, functionality behind a method name, a return value, and a set of arguments. You just need to invoke the method to harness its power, and forget about all the details that would use up those 7 ± 2 slots: abstraction! The best programmers always seek out abstraction to make it easier to understand complicated programs.

It will be difficult for you to understand at this point when to use abstraction, or even how it can be helpful. As you become more familiar with coding, and as you get to the point when it all doesn't feel quite so foreign (with hard work, you'll get there!), you'll start to get a good understanding of how to use methods and abstractions. For now, hold in there, and try and heed this advice on how to write your programs methodically! It's only a matter of time and sweat till you're a master of abstraction and all things programming!

Lastly, Pro-tip: sketch the problem before you start coding. This relates to the course philosophies point called "Planning is the best debugging!". A diagram of the problem can help you visualize its structure and start to see the cases that you must take into account.


What to Submit

Please submit, via Blackboard, a zip file of a folder with a name following the format: <FirstnameLastname_Lab> (with Lab as Mon or Fri), with two files inside: the completed HazMath.java, and the completed Combine.java.

Notes and grading: Your implementations must compile to get any credit. Please develop these in small chunks. Don't program everything, and hope it will work. Write each method separately, and test it separately. Your implementation will get credit proportional to how many assertions they pass. Make sure to submit your .java file(s), and that it has the correct class name. Your main method(s) will not be invoked, so do not assume that it executes.

If you simply implement your methods in such a way that they are customized to the specific test cases (i.e. by returning the desired string, but not having any of the logic required by the assignment), you'll receive no credit.