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:
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
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.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
.
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.
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