void star games

* play * code * learn *


Flash Tutorials

puce Flash ActionScript Mines Tutorial 3 Back Next

Container Class

To complete this game, we need another class that will keep track and manipulate the entire mine field. This class will keep reference to all of the tiles so that when we click on one, we can count the bombs hidden in the neighborhood and display it. Create another ActionScript 3.0 class called MineTable. Add the following import statements right after the "package {" line:

import Tile;
import Math;

Add an attribute called table, another one called size, and one called parent. Copy the attributes from the class Tile listing all the states here, otherwise we'll need to access them every time through an object of the class Tile, which can be inconvenient. Now let's write the initialization function for the table, or constructor. The table itself will have to be a 2 dimensional array. We'll use the built-in class Array for this. Since this provides 1 dimensional arrays, we'll need to create an array of arrays.

public function MineTable(theStage, dim:int) {
  var i, j;
  size = dim;
  parent = theStage;
  table = new Array(size);
  for (i=0; i<size; i++) {
    table[i] = new Array(size);
    for (j=0; j<size; j++) {
      table[i][j] = new Tile(parent, i, j, this);
    }
  }
}

Go back to the fla file and replace the entire code of the two loops creating the tiles with the following line:

var t = new MineTable(this.stage, 9);

Test the program. If everything is fine, you should see the same configuration as before.

Let's add some bombs to the table. First, declare another class attribute called bombsCount. Then add the following function:

public function setBombs(count) {
  var i, j, u;
  bombsCount = count;
  var bombsLeft = count, totalTiles = size*size;
  for (i=0; i<size; i++) {
    for (j=0; j<size; j++) {
      u = Math.random() * totalTiles;
      if (u < bombsLeft) {
        table[i][j].setBomb();
        table[i][j].setState(bombSt);
        bombsLeft --;
      }
      totalTiles--;
    }
  }
}

The random function generates a random number between 0 and 1. By multiplying it by the tilesCount, we obtain one between 0 and the number of tiles left to examine. bombsLeft indicates how many bombs we still have left to place on the table. So, for each position on the table, we generate a random number between 0 and the number tiles left, and if that number happens to be between 0 and the number of bombs left to place, we make that tile a bomb. At the same tile we decrease the number of bombs left to place, since we've placed one, and the number of tiles left to examine. This procedure gives the tile a chance to be a bomb equal to the number of bombs we need divided by the numbers of available tiles, in a hope to distribute them evenly in the table. This is based on a known algorithm that was proved to give every tile in the table the same probability to be turned into a bomb, while ensuring that we will place the exact count we need of bombs on the table.

What this function is not doing, though, is avoiding those situations where a bomb might be surrounded on all sides by other bombs, which normally is not a situation you should allow in the game. Even though it is rare, some modifications to the function will have to be done to account for that. I will leave it up to the reader to think of a solution for this.

In the function above, for testing purposes, I have set the state of all the tiles that are turned into bombs as 4, which will show the bomb clip. After testing it a few times, when you are satisfied that it works properly, you can comment out that part by placing two forward slash characters (//) at the beginning of the line. You can also delete the line instead, but it might be useful to have it there for easy access in case you want it for debugging again later.

Events

Now it's time to start making these tiles clickable. For that we need to attach a function that should be called in case of an event, or a callback function, to objects in the scene that we want to react to a mouse click. The event here is the mouse click itself. In ActionScript we can attach the callback function directly to objects like sprites and movie clips, such that when the click happens, the object that the mouse pointer was over at that time will react by calling the appropriate function. This way we don't need to worry ourselves about comparing the mouse position with the position and dimensions of objects on the screen and decide which object the event applies to. In other APIs, this is something that might have to be done by hand.

Let's think for a moment at what kind of interaction we want from our tiles. The only tiles that need to be clickable are the closed tiles. If they have been opened before or flagged, nothing should happen if we accidentally click on them. The way we've set up the tile symbols in the project, when we click on a closed tile, its image should be replaced in a first stage by the highlighted one. Only when we release the mouse button, the action should take place. We already have a function that can make this change for us, which is the function setState with a parameter of 1. We just need to link this function to the event. First, let's create a function that will be called when the closed tile is clicked. In the Tile class, add the following function before the end of the class definition:

public function clickClosedTile(ev:MouseEvent) {
  setState(highlightedSt);
}


and the go back to the constructor of the class Tile and add the following call at the end of it:

closedClip.addEventListener(MouseEvent.MOUSE_DOWN, clickClosedTile);

If you save the file and run the program again, when you click on the tiles, they should light up.

Game Functionality

Next, what needs to happen is that when the mouse button is released, the functionality of the game should be activated. We will define another callback function that we'll attach to the highlighted tile clip, with the event type being MOUSE_UP instead of MOUSE_DOWN. Then we need to figure out what should happen when we activate the tile. There are several cases to consider.

First, if the tile is a bomb, then we should fire it, and the game should be lost. For this, we should go through all of the tiles, and those that are still closed should be opened, either showing an empty space if they were empty, or showing the bomb image if they contained a bomb. A closed tile would normally be in a state of 0, but we could include the state of 1 to apply the same procedure to the tile that was highlighted. Here is a function to accomplish this, that should be added to the class MinesTable:

public function fireAll() {
  var i, j;
  for (i=0; i<size; i++) {
   for (j=0; j<size; j++) {
      if (table[i][j].state <= 1) {
        if (table[i][j].isBomb) {
         table[i][j].setState(explodedSt);
        }
        else {
         table[i][j].setState(emptySt);
        }
      }
    }
  }
}

Let's test this function right away. In the class Tile, add the following function:

public function clickHighlightedTile(ev:MouseEvent) {
  table.fireAll();
}


and then in the constructor for the Tile class, add the following line at the end:

highlightedClip.addEventListener(MouseEvent.MOUSE_UP, clickHighlightedTile);

Test the program and click on a tile. All the bombs should be shown, and all the other tiles should disappear.

Let's make the bombs only appear when we click on a bomb. Replace the function call to fireAll in the function clickHighlightedTile with the following conditional testing if the tile is a bomb:

  if (isBomb) {
    table.fireAll();
  }
  else {
    setState(emptySt);
  }


This way, if we click on an empty space, it just opens up. Otherwise, the bombs are fired. As an easy exercise, modify the function fireAll such that if a tile is flagged and it is a bomb, the bomb clip is displayed instead of the flagged or the exploded one. This way, if the player loses the game, they will still find out how many bombs they have guessed.

Before we take care of the lengthier details related to clicking on an empty tile, we could deal right now with the flags. Here it depends on what we want to do. We could add the flag to a closed tile either by holding down the shift key.

The function clickHighlightedTile takes one parameter that is the event. This parameter event is a complex structure containing a variety of data. One attribute that we can use here is the shiftKey that is true if we were holding the shift key down while clicking the mouse, and false if not. So let's add a test at the beginning of this function:

if (ev.shiftKey) {

and if this is true, then call the function setState with the parameter value equal to flaggedSt. Add an else before the next if and make sure the braces close appropriately.

But now that we've added the flags, we realize that we should be able to remove them. We want to remove them only if we click on those tiles again with the shift key held (we want to avoid accidentally setting up a flagged bomb). Add another function called clickFlaggedTile, check if the make it call the function setState with the parameter closedSt if the shift key is held, then link it to the flagged clip with the event MOUSE_UP in the constructor for the Tile. Test the program to see if it works.

Back Next