Graphic by Keith Ohlfs

CS111, Wellesley College, Spring 1998

Problem Set 6

Due: Friday, March 20 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 experience with conditionals, instance variables, and object diagrams.

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 final object diagram with the rest of your hardcopy submission. There is no softcopy submission for this part.

Laboratory Problem: Save the modified HurdleWorld.java file in the ps6_Buggles folder. When you have completed the entire problem set, upload the ps6_Buggles folder to your ps6 drop folder. Turn in a hardcopy of the HurdleWorld.java file.

Homework Problem 1: Save the modified HiLo.java file in the ps6_HiLo folder. Upload the entire folder to your ps6 drop folder. Turn in a hardcopy of the HiLo.java file.

Homework Problem 2: Save the modified FindWorld.java file in the ps6_Buggles folder. Upload the entire folder to your ps6 drop folder. Turn in a hardcopy of the FindWorld.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


Prelab: Object Diagrams

The purpose of this prelab is to give you some practice drawing object diagrams. Recall that an object diagram shows a collection of interconnected class instances. Each instance is drawn as a large box that is labelled by the class name and contains the labelled instances variables of the class. Each variable is a small box that contains either a primitive data type (e.g., int, double, boolean, char) or a pointer to another object. Because object diagrams are drawn with boxes and pointers, they are sometimes called box-and-pointer diagrams.

Consider the following Java class declaration:


class Node { public int value; public Node left; public Node right; public Node (int v, Node l, Node r) { this.value = v; this.left = l; this.right = r; } }

The Node class specifies that instances of the node class have three instance variables: a value variable that holds an integer, and variables left and right that hold pointers to other Node instances. The Node constructor takes an integer and two Node instances and intializes the instance variables with these values.

Here is a sequence of statements that creates and manipulates Node instances:

		
Node a = new Node(1, new Node(2, null, null), new Node(3, new Node(4, null, null), null)); a.left.right = a.right.left; a.left.left = a; Node b = a.left; b.left.value = a.left.value + b.right.value;

Recall that null denotes the null pointer, a special value that can fill any variable of object type in place of a pointer to an object of that type.

In this problem, you are to draw a single object diagram that depicts the variables and objects created by executing the above five statements. Your diagram should include the local variables a and b as well as all of the Node instances that are created. It should show the state of all variables and objects at the end of the execution of the five statements.

Follow these conventions:


Laboratory Problem: Hurdles

With Spring fast approaching, buggles are putting on their sneakers in preparation for one of their favorite activities: hurdle jumping! In this activity, a number of hurdles (walls one grid cell high) are placed at the base of a BuggleWorld grid. A buggle starting at position (1,1) must run across the base of the grid jumping all of the hurdles in its path until it reaches the opposite wall. The buggle does not know the placement of the hurdles in advance and so must detect them when it encounters them. For example, here is a sample hurdle configuration

and here is the result of a buggle jumping all of the hurdles

To begin this problem, download the ps6 folder from the CS111 download folder. This folder contains a ps6_Buggles folder that you will use both for this laboratory assignment and for Homework Problem 1. In the Test subfolder of the ps6_Buggles folder is an applet HurdleWorld.java that you should run with the Applet Viewer. This applet shows the correct behavior of a hurdling buggle. The applet begins with a random number and placement of hurdles.Pressing Run sets the hurdler in motion. Each time Reset is pressed, a new configuration of grid cells and hurdles is generated.

Your goal in this problem is to implement two kinds of hurdling buggles:

  1. A Hurdler that jumps all the hurdles in the grid.
  2. A TiredHurdler that jumps at most a presepecified number of hurdles in the grid.

Hurdler

The File HurdleWorld.java contains three classes, two of which are shown below:


