CSE373—Data Structures and Algorithms
for Nonmajors
Programming Assignment #1
due: Wednesday,
10/26/05, 10:00 PM
Overview:
This assignment introduces you to some concepts
in Artificial Intelligence while letting you practice using lists, iterators, stacks, and queues. The assignment attempts to solve a MineSweeper board using explicit rules and discovered
rules, both in a local neighborhood and globally (for the endgame, when the
total number of mines remaining beomes significant
and the size of the problem becomes tractable.)
An explicit rule, implemented as ZerosMineStrategy, is:
0 |
|
|
|
? |
|
|
|
|
The rule is that the middle square can be safely
explored, because it’s neighbor says that it has no
mines as neighbors.
It has 7 other forms (the zeros in the other
neighboring squares).
Another explicit rule, implemented as CountMineStrategy, is:
|
m |
|
|
2 |
|
m |
|
? |
The rule is that the bottom-right cannot be a
mine, because there are already two mines around the middle square.
The number 2 is generalized to n, and the ? can be in the other corners.
1 |
m |
2 |
2 |
4 |
|
m |
3 |
? |
The same rule applied contrarily. The bottom-right must be a mine, because the
only two remaining squares need to be mines for the 4 in the center to be
correct.
The number 4 is generalized to n, and the ? can be in the other corners.
More complicated deductions can be made, for
example:
0 |
0 |
0 |
1 |
2 |
1 |
|
? |
|
The ? has
to be empty, because if it were a mine, one of the remaining squares would have
to be a mine too (to satisfy the 2). But
if one of those other squares were a mine, then the 1-neighboring-mine
constraint would be violated.
It becomes impossible to explicitly define and
implement all such more complicated rules, so other techniques must be employed
instead. The alternative that we will
use is to sample the space of possible configurations and see if there are any
consistencies. For example, consider
m |
3 |
1 |
? |
? |
? |
There are 8 combinations for
???: ---, *--, -*-, --*, **-,
*-*, -**, ***, of whch only **- and *-* satisfy the
constraints. Both of those possibilities
have a mine in the first position, but they differ in the locations of the
other mine. So we can conclude that
there’s a mine in the first position.
When we do this same analysis on a larger space (5x5), the total
combinations become much larger, so large that we can’t possibly try them
all. So instead we try a random percent
of them, the idea being that we’re unlikely to get so unlucky that we’ll get a
falsely consistent result.
The DeriveRuleStrategy
performs such an analysis on 5x5 windows, while the EndGame
performs such an analysis on all remaining squares. The other main difference between the two is
on the significance of the number of remaining mines. For the former, if there are a lot of
remaining mines, the probability of a mine at a given square is independent of
any other square. But for the latter,
when the amount of mines are few, a mine somewhere could force the existence of
a mine nearby, but there may not be a mine available… it’s like counting cards
in a cardgame – it becomes most useful at the end.
Programming
(70%):
We are giving you several skeleton files, though
you will only be adding code to two.
·
DrawingPanel.java – simple interface for
mostly static graphics (hides AWT/Swing details, has changed from hw0 – needed paintImmediately())
·
Scanner.java – a simplification of
the Java 1.5 Scanner (only necessary if using 1.4.2)
·
MineCell.java – defines the legal
values of a single cell.
·
MineBoard.java – The state of the
board and game is stored and modified in this file. (has changed since hw0 – new constructors and
a mine print – add in your methods from hw0)
·
MineStrategy.java – interface for the
local strategy deciders
·
ZerosMineStrategy.java – implements the
classic Minesweeper 0 floodfill
·
CountMineStrategy.java -- makes a simple
counting decision
·
DeriveRuleStrategy.java – analyzes random
samples to discover complex rules
·
GuessWindow.java – windowing code for DeriveRuleStrategy
·
MineUI.java – graphics and UI code. (has changed
since hw0 – access modifiers)
·
SmartMineUI.java – extends MineUI. (Your
automation code goes here.)
·
EndGame.java – class that computes
the endgame analysis (Your endgame code goes here)
Part 1:
Automation (35%) [
unofficial recommended checkpoint: 10/19/05 ]
Method |
Description |
Point
removeNext(LinkedList ll) |
Returns
an item from ll. That item is no
longer present in ll. Must mimic the
behavior of dequeue() or pop(). |
void
explore(Point bp) |
First
explores the location bp (likely a message from the
UI). Then successively explores and
marks neighbors using a sequence of strategies from faster to slower. A location should be tested on a faster
strategy before being tested on a slower strategy,
and slower strategies should not be used until all locations have been tested
on the faster strategies. Invokes the
endgame analysis if appropriate. Stops
if no more can be deduced from the given information. You may define helper methods. You are encouraged to use existing
methods. RECURSION
WILL RECEIVE ZERO CREDIT. |
The method removeNext should
be only a few lines. See the Java API
for details on the methods that refer to the front or back of a linked
list. As written, you will need to do a
cast to Point on the object you get from the list, because get returns an
Object. This code is 1.4.2-compliant. If
you wish, you can rewrite the code to use 1.5 generics. As long as your code works in 1.5, you will
not be graded either way for your choice.
The explore method is more complex, but we’ll
build up to it. Let’s start using only
one strategy, say strategies[0]. First, declare two LinkedLists,
activeList and nextList,
and instantiate them (as empty). Then
call the parent class’s explore on bp. Add the neighbors of bp
to the activeList.
We’re going to have a loop that repeats until activeList
is empty. It removes the next item from
active list, and if that item still can be explored/marked (i.e. is “useful”),
then we see if the strategy can decide anything on it. If so, then we call the parent’s explore or
[the parent’s] mark, as appropriate. We
also add its neighbors to the list, as they now have more information about
themselves, and the list continues on.
Now let’s make it more complicated. The ideas behind the multiple strategies for
explore are: try faster strategies before slower ones. Exhaust all options for the faster strategies
before deferring to the slower ones.
Revert back to the faster strategies again when new information is
obtained.
We want to try the faster ones first. Conveniently, they are sorted that way in the
strategies array. If we have a location
where the current strategy cannot make a decision, that location is added to
the nextList (in the simplified example, it was just
discarded). When the activeList becomes empty, it is swapped with nextList (i.e. the references), and we move onto the next
strategy (all locations have been tried on the faster strategy first). Whenever any neighbors are added to the activeList, the strategy is reset to the fastest. When all strategies have been exhausted, the
endgame is solved if appropriate (isEndGame returns
true), and the method terminates.
You
may define helper methods. You are
encouraged to use existing methods.
RECURSION
WILL RECEIVE ZERO CREDIT.
Part
2: The endgame (35%)
Method |
Description |
Endgame(MineBoard mb, int
numSamples) |
Constructs
the object, creating appropriate arrays and lists. See the code already there. |
analyze(List
toExplore, List toMark) |
After
completion, toExplore will be augmented with locations
that are safe to explore, and toMark will be
augmented with the locations that are mines to be marked. You
will want to define several helper methods; they will closely parallel the
code in DeriveRuleMineStrategy. |
1 |
2 |
1 |
2 |
1 |
2 |
2 |
3 |
2 |
m |
4 |
m |
4 |
m |
4 |
m |
m |
m |
m |
(a) |
(b) |
(c) |
m |
4 |
m |
(d) |
m |
Suppose the endgame configuration is as follows,
and there is one mine left. Then that
mine must go at (b), and everything else is safe. But if there are three mines left, then (a),
(c), and (d) are mines, and (b) is safe.
(If there are two mines, then it’s a tossup between b&d
and a&c.)
The implementation of the endgame differs from DeriveRuleMineStrategy mainly in that we no longer have locale. A mine being at (a) influences a mine at (d)
in the above example, and (d) could have been arbitrarily far away. It will no longer be convenient to use dense
2-d arrays to store information; instead it will need to have lists of
locations (Points) along with their associated data.
There are some useful variables and methods
already present. relevantAsk assumes that (i,j)
is askable and returns true if there is something
unknown neighboring it. numNeighborsTrue counts the number
of trues neighboring (i,j)
– something to compare against ask. The
nested class EndGameChoice wraps a Point with a
count.
The 2-d array mines should have true at (i,j) if the MineBoard
is marked there, and false if (i,j) is askable there. If
neither are true, then (i,j),
wrapped into an EndGameChoice, should be in the
unknowns list. The
asks list should contain all askable locations
that have passed the relevantAsk filter. This
code is 1.4.2-compliant. If you wish,
you can rewrite the code to use 1.5 generics.
As long as your code works in 1.5, you will not be graded either way for
your choice.
Your analyze should first walk
through the unknowns, zeroing the count for each item. Then, for numSamples
times, it records constrained random distributions of (numMines-numMarked)
mines throughout unknowns. To construct
a random distribution, the easiest way is to shuffle the order of unknowns (see
Collections.shuffle) and, for the first (numMines-numMarked) elements, assign true to appropriate
locations in the mines array, and false for the latter elements. Discard all random distributions that do not
satisfy the constraints imposed by ask and numNeighborsTrue. For the remaining distributions, increment
count if there is a mine at that location.
Finally, if count is at a minimum or maximum, add the location to the
appropriate list. YOU MUST USE ITERATORS
TO RECEIVE FULL CREDIT. Once again, the
code should parallel DeriveRuleMineStrategy.
There are three mine files that are included in
the zipfile.
The first and third can be solved to completion from a single click with
the endgame analysis running. The second
cannot be solved to completion from a single click near the center.
Short
Answer Questions (30%):
Put your
answers to the following in a file called questions.txt (you can use Notepad
for editing). You will be graded on
content, not on spelling or grammar.
1) The method addNeighbors
puts items onto the back of a list. If
your removeNext operates on the front of the list,
are you emulating a stack or queue? If
your removeNext operates on the back of the list, are
you emulating a stack or queue?
Run explore with removeNext
operating on the front of the list.
Describe qualitatively the progression of the exploration. Run explore with removeNext
operating on the back of the list.
Describe qualitatively the progression of the exploration. How do they differ? (Adjust SLEEPTIME if necessary.)
2) Look at GuessWindow.java. In terms of n=size, what is the big-Oh of the
runtime of numNeighborsTrue? Of isValidLayout?
3) Why does the mouseClicked
method get called from running SmartMineUI, when it
resides in MineUI?
When mouseClicked calls explore, why does SmartMineUI’s explore get run? How can SmartMineUI’s
explore run its parent’s explore?
Bonus
(5%):
Put in
questions.txt too.
Analyze through experimentation the number of
samples needed for calls to DeriveRuleStrategy.decide. Characterize what inputs require more
samples.
How much does the performance degrade when
windows are increased to 7x7 or 9x9?
(Don’t adjust addNeighbors.)
Turn in
your work electronically from the “assignments” link on the class webpage.