Harvey Mudd College
Computer Science 60
Assignment 7, Due Wednesday, March 27 by 11:59pm


"SpamSeeker"

hw7.zip, the starter files for your BFS-based maze solving, including

Assignment 7 puts your code in the position of a laboratory mouse, seeking the shortest path from its starting point in a maze to an appropriate destination -- a destination containing, perhaps, a can of spam...! Reaching the spam by the shortest possible path will require implementing breadth-first search (BFS), as well as a Queue to help with that search algorithm.

Thus, the first part of this assignment is to create and test a Queue class -- using the class discussion and the example of Stack.java as a guide. Once your Queue is working, the second part of the assignment will ask you to use that information structure in the task of maze-solving.

Both parts of this assignment may be done individually or with a partner.  If you choose to work with a partner, you should work with the same partner on both parts of the assignment, though you may choose to do part 1 with a partner and part 2 on your own, if you like.  (Doing part 1 individually and part 2 with a partner is also possible, but more difficult because part 2 builds on part 1).

Submitting your code

For this assignment, you should submit five files under assignment #7:

Problem 0. A merge and mergeSort from assignment 6 (individual or pair)

15 points

Go back to assignment #6 to complete merge and mergeSort. A solution to the other problems from assignment #6 is available here List.java.

Problem 1. A Queue class (individual or pair)

35 points (20 points for Queue.java and 15 points for QueueTest.java)

This problem's task is to write a java class named Queue that implements a linked list that supports the standard Queue operations. Recall that a Queue is a list in which data are added (enqueued) at the "back" and removed (dequeued) from the "front."

In order to guarantee that your Queue class supports the appropriate Queue capabilities, you should make sure to download the file QueueInterface.java which consists of the following interface:

/*
* QueueInterface.java
*
* provides an interface for all of the funtions
* a Queue should implement
*
* If a class implements this interface, it can
* then be used as a Queue...
*/

interface QueueInterface
{
public boolean isEmpty();
public Object peek();
public Object dequeue();
public void enqueue(Object data);
public String toString();
}
Then, as you define your Queue class, you should declare that it will implement this interface, as follows:
class Queue implements QueueInterface
{
... rest of the Queue class here ...
This technique uses the compiler to make sure that your code adheres to the contract set forth by the QueueInterface. A similar example is use in Stack.java, in which the Stack class implements StackInterface.

More generally, you might want to use Stack as a good starting place for your Queue class. The source code for Stack and StackInterface are available, and we will cover/have covered both in class.

Inner class: QCell

Similar to the way in which the Stack class used an inner class named StackCell, Your Queue.java file might use an inner QCell class that will be the type of each cell in your Queue.

Test Driven Development Practice

The description below describes the required methods to write for Queue.java. Please use Test Driven Development (TDD) and write the test cases for each of these methods before writing the corresponding code. You will need to write JUnit test cases in the file QueueTest.java for the methods enqueue, dequeue, peek, and isEmpty described below. Here is a possible ordered list of tasks that uses TDD (If you don't have experience with TDD, please follow this ordered list!):

  1. Create the Queue class (with instance variables) and write the constructor
  2. Write toString and test in the main method that it works for a newly constructed Queue (we don't have any way to add QCells to the Queue - so don't worry about that yet).
  3. Add the enqueue method header to your file Queue.java (don't implement it yet!)
  4. Write test cases for enqueue that check if the output of toString on that Queue is equals (using equals) to what you expect (these won't pass yet).
  5. Write the code for enqueue.
  6. Test (and fix) enqueue and toString in tandum by using your JUnt test cases, print statements in the main method, and the Eclipse debugger.
  7. Add the isEmpty method header to your file Queue.java (don't implement it yet!)
  8. Write test cases for isEmpty (these won't pass yet)
  9. Write and debug your code for isEmpty
  10. Add the peek method header to your file Queue.java (don't implement it yet!)
  11. Write test cases for peek (these won't pass yet)
  12. Write and debug your code for peek
  13. Add the dequeue method header to your file Queue.java (don't implement it yet!)
  14. Write test cases for dequeue (these won't pass yet)
  15. Write and debug your code for dequeue

Data members for Queue

The Queue class itself should have two data members:


  private QCell front; // the front of the Queue object - cells are removed from here
  private QCell back;  // the back of the Queue object - cells are added to here

Format to use for toString

In order to make testing your Queue class as uniform as possible, we ask that you use the following format for your toString method. Notice that <FRONT> and <BACK> always are printed, and <FRONT> always has a single space after it. When elements are present, they are printed from front-to-back, each with a single space after it, including the final element. <BACK>, however, does not have a space following it.

Feel free to use the toString method in Stack.java as a guide... (it's very similar):

What to include in your Queue class:

Reminder: Your code in Queue.java should be clearly commented.

Your test cases in QueueTest.java should be clearly commented and will be evaluated based upon whether or not they catch bugs in ten buggy versions of the Queue class we have written.

Problem 2. A Maze solver (individual or pair)

50 points (40 points for Maze.java and 10 points for adding addition tests to MazeTest.java)

In the Maze.java file in the top-level assignment directory, you will find two classes:

What is already in Maze?

The Maze class has a constructor for creating a new Maze object from a String. There are example input Strings provided in MazeTest.java. In the input String the asterisk '*' represents the walls, spaces are free space, and letters are special free space: 'S' is the start, and 'D' is the destination for the maze.

The Maze class also has a toString method, a method named findMazeCell that will return a MazeCell that is holding a particular character (S or D, usually).

What needs to be implemented?

You need to add test cases for super-small Mazes (that you invent) to MazeTest.java. You should do this BEFORE writing your BFS code. All of your test cases should be commented to explain what they test.

The method that needs to be implemented is

   public ArrayList<String> BFS(MazeCell start, MazeCell destination)
This BFS method needs to either:
  1. find a shortest solution to the maze by breadth-first search, then:
  2. OR realize the maze is unsolvable then:
We will discuss the BFS algorithm, along with a related algorithm called depth-first-search, in class to help guide your implementation.

Notes and details...

Here is what the Maze shown in the example above might look like when printed after calling BFS:


**********
*S*      *
*o* oooo *
*oooo**o *
*    *Do *
**********
There certainly may be more than one shortest path (a tie for shortest). For example:

**********
*S*      *
*o*ooooo *
*ooo **o *
*    *Do *
**********
The difference would be that, in the latter example, the maze-solving "mouse" searches North before searching East; in the former example, the opposite is true.

To ease testing, we require that you consider paths starting with North (r-1,c), then South (r+1, c), then West (r, c-1), then East (r,c+1).

Optional Extra Credit: Maze-solving on a sphere

For completely voluntary bonus credit (up to +5 points), change your Maze class to handle mazes that do not necessarily have walls all around their outermost edge. In fact, you should interpret any edge as being connected to the corresponding "wrap-around" edge of the maze. This may lead to even shorter paths! For example, this is m5 from MazeTest.java, and is only slightly altered from above:


******** *
 S*       
* *      *
*    **  *
*    *D  *
******** *
but now the shortest path is potentially very different, wrapping around twice...

********o*
oS*     oo
* *      *
*    **  *
*    *Doo*
********o*
Another shortest path wraps only once, in the case that our mouse looks south before north.