public class HurdleWorld extends BuggleWorld {   Randomizer rand = new Randomizer(5); // Lots of code you can ignore omitted here.   public void run () { Hurdler happy = new Hurdler(); happy.tick16(); } }   class Hurdler extends TickBuggle { public void tick() {   } public boolean atFinishLine() { // Return true if buggle is facing wall (as opposed to hurdle) and false otherwise. // Should leave buggle in same position and heading as when it starts. // Should not leave any marks behind. } public void jumpHurdle() { // Cause the buggle to jump the hurdle it is facing. } }

The HurdleWorld class is responsible for changing the size of the grid and populating it with a random number of hurdles. You do not have to understand the code that performs these actions. All that you have to understand about HurdleWorld is that it has a run() method that makes a Hurdler instance named happy and tells this buggle to move for 16 clock ticks. The Hurdler class is a subclass of the TickBuggle class discussed in lecture. Hurdler has a tick() method that specifies what it does on each clock tick. This method overrides the default tick() message supplied by the TickBuggle superclass.

You should flesh out the tick() method so that it implements the following behavior:

As is often the case, the specification of the tick() method can be simplified with some auxiliary methods. You should define these methods as part of your solution:

TiredHurdler

After a long day of hurdling, buggles are often too tired to jump all of the hurdles in a configuration. The TiredHurdler class describes buggles who will jump no more than a specified number of hurdles. The constructor method for TiredHurdler takes an integer that is the maximum number of hurdles the buggle will jump. For instance, new TiredHurdler(3) creates a buggle that will jump no more than three hurdles. If it encounters a fourth hurdle, it will stop moving.

Here is the skeleton for a TiredHurdler class that you can find in HurdleWorld.java:


class TiredHurdler extends Hurdler { private int hurdlesLeft; public TiredHurdler(int hurdlesLeft) { this.hurdlesLeft = hurdlesLeft; } public void tick() {   } }

The class has an integer instance variable hurdlesLeft that keeps track of how many more hurdles the buggle is willing to jump. The constructor method for TiredHurdler initialize this instance variable to the number supplied when the instance is constructed. When a TiredHurdler runs the course, it should decrement this instance variable every time it jumps a hurdle and refuse to jump a hurdle when this instance variable contains 0.

Use the following version of run() to test your TiredHurdler:

	
public void run () { Hurdler happy = new Hurdler(); happy.tick16(); TiredHurdler harried = new TiredHurdler(3); harried.setColor(Color.green); harried.tick16(); }

This causes TiredHurdler harried to follow Hurdler happy part of the way through a course.

Turning in the Lab Problem

For your hardcopy submission, turn in the final version of HurdleWorld.java after you have finished implementing both the Hurdle and TiredHurdle classes. Your softcopy submission should be the final version of the ps6_Buggles folder after you have also completed Homework Problem 2.


Homework Problem 1: HiLo

The Game of HiLo

The game of HiLo has two players: (1) the Chooser, who chooses an integer between 1 and some predetermined upper bound (say 1000); and (2) the Guesser, who attempts to guess the chosen integer in as few turns as possible. On each turn, the Guesser makes one guess at the chosen integer, and the Chooser gives one of three responses:

  1. Correct: the guess is correct.
  2. Hi: the guess is higher than the chosen value.
  3. Lo: the guess is lower than the chosen value.

The game ends when the Guesser guesses the correct value.

HiLo is not particularly exciting to play because there is a simple optimal strategy that narrows the range of possible numbers in half each time by choosing the midpoint of the range. For example, for the range [1..1000], the midpoint guess is 500. If 500 is too high, the next range is [1..499] and next guess is 250; if 500 is too low, the next range is [501..1000] and next guess is 750. With this strategy, it is possible to guess any number between 1 and 1000 in ten or less guesses because 1000 can successively be divided by 2 only ten times before a 1 is reached. (In other words, the base 2 logarithm of 1000 is about 10). This divide-in-half strategy, known as binary search, is extremely important in computer applications for efficiently searching large information spaces.

An Graphical User Interface to HiLo

Even though HiLo may not be much fun to play, it is a good game to implement as a Java program because it nicely illustrates both conditionals and instance variables. In this problem, you will implement the behavior of a program that plays the role of the Chooser; the user of the program plays the role of the Guesser. You are provided with a user interface layout for the game and must specify how to handle events generated by the user. Below is what the user interface looks like. (You can play a working version of the game by executing the HiLo.html applet within the Test subfolder of the ps6_HiLo folder.)

Pressing the New Game button starts a new game by having the computer choose a random number between 1 and the number chosen in the Choice menu at the top of the screeen. The user then guesses numbers by typing them in next to the Guess button and pressing the Guess button. The computer provides feedback in red depending on the situation; see below for details. If the checkbox labelled Show number of guesses? is checked, then the number of guesses made so far appears in blue below the feedback phrase; otherwise, this area is blank. Furthermore, clicking the checkbox on and off dynamically toggles the blue phrase. Finally, each guess is entered in the history list with a comment about whether it was low, high, or correct. The game ends as soon as a correct guess is made; further guesses are ignored until a new game is begun.

There are several kinds of feedback that can be reported in red depending on the configuration of the game. Below is a list of all possible feedback strings with explanations. Italicized elements should be filled in by appropriate numbers.

Feedback

Displayed when ...

guess is correct.

the current guess is the chosen number.

guess is low.

the current guess is less than the chosen number.

guess is high

the current guess is greater than the chosen number.

guess is out of bounds!

the current guess is out of the range 1 to upperBound.

Please guess my number
between 1 and
upperBound.

a new game has just started.

No game in progress.
Press New Game to start.

the user attempts to make a guess when no game is in progress.

The HiLo Class

The layout of the GUI depicted above has already been implemented for you in the file HiLo.java. However, the interface as implemented does not handle any events. Your goal in this problem is to flesh out the action() method skeleton in this file so that a user can play a game with the interface as described above. If you have questions about what the game should do in a particular situation, run the version in Test/HiLoTest.html.

Recall that an action method has the following format:

