Graphic by Keith Ohlfs

CS111, Wellesley College, Spring 1998

Problem Set 8

Due: Friday, April 10 by 4:00 p.m.

[CS111 Home Page] [Syllabus] [Students] [Lecture Notes] [Assignments] [Programs] [Documentation] [Software Installation] [FAQ] [CS Dept.] [CWIS]

Reading Assignment:

About this Problem Set

The purpose of this problem set is to give you more experience with recursion, including practice with invocation trees, and writing recursive methods which return values.

There are 3 pieces to this problem set: the prelaboratory assignment, the laboratory assignment and the homework assignment. You are required to do all three parts. We recommend that you do the prelaboratory problem before your lab section, the laboratory problem during your lab section and the homework problems after completing the prelab and lab assignments.

How to turn in this Problem Set

Prelab Problem: Turn in your invocation tree diagram with the rest of your hardcopy submission. There is no softcopy submission for this part.

Laboratory Problem: Save the modified Hurdle1World.java file in the HurdleWorld folder. Upload the entire folder to your ps8 drop folder. Turn in a hardcopy of the Hurdle1World.java file.

Homework Problems : Save the modified HungryWorld.java file in the HungryWorld folder. Upload the entire folder to your ps8 drop folder. Turn in a hardcopy of the HungryWorld.java file. Save the modified PathFinderWorld.java file in the PathFinderWorld folder. Upload the entire folder to your ps8 drop folder. Turn in a hardcopy of the PathFinderWorld.java file.

Turn in only one package of hardcopy materials. Staple your files together with the cover page, and submit your hardcopy package by placing it in the box outside of Jennifer's office (E104, directly across from E101).

Reminders


Pre-Lab: Invocation Tree for Pascal's Triangle

As discussed in class, an invocation tree is an abbreviated version of a Jave Execution Model. It contains a node for each method called during the execution of a program. Each node contains the name of the method, the values of its parameters, and (where applicable) its result. There is an edge connecting each "parent" node to the "children" nodes for the method invocations encountered directly within the body of the parent. The children nodes are all shown at the same vertical level, arranged from left to right in the order of their execution.

For example, consider a recursive definition of the Fibonacci function:

	public int fib (int n) {
		if (n < 2) {
			return n;
		} else {
			return fib(n-1) + fib(n-2);
		}
	}

Below is an invocation tree for the invocation fib(6). Each node has the form fib(n):r, where n is the parameter of the invocation of fib and r is the result returned by the invocation.

In this problem, you will draw an invocation tree for the invocation of a method that computes an element of Pascal's triangle. Pascal's triangle is a triangular arrangement of numbers whose outer edges consist of 1s and each of whose inner elements is the sum of the two numbers immediately above it to its right and left:

                      1
                   1     1
                1     2     1
             1     3     3     1
          1     4     6     4     1
       1     5    10    10     5     1
    1     6    15    20    15     6     1

Let P(r,i) indicate the value of the ith element in the rth row of Pascal's triangle, where rows are numbered from top to bottom starting with 0, and elements in a row are numbered from left to right starting with 0. For example, P(4,0) = 1, P(4,1) = 4, and P(4,2) = 6.

Here is a recursive method that computes the value of P(r,i):

 
	public int P (int r, int i) {
	  if ((i == 0) || (i == r)) {
  		return 1;
  	} else {
  		return P(r - 1, i - 1) + P(r - 1, i);
  	}
	}

For this problem, you are to draw a complete invocation tree for the invocation P(6,4). Each node of your tree should have the form P(r,i):a, where r is the row number, i is the element number within a row, and a is the answer returned by the invocation. Be sure to draw your nodes small enough so that they all fit on a single piece of paper. All children of the same parent should appear at the same vertical level, as in the fib example above.


Laboratory Problem: And Even More Buggle Hurdles - Detecting the Finish Line and Counting Hurdles

Task 1: In Problem Set 7, the buggles learned to jump hurdles of arbitrary height by using a recursive method. When a number of hurdles were placed at the base of a BuggleWorld grid, a buggle starting at position (1,1) could run across the base of the grid jumping all of the hurdles in its path until it reached a bagel (the finish line).

This assignment is similar to problem set 7; just as before, the buggles do not know the placement of the hurdles and do not know the length of the race. The difference is that the race committee has run out of bagels. So, the buggles must learn to stop the race by detecting the opposite wall. You may recall that the buggles learned to do this when they jumped hurdles of a known height. However, it is more difficult to detect the opposite wall with hurdles of arbitrary height, since the buggles do not know whether they are jumping a hurdle, or going up the wall! At least, that is, until they run into the top of the grid (which is, indeed, how you distinguish between the two).

Therefore, the buggle has the following behavior:

After the race, the grid should appear as:

To see a working applet, download the the ps8_programs folder from the CS111 download directory. Look in the HurdleWorld folder for the Test folder. Open the Hurdle1World.html file from the Test folder with the Applet Viewer. Experiment with the HurdleWorld to make sure you understand the requirements for task 1.

Next, open the HurdleWorld folder from the ps8_programs folder. Open the project window and the Hurdle1World.java file. This file contains a number of methods responsible for setting up the hurdle race. You do not need to understand these files to successfully complete this lab. The file also contains a run() method, and a Hurdler1 class containing four instance methods: runHurdles(), atFinishLine(), findTop(), and jumpHurdle(). You have been given the runHurdles(), atFinishLine(), and jumpHurdle() methods, which you should recognize as the recursive solution to jumping hurdles of arbitrary height from problem set 7. You need to fill in the findTop() method stub to detect the opposite wall. Notice that this method is recursive, and returns a boolean value. Let's work on how to write findTop() in Lab.

