CE881 Lab 2: WhiteOut!

Introduction

In this lab we’ll work through implementing a simple but fun puzzle app.  The lab has the following aims:

I played this game when I was a child. In the original version, it was a building with several windows with lights on or off. The night is coming, we need to turn off the lights. That's why I call it the WhiteOut puzzle.

The rules of the puzzle are simple:

An example game state displayed of the App is shown below.

WhiteOut

WhiteOut

Design of Model

The model must be able to represent the state of each switch.  Since each switch is either on or off, the model state could be represented by a 2d array of boolean (i.e. boolean[][] grid).

You can also use a 2d array of int (i.e. int[][] grid). One simple reason for this: flipping the state of a switch is just as easy, but now it’s possible to change the appearence of each square by using the switch state as an index into an array that controls the drawing style.

In my implementation I chose instead to use a 1d array of int (i.e. int[] bits).

The class diagram for the model I implemented is given below.  We already mentioned the grid field.  There’s also a variable called neighbourTable.  It is a lookup table to find the 4 neighbours (top, bottom, left, right) of a given switch.  The int variable dim conveniently stores the size of the dim x dim grid.

WhiteOutModel Class Diagram

WhiteOutModel Class Diagram. Attention: you may not need to implement all the methods listed in this figure.

Model Implementation

Now work through implementing the model.  Create a new Android project by following a similar process as last week.  Now is also a good time to start up an emulator!

The constructor takes the parameter n as an argument and creates the grid ("int[] bits" in my code), which together with the aforementioned instance variables gives us:

Instance vars and Constructor

Instance vars and Constructor

The flip method takes the i,j location of the switch within the grid and tries to flip it.  Note the use of the try catch block.  This provides the easiest way to prevent illegal indexes causing the app to stop with an error.

trFlip method

flip method

Testing the model will be important, and it’s a good idea to do this from plain Java rather than to always have to test within an Android app.  For this purpose we have the following toString() method.  This will allow us to check the state of the model after flipping a switch:

toString method

toString method. Attention: you may need to use Arrays.toString() method if you use a 2d array.

You may need a isSolve() to check if the puzzle is solved:

isSolved method

isSolved method.

Exercise One

Now please finish your implementation of model

Write a new class to test this and check the operation of the model.  You can set the grid to be in various states.  This is a simple example, which is sufficient for this lab:

ModelTest

ModelTest

Run this and check that the console output is what you expect.

WhiteOutView

Having confirmed that the model works, it is now time to build the view class.  I called this WhiteOutView.

The first part of the implementation declares what it extends and implements, and also the required static and instance variables:

WhiteOutView Declarations

WhiteOutView Declarations. Attention: You may not need all the attributes listed in the figure !

The gridSize variable is used to store the size of each grid cell.  This is calculated dynamically every time the grid is drawn, in order to be robust to any changes in size of the WhiteOutView component.

The other supporting variables and setGeometry method to calculate them are here:

setGeometry

setGeometry

We’ll draw the view in several steps: create a Paint object and vary only the colour for part we need to draw.  Then we’ll draw the view background and the grid background (the grid is a square on the possibly rectangular overall WhiteOutView.

Then we simply iterate over the set of grid cells, setting the corresponding light color depending on the state of the grid there.  Note: I have accessed the grid array directly from the WhiteOutView class: this is questionable practice (why?), and you may prefer to define a getSwitchState method in the model class instead.

onDraw method

onDraw method

drawTile

drawTile

The event handling methods are as follows:

When you have the system up and running experiment with returning true from the onTouch method, and confirm that this breaks the onClick event.

Event Handling

Event Handling

There are some remaining things that need to be done.  When creating the View class the IDE may have prompted you to create constrcutors. These are the constructors I have.  Note that one that takes a LightsModel as an argument. 

WhiteOutView Constructors

WhiteOutView Constructors

Since much of the setup (at least for now) is common to all constructors I have defined a setup method to take care of all this.  This also calls a checkModel method.  The rationale behind this is to ensure that something still works even if the model was not setup as expected.  This is a bit sloppy and is just a quick fix: you should also investigate under what circumatances each constructor gets called, and how to ensure in all cases that the model is set up correctly (e.g. has the correct value of n).

setup and checkModel methods

setup and checkModel methods

Finally, we need to ensure that the main activity class invokes the view.  The view could be setup in XML but for now I’ve done it directly in Java: 

MainActivity class

MainActivity class with two buttons.

Exercise Two

You should now have a working WhiteOut puzzle, but there is no indication of when the puzzle is solved, and no facility to reset the puzzle.

By reading up on the LinearLayout class and experimenting with XML layouts, create a new version of the puzzle app with a status message (e.g. using a TextView) and a reset button (I suggest using a Button).

Then try to hook up these view components to the main activity and the WhiteOutView in order for everything to work in a sensible fashion: you can decide on the details of exactly how it works. 

Don’t be too concerned if you don’t get all this working within this lab: we’ll study more about how to hook up components to work with each other next week.  Here are a couple of tips to get you started.

You can edit the XML to make button clicks trigger a method (by default a method in the main activity.  For example, add the following Button declaration within a LinearLayout:

Button onClick

Button onClick

 

Then you need to rewrite the main activity class to change the onCreate() method and to add the resetModel() method.  The onCreate method has now been changed to use the XML layout.

Note the use of findViewById in the resetModel method, and the fact that we call postInvalidate on the WhiteOutView in order to refresh the view.

onCreate and resetModel

onCreate and resetModel

Exercise Three

Implement a method to let the player choose the size of grid.

Hint: A button needs to be added. You will also need a dialog.