	public boolean action (Event evt, Object arg)  {
		if (test for event 1) {
			statements for event 1
		} else if (test for event 2) {
			statements for event 2
		} else ... more test/statement pairs here ...
 
		} else {
			return super.action(evt, arg);
		}
		return true;
	}

Your action method should handle the following three events:

  1. Pressing of the New Game button.
  2. Pressing of the Guess button.
  3. Checking/unchecking the checkbox labelled "Show number of guesses?"

Remember that you can check for the component over which an event occured via the boolean expression:

evt.target == component-expression

Rather than writing all of your code in the action method itself, it is suggested that you write three auxiliary methods, one for each of the events you need to handle. Then the action method will simply dispatch to your auxiliary methods. This makes the code easier to read.

Below is documentation on the instance variables and methods you will need to refer to in your solution. You additionally need to know the following facts about strings:

"The value of " + two + " + " + pi + " is " + (two + pi) + "."

is the string

"The value of 2 + 3.141 is 5.141."

Note that if the subexpression (two + pi) were not parenthesized, the resulting string would be

"The value of 2 + 3.141 is 23.141."

because Java would concatenate the strings for 2 and 3.141 rather than add the numbers.

Instance Variable Documentation

Here is documentation on the instance variables of the HiLo class:

User Interface Components

Button newGameButton : Pressing this button starts a new game.

Choice upperBoundChoice : Choice menu that determines the upper bound of the computer's choice when a new game is started.

Checkbox showNumberOfGuessesCheckbox : checkbox indicating whether or not the number of guesses should be displayed.

Button guessButton : The user presses this button to make a guess.

TextField guessField : The user enters a guess in this text field.

Label feedbackLabel : Label in which the red feedback string is displayed.

Label guessesLabel : Label in which the blue number of guesses is displayed.

ModifiableList historyList : Scrollable list in which the history of guesses is displayed.

State Variables of the Game

int chosen : The number chosen by the computer for the current game

int guesses : The number of guesses made so far by the user in the current game.

int upperBound : The upper limit of chosen in the current game.

boolean done : If true, no game is in progress; otherwise, the current game is in progress.

Randomizer rand :Random number generator used for choosing number. See notes on the intBetween() method below.

Method Documentation

Here is documentation on various methods that will be helpful in your solution.

Checkbox instance methods

public boolean getState()
Returns the state of this checkbox: true if checked, false if not.

Choice instance methods

public String getSelectedItem()
Returns the label of the selected item in this choice menu.

Integer class methods

public static String toString (int n)
Returns the string representation of the given integer. E.g. Integer.toString(17) returns "17".

public static int parseString (String s)
Returns the integer corresponding to a given string representation of a number. E.g. Integer.parseInt("17") returns 17. If s is not interpretable as a number, a run-time exception occurs.

Label instance methods

public void setText (String s)
Changes the displayed string of this label to be s.

Modifiable List instance methods

public void addItem (String s)
Adds item s to the end of this list.

public void clear()
Clear all the entries in this list so that it becomes empty.

Randomizer instance methods

public int intBetween(int lo, int hi)
Returns a random integer between lo and hi, inclusive.

