This lab is concerned with manipulating 2d-arrays. First set of exercises is ensure you can use loops to navigate and manipulate a 2D array in many different ways. The second exercise leads to next week's submission on MineSweeper.
In each case you need to write a method that performs some calculations on a parameter array. You can put all these methods into any convenient class, say "ArrayTester" or something like that.
The lab exercises are all based on the simplest case, where the base type of the array is primitive. But you should always consider how to extend this to the situation where the base type is a reference type.
If you think of a 2d-array a as a matrix, then the sub-arrays a[0], a[1], etc are the rows of the matrix. Thus row 0 consists of the elements a[0][0], a[0][1], a[0][2], etc. Then column 0 is the elements a[1][0], a[2][0], a[3][0] and so on.
Column 0 | Column 1 | Column 2 | |
Row 0 | a[0][0] | a[0][1] | a[0][2] |
Row 1 | a[1][0] | a[1][1] | a[1][2] |
Row 2 | a[2][0] | a[2][1] | a[2][2] |
Row 3 | a[3][0] | a[3][1] | a[3][2] |
public int max(int[][] a)that returns the maximum value in the 2d parameter array
a
. public int rowSum(int[][] a, int x)that returns the sum of the elements in Row
x
of a
. Write a method
public int columnSum(int[][] a, int x)that returns the sum of the elements in Column
x
of a
(careful with rows of different lengths!). public int[] allRowSums(int[][] a)that calculates the row sum for every row and returns all of the values in an array.
public boolean isRowMagic(int[][] a)that checks if the array is row-magic (this means that every row has the same row sum).
public boolean isColumnMagic(int[][] a)that checks if the array is column-magic (this means that every column has the same column sum).
public boolean isSquare(int[][] a)that checks if the array is square (i.e. every row has the same length as
a
itself). public boolean isMagic(int[][] a)that checks if the array is a magic square. This means that it must be square, and that all row sums, all column sums, and the two diagonal-sums must all be equal.
public boolean isLatin(int[][] a)that checks to see if the array is a Latin square. This means that it must be square (suppose it is n x n), and that each row and each column must contain the values 1, 2, ..., n with no repeats.
public boolean isSudoku(int[][] a)that checks to see if the array is a valid Sudoku grid. This means that the array must be a 9 x 9 Latin square with one extra condition - each of the nine 3 x 3 sub-grids must ALSO contain the entries 1, 2, ..., 9 with no repeats.
Here we will put together an entire class that performs a non-trivial task. Thisis a sort of "mini-project" to get you prepared for the mainproject. The task is to produce an implementation of the gameMineSweeper
.
Note: The MineSweeper
class is expected to take you two weeks to do, and so it is the assessed portion of .
Notice: In order to get marks for this lab, your MineSweeper classmust actually work - in other words, it must correctly countup the number of mines and display it. There is no partial creditfor code that just draws a few squares, and either displays incorrect numbers or no numbers at all. Also important is to get the game logic correct, such as detecting the end of game and so on.
I expect you to use only the SimpleCanvas
class for yourgraphical display. If you wish to do something more complex, using theJava API, then you may do so, but please run it past me first, eitherin person, or by emailing admin1200
.But be mindful, the purpose of this lab is to get familiar with 2D arrays, not on Graphical User Interface (GUI). A proper GUI should be done using javax.Swing.*
, which supports menus, buttons, selection lists and etc.
In this section of the lab, we will create a simple version of the popular game "MineSweeper".
Note: I only require very simple graphics; you donot need to try to add the counters, the smiley face, the menus orthe buttons of the familiar "professional" version. I amprimarily concerned that the underlying logic of the gameis correctly implemented.
In this game, the user is presented with a grid of squares as follows:
Each square is either empty, or contains a mine, but the user does not knowwhich is which. The only information that the user has is the numberof squares, and the total number of mines.
At each stage of the game, the user selects a square and chooses whether to "Dig" or "Mark" at that position. If the user digs, and the square does not contain a mine, then a number is revealed, showing the total number of mines contained in theneighbouring squares (a square's neighbours are the 8 squares adjacent toit either horizontally, vertically or diagonally). In the followingpictures, the number 2 appears, indicating that there are two minesin the neighbouring squares (marked in red in the 2nd picture).
If the user digs, and the square does contain a mine, thenit explodes and the user has lost the game. The board then indicates thelocation of all the hidden mines (the square in red is the one wherethe user foolishly dug!):
As the game progresses, the user may be able to deduce thelocation of some of the mines by using the information containedin the revealed numbers. For example, in the following situation, there must be two mines in the squares indicated:
If the user thinks that they have deduced the location of a mine, then theymay mark the square with a little flag. Notice that the square becomes marked whether or not itactually contains a mine.
The user wins the game if they successfully dig all of thesquares that do not contain mines (whether or not they have markedthem).
In the professional version of this game, the squares are selected by mouse clicks (left for "dig" and right for "mark") and thereare a variety of other visual effects. However, we can implement afully functional version, although less slick, as follows:
We will create a class called MineSweeper
, with eachobject of this class representing (and managing) a single game.It will use a SimpleCanvas
to draw on toindicate the progress of the game.
The first constructor for the class MineSweeper
should allow theuser to specify the number of rows, the number of columns, and the number of mines to be hidden. The signature of this should be
public MineSweeper(int numCols, int numRows, int numMines)
The first task of the constructor is to construct the minefield - thiswill require a 2d-array. You will use the entries of this array totrack the status of each square. There are many ways to do this, with one simple way being to use a simple code to represent all thepossibilities, such as the following:
You can also use Java 5's enumeration - read the magic number section.
Initially, every square is untouched, and so all that is needed is tofill the array with 0s, and then randomly create the correct numberof mines.
The second task of the constructor is to create a SimpleCanvas
and draw the minefield on it - for the time being, just be content todraw a grid of grey squares, and do not attempt to draw the smiley faceor the timer etc.
The second constructor is vitally important for the marking processand so it must be correct. Its signature is
public MineSweeper(boolean[][] mineField)This allows the client to specify the initial minefield, via a boolean array - the
(i,j)
entry of this is true
if the cell contains a mine.This constructor will be used by my marking program to set up a known initial minefield, so that I can play a game automaticallyand simply watch the correct numbers come up. If you do not supplythis constructor, then it is almost impossible to mark your program.
At each stage, the user has only two options - either to dig a squareor to mark a square, and so we only need two public methods. We donot yet know how to make a SimpleCanvas
respond to mouse clicks,so we will make do with using co-ordinates to specify the squares.
public void dig(int i, int j)
This method indicates that the user wishes to dig thesquare in position (i,j)
(where (0,0)
isthe top-left square and the co-ordinates use the standard Java graphicsconvention that the first co-ordinate is left-to-right and the second top-to-bottom.
The object should respond according to the status of the square(i,j)
:
At this stage, you should also check to see whether the user hasactually won the game - if so, then maybe print a congratulatory message on the screen. You may find it convenientto use an instance variable to keep track of the number ofsquares remaining to be dug, and when this number reaches zero, theuser has won the game.
public void mark(int i, int j)
This method indicates that the user wishes to mark thesquare in position (i,j)
.
Do not bother adding any methods to clear the screen, or start a new game - the client can simply create a new MineSweeper
if they want to play a new game.
Here is a small that will ensurethat your basic functionality (only dig is being tested) is working.Please make sure that your program gets identical results.
On constructing an MSTester
object, your screen should look something like this:
After calling the method test1()
, which will do a large numberof dig
commands with a small pause between each one, yourscreen should look roughly as follows:
Then test2()
will dig the final un-mined square and thereforeit should reveal the mines and display a congratulatory message.
Remove all magic numbers from your code, and replace them withsuitable constants. This means removing any literals thatrefer to parts of the program that may be changed.
For example, if you have decided that each of the squares in thegrid should be 40 x 40 pixels, then you might have code such as
drawFilledRectangle(x,y,40,40,java.awt.Color.gray);
This code uses a magic number - 40 - and a "magic colour" java.awt.Color.gray
. You should replace these with symbolic constants as follows: create class variables to representthe square size, and the initial colour
private static int SQUARE_SIZE = 40;private static java.awt.Color SQUARE_COLOUR = java.awt.Color.gray;and replace all occurrences of the magic numbers with theirsymbolic equivalents.
drawFilledRectangle(x,y,SQUARE_SIZE,SQUARE_SIZE,SQUARE_COLOUR);
Clearly, all the "status" values should be replaced by symbolic class variables:
private static int CLEAR = 0;private static int MINED = 1;... etc
As from Java 5, there is a facility called enumerations that permits the use of special types that can only take on a specific fixed range of values, so that errors like this would be prevented by the compiler.
In Java 5, you might define
public enum Status {CLEAR, MINED, CORRECT_MARK, BAD_MARK, DUG};to indicate the
Status
is a type (ie a class) that can only take on the five values given. If you have method signature to report status private Status equire(int x, int y)
and the compiler would only allow the values CLEAR
, MINED
, CORRECT_MARK
BAD_MARK
and DUG
to be returned. The professional version of this game has some additional nicetouches to make playing it slick and convenient. If you have thetime and interest, then consider implementing these improvements.
Add a counter that starts off at numMines
and goes downby one every time the user marks a square. This allows the user to quickly check how many mines they have remaining - provided theydon't incorrectly mark a square that does not actually contain amine.
If a square is dug, and it has no mines in the neighbouringsquares, then according to the above rules, you would write a 0 intothat square. However, if a user sees a "0 square" then theyimmediately know that they can safely dig all 8 of its neighbourswithout risk of a mine exploding. Therefore, rather than force theuser to call dig
when it is guaranteed to be safe, theauto-reveal feature will automatically dig those safe squares, andreveal the corresponding numbers. (This process may reveal more0-squares, in which case their neighbours are automaticallydug, and so on.)
When the game starts, the user has no information about the locationof the mines and must therefore choose a square at random. It wouldbe frustrating for the user if that very first square contained a mine,because then the game would end almost before it has begun. Mostversions do not permit this to happen, so that wherever theuser first clicks is always free of mines. This could bedone by delaying the "mine-laying" until immediately afterthe first click, or just shifting a mine if the user is unlucky enough to hit it on the first go.