In class, we talked about how the
Stack
data type can be used to help an airline agent search and find whether there are flights
connecting two cities on the map.
This is just one among a
class of problems in which there is an initial state, a goal to be
reached, and a set of possible actions that can be performed at each
point in the problem solution. The aim is to find a sequence of
actions that successfully reaches the goal from the initial state.
The Stack
data type provides a natural means for implementing the strategy of
depth-first search for finding a solution.
Believe it or not, this is not a new idea. In the myth of the Labyrinth,
a group of young Athenians were sent to be consummed by a monster, the Minotaur,
residing in a maze-like structure, the Labyrinth. A smart
Athenian, Theseus, outsmarted the monster by finding him first and killing him.
In his effort, he had the advice of Ariadne, the beautiful daughter of King Minos.
She gave him a string that he used, like a stack, for the purpose of keeping track of
where he had been.
She also told him to keep his left hand on the wall so that he could search the
Labyrinth exhaustively. That's what Computer Scientists call
depth-first-search.
In this problem, you will implement Theseus' search.
You will complete a program to search a two-dimensional maze, and you will use a
The maze pattern will be stored in a two-dimensional (2D) array. Before you begin programming, you should familiarize yourself with how to create, initialize and access the contents of a 2D array. For simplicity, the maze will be stored in a 2D array of int values. Different integers can be used to represent free space and walls in the maze. In the sample maze below, 0 represents a free space and 1 represents a wall:
0 1 1 0 0 1
0 0 0 0 1 1
0 1 1 0 0 1
1 0 0 0 0 1
0 0 1 1 0 0
1 0 0 0 0 0
Assume that during the search for a path through the maze, it is only possible to move in the horizontal or vertical direction (not diagonal). Given a starting location at the upper left corner of the maze, and a goal location in the lower right corner, the following picture shows a successful path through the maze, using the integer 8 to label the positions along the path:
8 1 1 0 0 1
8 8 8 8 1 1
0 1 1 8 0 1
1 8 8 8 0 1
0 8 1 1 0 0
1 8 8 8 8 8
In general, a different path could be found, depending on the details of how the search process is implemented.
One of the first steps in the design of an object-oriented program is to consider what information needs to be represented in the problem solution. For the maze search problem, this information includes the maze itself, locations in the maze, and the path through the maze. If you hand simulate the search process, you will soon discover that it also helps to keep track of locations that you have already explored, to avoid running around in circles. Some of the information to be represented in a problem solution may be grouped within separate classes of objects that have instance variables to store the information.
Partial code for this problem can be found in the directory /home/cs230/download/Maze, which you should copy into your own directory. Study the existing code before starting your programming. This directory contains three code files:
Your task is to complete the definition of the Maze class. First, write a constructor method that has a single input that is a 2D array that contains a sample maze. The MazeTest.java file contains code that illustrates how this constructor method can be invoked. Second, write a method named searchMaze() that searches for a path through the maze stored in mazeArr. This method should have four inputs that specify the row and column of the starting location for the search, and the row and column for the goal location. The MazeTest.java file also contains code to illustrate how this method can be invoked. The searchMaze() method should use a Stack to represent the current partial path through the maze during the search process. The items placed on the Stack should be single Place objects. The following English description embodies the basic search strategy. The overall strategy is similar to the graph search algorithm that we discussed in class. The expression "current Place" is used to refer to the location of the maze that is currently occupied during the search.
Push the starting Place onto
the Stack, mark this location as visited, and set the current
Place to the starting Place
Repeat the following actions until the goal Place is reached:
Search for a
new Place that is adjacent to the current Place, and is an
open space in the maze that has not yet been
visited
If
such a new Place is found, push it onto the Stack, mark this new
location
as visited, and set the current
Place to this new location
If
no such Place is found, the search process has reached a dead end and needs
to back up.
In this
case, pop
the Place that is on top of the Stack, and set
the current Place to the new Place
that is now on top of the Stack
When the loop is done, print the successful path that is stored on the Stack
You should consider the possibility that the maze cannot be solved, given the pattern of walls (i.e. there does not exist a valid path from the start to the goal position). How can you modify the above English description to detect this situation? The searchMaze() method should print a message in this case, indicating that the maze cannot be solved. Also keep in mind that when searching for a new Place that is an open space in the maze that has not yet been visited, you should only consider the four neighboring locations to the left, right, above and below the current location. It does not matter what order you consider these four locations. It is ok to define one or more helper methods, in addition to the searchMaze() method.
Submission details: First, make sure that you have included enough testing code in your program to show us that you have done a reasonable job in testing it. (You need to figure out what is "enough"). Hand in a hardcopy of your final Maze.java code file, and drop off an electronic copy of your program by connecting to your Maze directory and executing the command:
submit cs230 Maze *.*
Suppose that Java did not provide a built-in
Stack
class. Fortunately, we can create our own Stack
class using any of a variety of data structures to store the
contents of the stack. In lecture, we created a stack interface,
StackInterface, and implemented the interface
with a class called
StackLinkedListBased, which used a
LinkedList to store the contents of the
stack. In this problem, you will define a new class that implements
StackInterface using a
Vector to store
the contents of the stack.
Early in the semester, we emphasized the
importance of separating the formal specification of an abstract data
type from its implementation details. One advantage of doing this is
that the implementation can be changed, perhaps to be more efficient,
without affecting the correctness of application code that uses this
data type. Since your new class,
StackVectorBased will implement
StackInterface, any code that uses
an implementation of StackInterface
will run correctly if it utilizes your new class. To begin your implementation, download the
directory /home/cs230/download/StackVectorBased
into your own directory. This directory contains the file
StackInterface.java that you
must implement, the file StackLinkedListBased.java
that was developed in class and implements the
Submission details: Hand in a hardcopy of your StackVectorBased.java code file, and drop off an electronic copy of your program by connecting to your StackVectorBased directory and executing the command:
submit cs230 StackVectorBased *.*
In class, we discussed the issue of algorithm efficiency and the important question, "As the size of the problem grows, how does the amount of computation grow?" In this problem, you will compare two strategies for performing the same task, with this question in mind. Here is a code file named Algorithms.java that contains two methods that each determine whether an input array contains integers that are sorted in increasing order. First examine the two methods carefully and summarize each implementation strategy in words. Let the size of the problem be defined as the size of the input array, and denote this size by n. For each method, what situation represents the "best case" for the algorithm, requiring the least number of array references to determine the value to be returned? What situation represents the "worst case", requiring the most array references? For the worst case, determine the total number of distinct array references performed by each implementation, as a function of n. Which strategy is most efficient, and why?
Submission details: Hand in a typed (not handwritten) hardcopy of your work. You can draw by hand any graphs that might help us understand your work.