TextField instance methods

public String getText ()
Returns the text in this text field.

Turning in Homework Problem 1

For your hardcopy submission, turn in the final version of HiLo.java. Your softcopy submission should be the final version of the ps6_HiLo folder.


Homework Problem 2: FindWorld

In the FindWorld problem, a hungry buggle named Fiona starting at coordinates (1,1) is attempting to find a single bagel hidden in an connected acyclic maze. An connected acyclic maze is a maze in which there is a unique non-backtracking path from any point in the maze to any other. This means that it is always possible for Fiona to find her bagel! Here is an example of a connected acyclic maze:

It is possible to visit all the cells of an connected acyclic maze by using the "right-hand-on-the-wall" strategy. That is, if you always keep your right hand on a wall as you walk through such a maze, you will eventually visit all the cells. Along the way, you will visit some cells more than once (i.e., backtrack), but that's OK.

Your goal is to program members of the Finder class (such as Fiona) to find the bagel in the maze by using the "right-hand-on-the-wall" strategy to visit cells in the maze until the bagel is found. Upon finding the bagel, the buggle should not eat it. but just sit on top of it. The buggle should leave a mark behind in every visited cell except for the cell containing the bagel. Here is a snapshot of the above grid when Fiona finds the bagel:

To begin this problem, use the Applet Viewer to run the FindWorld.html applet within Test subfolder of the ps6_Buggles folder that you downloaded for the laboratory assignment. This applet contains a working solution of the maze-navigating buggle. When you start this applet, you should see the maze and bagel shown in the first picture above. Every time you click on the Reset button, a new maze and bagel will be randomly generated. When you click Run, the buggle uses the buggle uses the right-hand-on-the-wall strategy to find the bagel.

Your goal is to develop a program that instructs the buggle to correctly find the bagel in any such maze. You will use Reset and Run to test your program on a variety of trails.

You will implement yout code in the file FindWorld.java. This file contains two classes, as shown below:


public
class FindWorld extends MazeWorld { public void run () { Finder fiona = new Finder(); fiona.tick256(); // Would be nicer to stop when find bagel, // but assume a predetermined number of steps for now. } }   class Finder extends TickBuggle { public void tick() { // Override the default tick method of the TickBuggle class. // Keep "right finger" of buggle on right wall until find bagel. }   // Put auxiliary methods here }

The FindWorld class is a subclass of the MazeWorld class, whose responsibility is to draw a connected acyclic maze with a bagel in it. The run() method of the FindWorld class creates Fiona and tells her to execute her tick() method 256 times. (As usual, a predetermined number of steps is icky; we will see how to get rid of this ickiness later in the course via recursion and iteration) You do not need to modify the FindWorld class in this problem.

In this problem, you should "teach" members of the Finder class how to follow the "right-hand-on-the-wall" strategy by filling in the details of the tick() method skeleton in that class. Before writing any code, you should think carefully about the kinds of "before and after" rules your buggle should be following. That is, describe the buggle motion in terms of rules like "if the buggle is in configuration A before its move, it should be in configuration B after its move." For example, what should the buggle do when there is a wall to its right? when there is not a wall to its right? when there is a wall in front of it? when there is not a wall in front of it? These conditions are not orthogonal; e.g., the buggle may have a wall both in front of it and to its right.

Try to narrow the number of rules into the smallest number that implement a correct strategy. We strongly encourage you to work with your classmates on this part of the problem.

Once you have figured out a strategy, you should encode that strategy in Java code in the body of the tick() method within FindWorld.java. As always, you should feel free to define any auxiliary methods that you find helpful. In this problem, it is particularly helpful to define the following auxiliary method:

public boolean isWallToRight()
Returns true if there is a wall directly to the right of this buggle, and false otherwise.When this method returns, this buggle should have exactly the same position and heading as it did before the method was called.

It is possible to write a clear and correct solution to this problem in remarkably few lines of code. If you find yourself lost in a rats nest of conditionals, you are probably on the wrong track.

Turning in Homwork Problem 2

For your hardcopy submission, turn in the final version of FindWorld.java. Your softcopy submission should be the final version of the ps6_Buggles folder after you have also completed the Laboratory Problem.

Phew! When you're done with this, you'll be ready for your vacation. We hope you'll have a relaxing and enjoyable Spring Break!