Graphic by Keith Ohlfs

CS111, Wellesley College

Mutual Recursion Example
PathFinderWorld
[CS111 Home Page] [Syllabus] [Lecture Notes] [Assignments] [Programs] [Documentation] [Software Installation] [FAQ] [CS Dept.] [CWIS]

 

Scenario

We have a maze with either 0 or 1 bagel. We create a new buggle and pick up its brush. Now we want the buggle to find the bagel and draw the shortest path from the bagel back to its starting position. The position and heading of the buggle at the end of the exercise should be the same as at the beginning. If the buggle found a bagel, its brush will be down at the end. Otherwise, its brush will remain in the same state it was in before it started trying to find the bagel. The buggle's method findBagel returns a boolean value of true if the buggle found a bagel and false otherwise. We can assume that the buggle has a wall to its back when we call the findBagel method.

Breaking the problem into subproblems

There are two tasks to this problem. First, we need to find the bagel. Then, we need to draw the path from the bagel back to its start.

Solving subproblem 1: Finding the bagel

What is the simplest case, our base case?
If the buggle is over a bagel when it starts, then it has found the bagel.

What if it is not the base case?
The buggle will have to search to the left, front, and right for the bagel. It doesn't have to search behind because it starts with its back to a wall and as it moves forward searching for bagels, it will have already searched the cells behind it. Searching in any direction is essentially all the same procedure. So, if the buggle can search for a bagel in front, it should be able to search to the left and right by turning in those directions and then searching in front.

How do we search for a bagel in front?
This is a subproblem of the original problem so we approach it as its own problem.

What is the simplest case, our base case?
If there is a wall in front of the buggle, there can't be a bagel in front.

What if it is not the base case?
The buggle will have to go forward into the cell in front to check if there is a bagel there or not. So, the buggle will go forward. How does it check if there are bagels there and/or around it? That's what findBagel does. So, if the buggle calls findBagel it will know if there is a bagel in that cell or around it. After the buggle has found out the answer, it should return to its initial location by going backwards.

Solving subproblem 2: Drawing the path

To draw a path from the bagel to the start, the buggle needs to put its brush down when it finds a bagel. The "shortest" path is achieved if we use a recursive method of finding the bagel and make sure to stop searching for the bagel when it is found. Our solution to the first subproblem does this if we use the logical OR ( || ) operator which short-circuits evaluation of its arguments. Assuming we ask the buggle to first search to the left, then the front, and finally the right, a short circuit evaluation will not search to the front or the right if the buggle found a bagel to its left.

Putting it all together: Writing the code

That's all there is to it. The final code is presented below. The things to note are:
  1. Each method has a clearly defined contract which is explained in the comments in the method
  2. This is an example of mutual recursion because findBagel calls findLeft, findFront, and findRight, which in turn call findBagel
  3. We use abstraction to name the process of finding a bagel in a particular direction (in this case, the front) and then reuse the method we have defined to define variations of the method (i.e. findLeft and findRight)
  public boolean findBagel() {
    // This method returns true if there is a bagel
    // to the left, front, or right of the buggle,
    // otherwise, it returns false.
    // If it returns true, it draws a path from the bagel
    // up to (but not including) its initial position.
    // The state (position and heading) of the buggle
    // must be left unchanged by the invocation of the method.
    // The buggle's brush will be down if it found a bagel
    // and left unchanged if it did not.

    if (isOverBagel()) { // base case: found the bagel
      brushDown();       // put the brush down
      return true;
    } else {             // search for bagel in surrounding cells
      return findLeft() || findFront() || findRight();
    }
  }

  public boolean findFront() {
    // This method returns true if there is a bagel
    // to the front of the buggle,
    // otherwise, it returns false.
    // If it returns true, it draws a path from the bagel
    // up to (but not including) its initial position.
    // The state (position and heading) of the buggle
    // must be left unchanged by the invocation of the method.
    // The buggle's brush will be down if it found a bagel
    // and left unchanged if it did not.

    if (isFacingWall()) { // base case: wall in front, no bagel
      return false;
    } else {              // search for bagel starting from cell in front
      forward();          // go forward
      boolean result = findBagel();  // check if bagel here or around
      backward();         // undo the forward, maintain buggle state
      return result;
    }
  }

  public boolean findLeft() {
    // This method returns true if there is a bagel
    // to the left of the buggle,
    // otherwise, it returns false.
    // If it returns true, it draws a path from the bagel
    // up to (but not including) its initial position.
    // The state (position and heading) of the buggle
    // must be left unchanged by the invocation of the method.
    // The buggle's brush will be down if it found a bagel
    // and left unchanged if it did not.

    left();
    boolean result = findFront();
    right();
    return result;
  }

  public boolean findRight() {
    // This method returns true if there is a bagel
    // to the right of the buggle,
    // otherwise, it returns false.
    // If it returns true, it draws a path from the bagel
    // up to (but not including) its initial position.
    // The state (position and heading) of the buggle
    // must be left unchanged by the invocation of the method.
    // The buggle's brush will be down if it found a bagel
    // and left unchanged if it did not.

    right();
    boolean result = findFront();
    left();
    return result;
  }