CMPSCI 187: Programming With Data Structures

David Mix Barrington

Fall, 2012

Programming Project #3: Capitals for Continents

Originally posted 11 October 2012, due at 11:59 p.m. EDT on Wednesday 24 October 2012, by placing .java files in a subdirectory of your cs187 directory on your edlab account, called proj3. For more information on accessing the edlab, see here or ask in discussion section.

Some updates in purple on 12 October 2012.

Some more updates in brown on 12 October 2012. The class you construct should be named MapGrid, not Continent, because that name is just too confusing.

Some more changes on 19 October 2012. Note that the q and a page now has something on it. (Link fixed 21 October)

MAJOR change on 21 October: I am redefining Project #3, still due on Wednesday, to include only the first task below, the continentMap method. The other tasks will form part of Project #4, to be due on Monday 5 November. Also, a grading driver is now available.

Goals of this project:

  1. Write a program using recursion, though this will be in the borrowed code.
  2. Write a program using queues to solve a search problem.
  3. Write a program using an existing code base from the textbook.

Questions and answers about this project are collected here.

In Section 4.4, DJW present code for a class called Grid, where a Grid object has a two-dimensional array of booleans. You can download their code from their course website by following the instructions on page 28.

They define a blob as a set of grid cells such that from any cell in the blob, you may travel to any other cell in the blob but to no cell not in the blob. Here "travel" means to take a sequence of moves each directly north, south, east, or west, and use only cells in which the grid's boolean entry is true. Note that diagonal moves are not allowed.

Because I am a fan of the Civilization family of computer games, I prefer to call the true cells land cells, the false cells water cells, and the "blobs" continents. So in my terminology, their method countBlobs determines how many continents are on the map, and their method markBlob marks all the cells on the continent of the cell given by its parameters.

All of your code will be in a class called MapGrid, which will extend the class Grid. So all the DJW code for Grid should be in your edlab directory or somewhere else where it can be found from your edlab directory.

As explained in Lecture 15, we want you to write a new constructor for MapGrid, which will mimic their constructor for Grid except for one thing -- it will have an additional int parameter called seed, and will create its Random object with that seed instead of without a parameter. This will allow us to test your program on maps that are randomly generated using seeds of our choice, making grading easier. (It is too bad that we have to repeat the code instead of just calling it, but their code is insufficiently flexible for that.)

Your first and easier task is to alter their code so that instead of just counting the continents, it names them and is able to return a string that denotes the map with the continents' names. For consistency, we will insist that the first continent be named 'A', the second 'B', the 26th 'Z', the 27th '[', the 33rd 'a', and so on. If the name is a char and we have an array indexed by the continents, then the name of continent i is given by the Java expression 'A' + i. (Don't use "A" or it will interpret the addition as concatenation.)

You should write a method continentMap( ) that returns a String that has a line for each row of the map, a hyphen for each water square, and a letter denoting the continent name for each land square, as in:


A-A--B--C--
AAA--B-----
--AA-B-----
-----BB----
----D---EEE

Note that you should number the continents in the order that you encounter them, going row by row and going left to right in each row.

This first task will make use of markBlob, or something like it, to identify all the cells in a continent when you first encounter the continent. Unfortunately DJW made markBlob private rather than protected. You may fix this in Grid (as we will grade your work using the version of Grid that is in your directory), or you may write a new version of markBlob to go in MapGrid.

There will be partial credit for carrying out this first task -- you are probably best off doing it and testing it first before starting on the second task.

Your second task is where you will use queues. The distance between two land squares (if it exists) is the length of the shortest path from one to the other, using only north-east-south-west moves on land squares. We want to find the best cell for a capital of each continent. A cell makes a better capital the more centrally located it is -- we define the centrality of a cell to be the largest distance from that cell to any other on its continent, and we want the capital to be a cell with minumum centrality.

In our example above, continent A has two cells that are within three steps of any other cell on the continent -- the second and third cell in the second row. No cell has every other cell within two steps, so these are the two candidate capitals. We want you to choose the first candidate with the best centrality. That is, you test the longest distance for every cell, and move the capital only when you find a candidate with strictly smaller longest distance.

In our example, the A capital would be the second cell in the second row. The B capital would be the B cell in the third row. The C capital is of course the only c cell, and likewise the D capital. Finally the E capital is the middle of that continent's three cells.

You should have a method capitalMap that returns a String like this:


A-A--B--*--
A*A--B-----
--AA-*-----
-----BB----
----*---E*E

I don't mind that the distinction between continents C and D is obscured on the map -- small continents have to live with that sort of thing.

You should also write a method continentTable( ) that returns a String that is a table like the following:


Continent  Capital  Centrality

A          1,1      3
B          2,5      2
C          0,8      0
D          4,4      0
E          4,9      1

Of course to make these output methods work you are going to want to give each grid a field that keeps track of the continents, probably in the same format as the continent table.

Now, the Fun Part!

How do we compute the centrality of a cell? The easiest way to do this is somewhat similar to the markBlob method, in that we "mark" all the cells on the continent. But instead of the stack implicit in the recursion in markBlob, we'll use a queue. As we'll see in Chapter 5 of DJW and starting in Lecture 17, a queue is a data structure that holds objects, can accept new objects that are enqueued, and can dequeue the object they have been holding the longest.

We will make a queue of Move objects from Project #2. When we start our search from cell (i,j), we will visit all the cells on (i,j)'s continent and enqueue a Move for each cell as we visit it. This Move will have three int fields: row for the row number of the cell we are visiting, col for the column number of the cell we are visiting, and dist for this cell's distance from (i,j).

As in markBlob, we will mark each cell as visited when we encounter it. The key fact is this: if we are looking at a cell with distance d, and we see an unseen land neighbor of this cell, then it has distance d+1. We begin by putting (i,j) on the queue with distance 0. We then dequeue the oldest element in the queue, which is (i,j) because it's the only element. We look for all the unseen land neighbors of (i,j) and enqueue each of them with distance 1. Then we dequeue one of them (it doesn't really matter which) and enqueue its unseen land neighbors with distance 2. We continue in this way until the queue is empty -- when it is, the centrality of (i,j) is the distance of the last Move that we dequeue.

You should put your classes in your EdLab space, in a directory called "cs187/proj3", and test the behavior with a main method or a driver class. (We will test the code with our own driver class.)

If you create your code within Eclipse (which we encourage but don't require), you will want to ignore the warning message that says "The use of the default package is discouraged". Bear in mind that if you make the mistake of declaring your class in a package, our driver class will not see it (since it will not be in a package) and we won't be able to test your code.

Last modified 21 October 2012