Computer Science 15-100 (Sections S-V), Fall 2008
Homework 11
Due:  Thu 20-Nov-2008 at 11:59pm (email copy) and at Friday's class/recitation (identical physical copy)
(no late submissions accepted).


Read these instructions first!


  1. ch6Exercises
  2. ch7Exercises
  3. SCALES Practice
    1. SCALES and the JCF
      1. ArrayShuffle
    2. Mystery Methods
  4. Interfaces Practice
    1. Comparator
    2. Iterable and Iterator
  5. Bonus/Optional:  Sokoban
  6. Bonus/Optional: MovieCoStars

  1. ch6Exercises
    Read and study Chapter 6, sections 6.1 through 6.9 (so you are not responsible for sections 6.10 through 6.13).  Then, do the following Exercises from Chapter 6 (pp 368 - 369):
    Exercises 6.10,  6.11,  6.12
     
  2. ch7Exercises
    Read and study Chapter 7, sections 7.1 through 7.8 (so you are not responsible for sections 7.9 through 7.10).  Then, do the following Exercises from Chapter 7 (pp 432 - 433):
    Exercises 7.1,  7.3,  7.7,  7.10 
     
  3. SCALES Practice
     
    1. SCALES and the JCF
       
      1. ArrayShuffle
        Note:  Sun provides the Java library source code for free.  This is a tremendous asset both for students and even for professional developers.  An important point of this question is to encourage you to use this resource in the future by:  (a) getting it copied to your hard drive; and (b) showing you how you might use it as a reference.

        Also note:  A second point of this question is to help you deal with the situation where you, as intermediate programmers, must look at code that is a bit more advanced than what you have so far learned.  The point is not for you to understand every detail of such code, but to be able to glean the "gist" of the code without understanding every last detail.

        With these points in mind, first download the JDK source code (this is big, it may take a while!) from Sun's web site as follows:
        a)  Go to http://java.sun.com/javase/downloads/index.jsp
        b)  Scroll to "Java SE 6 JDK Source Code"  (For these purposes, it is ok to download this version of the source code even if you are using an older version of the JDK.)
        c)  Select ">> Download" to the right.
        d)  Select "http://www.java.net/download/jdk6/6u3/promoted/b05/jdk-6u3-fcs-src-b05-jrl-24_sep_2007.jar"
        e)  Download the 111+MB jar file
        f)   Double-click on the jar once downloaded (it will lead you through the rest of the install).
        g)  To find the source tree, go to the install directory, then to here:  j2se\src\share\classes\

        Next, we want to find the code for the Collections.shuffle method.  To do that, we note that the Collections class is in the java.util package (we import it with "import java.util.Collections).  Packages correspond to directories, so in your source tree that you downloaded, go to the "java" directory and then to the "util" directory.  In that directory, you should find the Collections.java file.

        Open the Collections.java file and scroll to the shuffle method (there is more than one -- find the one with the most code in it)  This is the actual Java code that gets called when you shuffle an ArrayList (or any other Collection).  As noted, some of the code uses techniques a bit beyond 15-100, and so may be confusing to you, but you still should be able to get the gist of it both by reading the code and reading the accompanying comments.

        Finally, on to the actual question:  In a file named Hw11ArrayShufflejava, first simply paste the unmodified code for the shuffle method from Sun's implementation (which you just downloaded) into a comment in your header.  This is to confirm that you downloaded and viewed the code, and to provide you with a guideline for implementing your own shuffle method over an array of int's.

        Next, also in the file Hw11ArrayShuffle.java, write the following method:
           public static void shuffle(int[] a)
        This method should shuffle the given array of int's using the same basic approach that Collections.shuffle uses.  Your code must not call Collections.shuffle, but rather directly implement the algorithm.

        Also, provide a suitable test method (which itself may take some thinking -- how would you properly test this?).  Be sure to document how your test method works (since there may be a little bit of math behind your logic)!  To help you, here are some incorrect shuffle "solutions" that your test method should detect as not shuffling the array:
          public static void bogusShuffle1(int[] a) {
            // reversing isn't shuffling!
            Collections.reverse(intArrayAsList(a));
          }
        
          public static void bogusShuffle2(int[] a) {
            // rotating isn't shuffling!
            Collections.rotate(intArrayAsList(a), a.length/2);
           }
        
          public static void bogusShuffle3(int[] a) {
            // sorting isn't shuffling!
            Collections.sort(intArrayAsList(a));
          }
        
          public static void bogusShuffle4(int[] a) {
            // This one DOES shuffle, but it also CORRUPTS the array!
            a[0] = a[1]; // this corrupts the array!
            Collections.shuffle(intArrayAsList(a));
          }

        Note that these use the intArrayAsList helper method, that you will have to also include in your code if you wish to use these methods:

          public static List<Integer> intArrayAsList(final int[] a) {
            return new AbstractList<Integer>() {
              public Integer get(int i) { return a[i]; }
              public Integer set(int i, Integer val) { int old=a[i]; a[i]=val; return old;}
              public int size() { return a.length; }
            };
          }


        In effect, you should have a test method for your test method (really!), and you should verify that your test method correctly detects each bogus shuffler.  Then, it should also note that the following method actually works (although it would not be allowed as a solution to this problem):

          public static void workingShuffle(int[] a) {
            // This really does shuffle (of course), although you could not
            // use this as a solution to this problem (but it's useful for
            // testing your test method!)
            Collections.shuffle(intArrayAsList(a));
          }

        Finally, your test method should not depend on human observation (so it should not, for example, print out an array and ask the user to "check if this looks shuffled").  Instead, you may need to call a shuffle method many times and look for some patterns that should occur if it works (for example, how often should a[0] remain at a[0] after a shuffle?  Think about this!).  You have to handle the fact that there is some variation, of course, but still, you should be able to write a test method that, without human assistance, determines that workingShuffle works and that the 4 bogusShuffles do not work.
         

    2. Mystery Methods
      In the written portion of your submission, answer the following questions in general, and in just a few words of plain English.
       
      1. In general, what does the following method do?
          public static ArrayList<String> f(String[] a) {
            ArrayList<String> result = new ArrayList<String>();
            HashSet<String> set = new HashSet<String>();
            for (String s : a) {
              if (set.contains(s))
                result.add(s);
              set.add(s);
            }
            Collections.sort(result);
            return result;
          }
      2. In general, what does the following method do?
          public static ArrayList<String> g(String[] a) {
            HashSet<String> set = new HashSet<String>(Arrays.asList(a));
            ArrayList<String> result = new ArrayList<String>(set);
            Collections.sort(result);
            return result;
          }
      3. In general, what does the following method do?
          public static ArrayList<String> h(String[] a) {
            ArrayList<String> list = new ArrayList<String>(Arrays.asList(a));
            ArrayList<String> result = new ArrayList<String>();
            String last = null;
            Collections.sort(list);
            for (int i=0; i<list.size()-1; i++)
              if ((list.get(i).compareTo(list.get(i+1)) == 0) &&
                  !list.get(i).equals(last))
                result.add(last = list.get(i));
            return result;
          }
  4. Interfaces Practice
     
    1. Comparator
      In a file Hw11Comparator.java, write the following method:
        public static void sortByLength(String[] a)
      This method takes an array of non-null Strings and sorts that array according to the length of the Strings (shortest first, longest last), or, in the event of a tie (same-length strings), then sort alphabetically.  To do this, the method must use Arrays.sort and give it an instance of a Comparator that you write that provides this "unnatural" ordering of Strings.  You may ignore the case of null Strings (and throw a NullPointerException, which is a reasonable response in any case.  Be sure to also provide a suitable test method.
       
    2. Iterable and Iterator
      First, study the following sample code, which implements a very simple QuadraticEquation class and tests it:
      import java.util.*;
      class MyCode {
        public static void main(String[] args) {
          // Create the quadratic equation f(x) = 3x^2 + 2x + 1
          QuadraticEquation f = new QuadraticEquation(3, 2, 1);
      
          // Verify that f(2) = 3*2*2 + 2*2 + 1 = 12 + 4 + 1 = 17
          assert(f.eval(2) == 17);
        }
      }
      
      // This class models a quadratic equation of the form y = ax^2 + bx + c,
      // where each of a, b, c, x, and y are all ints.
      class QuadraticEquation {
        private int a, b, c;
        public QuadraticEquation(int a, int b, int c) {
          this.a = a;
          this.b = b;
          this.c = c;
        }
      
        public int eval(int x) {
          return a*x*x + b*x + c;
        }
      }

      Here is a modified version of the main method that uses an enhanced for loop to print out the coefficients a, b, and c of the equation:

        public static void main(String[] args) {
          // Create the quadratic equation f(x) = 3x^2 + 2x + 1
          QuadraticEquation f = new QuadraticEquation(3, 2, 1);

          // Show that f(2) = 3*2*2 + 2*2 + 1 = 12 + 4 + 1 = 17
          assert(f.eval(2) == 17);

          // Print out the coefficients of this equation
          System.out.println("The following should print the coefficients of");
          System.out.println("y = 3x^2 + 2x + 1, one per line.  That is, it should");
          System.out.println("print 3 on the first line, 2 on the second, and 1 on the third.");

          for (int coeff : f)
            System.out.println(coeff);
        }


      In a file Hw11IterableAndIterator.java, first copy the modified version of the code from above, and then change the implementation of QuadraticEquation so that the enhanced for loop in the sample code works.

      Note:  the solution to this question is very similar to the Iterable + Iterator example from the class notes.  Use those notes as your guide here!  One important difference, however, is that here you may not place the coefficients a, b, and c into an array, an ArrayList, or any other Collection, even if that is a reasonable approach to this problem.

      Specifically, you will have to change the QuadraticEquation class so that it implements the Iterable<Integer> (since we are iterating over int values, we must use the wrapper class Integer here).  This, in turn, requires that you create an instance of QuadraticEquationIterator -- a class you must write that has been specialized to iterate over QuadraticEquation coefficients a, b, and c, and so it implements Iterator<Integer>.  Again, look closely at the notes for details.

      Remember:  you may not place the coefficients a, b, and c into an array, an ArrayList, or any other Collection!
       

  5. Bonus/Optional:  Sokoban
    First, if you don't know already, learn how to play Sokoban (it's fun!).  Try this game, for example:
      http://javaboutique.internet.com/Sokoban/
    Now, in a file named Hw11BonusSokoban.java, create a class that extends JComponentWithEvents which reproduces Sokoban as faithfully as you can (though you can make reasonable simplifications or other changes to the visual design as you deem appropriate, so long as you do not change the rules of the game).  Of course, there is an abundance of free Sokoban source code out there (including at the site listed above!), so (as usual) you should not look at any of that source code!  This is both as a matter of academic integrity as well as to ensure that this is a rich learning experience for you.  Feel free to add interesting options, such as a level editor, a high score list, and -- if you think you can -- a Sokoban solver!  Be sure to include your timesheet, as usual.  And have fun!!!
     
  6. Bonus/Optional:  MovieCoStars
    Note:  don't be discouraged by this lengthy description -- thanks to the power of the JCF, the actual code you will write is neither too long nor too complicated.

    In a file named Hw11MovieCoStars.java, write a program that reads in a movie database file, Hw11MovieCoStars.txt (watch the capitalization!), that contains a list of movies and their stars.  The first line of the movie database file will contain a movie title, and each subsequent line up to either the end of file or a blank line will contain one star's name, and this pattern repeats until the end of file.  For example, here is a sample movie database file with just a few movies and just a few of their stars:

    Shall We Dance
    Fred Astaire
    Ginger Rogers
    William Brisbane

    Swing Time
    Fred Astaire
    Ginger Rogers
    Betty Furness

    Keeper of the Bees
    Neil Hamilton
    Betty Furness
    Emma Dunn


    Note that there is not a trailing blank line at the end (so there is a newline to terminate the "Emma Dunn" line at the end, but no blank line after that). 

    When reading in a movie database file, your program must use these classes with these public methods (though you may add additional methods as you desire):

        class MovieDatabase {
           // construct a movie database from the given file
           public MovieDatabase(String filename) { }

           // Return a HashMap that maps movie names to instances
           // of the Movie class
           public HashMap<String, Movie> getMovieMap() { }

           // Using the movie map (see above), return the Movie with the
           // given name, or null if no such Movie exists.
           public Movie getMovie(String movieName) { }

           // Return a HashMap that maps actor names to instances
           // of the Actor class
           public HashMap<String, Actor> getActorMap() { }

           // Using the actor map (see above), return the Actor with the
           // given name, or null if no such Actor exists.
           public Actor getActor(String actorName) { }

           // Returns a string, that when printed, would print
           // every actor alphabetically followed by an alphabetized
           // list of their costars (without duplicates, and with each costar
           // indented 3 spaces).  You can assume every actor has two names
           // (first and last), and alphabetize names by last name, then first name.
           public String getCoStarsReport() { }

           // Prints the string returned by getCoStarsReport()
           public void printCoStarsReport() { }
        }

        class Movie {
          // Return the title of this movie
          public String getTitle() { }

          // Return a HashSet of all the stars in this movie
          public HashSet<Actor> getStars() { }
        }

       class Actor {
          // Return the name of this actor
          public String getName() { }

          // Return a HashSet of all the movies this actor stars in
          public HashSet<Movie> getMovies() { }

          // Return a HashSet of all the actors this actor co-stars with.
          // (That is, all the actors who also appear in movies with this actor.)
          public HashSet<Actor> getCoStars() { }
       }


    Each movie should correspond to one instance of the Movie class, and each actor should correspond to one instance of the Actor class (even though actors appear multiple times -- but that is why you have an actor map, after all:  to find the unique instance of the Actor class for each actor).

    Here is the sample output from a call to printCoStarsReport() for the file given above:

    Fred Astaire
       William Brisbane
       Betty Furness
       Ginger Rogers

    William Brisbane
       Fred Astaire
       Ginger Rogers

    Emma Dunn

       Betty Furness
       Neil Hamilton

    Betty Furness
       Fred Astaire
       Emma Dunn

       Neil Hamilton
       Ginger Rogers

    Neil Hamilton
       Emma Dunn

       Betty Furness

    Ginger Rogers
       Fred Astaire
       William Brisbane
       Betty Furness

    Note that there is not a trailing blank line at the end (so there is a newline to terminate the "Betty Furness" line at the end, but no blank line after that).  Also, as usual, be sure to add test methods as appropriate (you will be graded on your test methods, too!).

Carpe diem!