Task 2: As a second exercise, we will again use recursion with a return value, to count the number of hurdles that the buggle has jumped for a particular race. When the race is completed, the buggle should change color, and draw a line of length corresponding to the number of hurdles jumped, as shown:

To see a working applet, look in the HurdleWorld folder for the Test2 folder. Open the HurdleWorld2.html file from the Test2 folder with the Applet Viewer. Experiment with the HurdleWorld to make sure you understand the requirements of task 2.

Re-open the Hurdle1World.java file in the HurdleWorld folder from task 1. You will simply be adding your modifications for task 2 to this file. Specifically, change the runHurdles() method so that it:

You will also need to change the invocation of runHurdles() in the run() method. Let's work on this together in Lab.


Homework Problem 1: Hungry World

This problem deals with a buggle that has the following behavior. The buggle starts out in the middle of the first row of a grid facing north. The grid cells are randomly populated with a user-specified number of bagels; however, the bagels are initially placed so that none are in the column in which the buggle initially faces. The buggle moves row-by-row from the bottom row to the top row, and performs the following action at every row:

For example, suppose the initial grid configuration is:

Then the final grid configuration should be:

To see a working applet, look in the HungryWorld folder for the Test folder. Open the HungryWorld.html file from the Test folder with the Applet Viewer. Experiment with the HungryWorld to make sure you understand the task.

Part a. Assume that you are provided with a public void eatRow() method that implements the row-eating behavior described by the three bullets above. Use this method to implement the following method:

public void eatRows()
Apply the eatRow() method at every row in the grid as the buggle moves
from the bottom row to the top row. 

Part b. Assume that you are provided with the following methods:

public int countBagels()
Return the number of bagels between the buggle and the wall it is facing.
Calling this method should not change the state of the buggle. 
	
public void eatBagels()
Eat all the bagels between the buggle and the wall it is facing, 
leaving behind a colored square in every cell that originally contained a bagel.
Calling this method should not change the state of the buggle. 
Using the above two methods, implement the public void eatRow() method mentioned in part a.
 
Part c. Implement the  public int countBagels() method described in part b.
 
Part d.  Implement the  public void eatBagels() method described in part b.


Homework Problem 2: PathFinder World

In Problem Set 6, you taught a buggle to find a bagel in a maze by walking forward and turning so that her right hand was always on the wall. The result of the maze navigation process was rather unsatisfying, however. The buggle typically colored in large segments of the maze, most of which were blind alleys that did not lead to a bagel.

In this problem, you will implement a more elegant maze searching program in which the buggle, upon finding the bagel, draws the shortest path from the bagel back to its starting position (the lower left corner). The following graphic shows the final configuration of the grid after a sample search:

You should begin this problem by experimenting with the working applet in the Test subfolder of the PathFinderWorld folder within ps8_programs. Open the PathFinder.html file from the Test folder with the Applet Viewer. The buggle world grid should contain the initial maze shown above. Pressing Run creates an eastward facing buggle in the lower left corner and causes her to search for the bagel and ultimately draw a path back to her starting position and heading. After each successful search, pressing Reset draws a new random maze, and pressing Run will solve the new maze. Experiment with the PathFinderWorld to make sure you understand the correct operation of the search.

Even if you play with PathFinderWorld for a very long time, it will probably not be at all obvious to you how the buggle accomplishes her task. In a typical maze, the buggle seems to wander around for a while, exploring various blind alleys, until she happens to find the bagel. At that point she very purposefully draws the shortest line directly back to the starting position. How does she do it?

It turns out that the buggle is following a rather clever recursive strategy. In order to explain the strategy, it is first necessary to introduce some terminology. The buggle always enters a grid cell via some direction. We will use the integers 1, 2, 3, and 4 to number the sides of the cell relative to the direction in which the cell was entered. These integers, called the search orientation, are assigned as follows:

The following diagrams depict these labelling conventions. In each diagram the buggle has moved forward from the striped position to the solid position. The sides of the current cell are labelled by the search orientation.

(Detail: The buggle never actually enters its starting cell in the lower left corner,. However, it turns out that it's OK to say that it entered this cell via an easterly entry direction, and assign the search orientation labels accordingly.)

We are now in a position to describe the search strategy of the buggle. In this problem, you will encode this strategy in terms of the public boolean search (int search_orientation) method. The search method takes a single integer parameter that indicates which wall the buggle is facing when search is called. For instance, an argument of 1 indicates that the buggle is facing to the left of the direction via which it entered the cell.

Similarly, an arguments of 2 indicates that the buggle is facing in the same direction in which it entered the cell:

Analagously, an argument of 3 means that the buggle is facing to the right of its entry direction, and an argument of 4 means that the buggle is facing opposite its entry direction.

Here is the contract for the search method:

public boolean search (int search_orientation)

This contract for search is rather complex, but it has a big payoff: search specified in this manner has a natural recursive decomposition. In particular, suppose search(s) is invoked in grid cell A with s being 1, 2, or 3. If the the direction labelled s is not blocked by a wall, then the buggle can step forward in that direction from A into the next cell (call it B), turn left (thereby facing search orientation 1 in B) and call search(1). This will return a boolean (call it result) indicating whether there is a path to the bagel from any of the three orientations in cell B. At this point, the buggle can return to its original position in cell A by turning right and stepping backward. If result is true, the buggle can immediately return true, because a path to the bagel through cells A and B has been found. But if result is false, the buggle can turn right and explore the other orientations of cell A via search (s+1). Of course, this right must later be undone by a corresponding left, and whatever value is returned by search(s+1) in cell A must be returned by search(s) in cell A.

Your goal in this problem is to define the search method based on the above discussion. The class PathFinder within the file PathFinderWorld.java contains a method skeleton for search that you should flesh out. You should pay attention to the following notes in your definition: