void star games

* play * code * learn *


Flash Tutorials

puce Flash ActionScript Mines Tutorial 4 Back Start Over

Text Box

We're almost there with the game. What we need to do next is two things. First, when we click an empty tile, we need to count the neighbors of that tile that are bombs and display a number on its tile. Second, when a tile is opened that does not have any neighboring bombs, all of its neighbors that are still closed should also be opened.

To be able to display these numbers, we need an object that can display text. We'll add it programmatically to the class Tile. Add an attribute at the top called textBox, and add the module flash.text.TextField to the list of imported modules at the top of the package (before the class definition). Then add the following lines to the Tile constructor, after all the clips have been added to the parent:

textBox = new TextField();
textBox.x = x+9;
textBox.y = y+9;
textBox.text = "4";
parent.addChild(textBox);
textBox.visible = false;

Comment out the line making the textBox invisible for testing. When you run the program, you should see a number 4 displayed on top of all the tiles. You can tweak the 9 added to the x and y of the text box to make it centered over the tile (9 has worked for me). Once you have done that, delete the 4 in the code you added, but leave the pair of quotes in place. This will initialize the text with an empty string, then uncomment the line making the textBox invisible. The textBox needs to be invisible even though the text that it displays is empty, because if it is visible, it interferes with the functionality of the button.

We are ready now to write a function that checks all of the neighbors of a tile and counts the bombs. There are several different ways in which this can be accomplished. Our solution for this is to store two arrays that give us the offset in the two coordinates of each of the 8 neighbors. For example, the neighbors that is up and to the left of the current position will have an offset of -1 on both coordinates.

Add the following function to the class MinesTable:

public function countBombsAround(xTile, yTile) {
  var offx:Array = [-1, 0, 1, 1, 1, 0, -1, -1];
  var offy:Array = [-1, -1, -1, 0, 1, 1, 1, 0];
  var i, count=0, c, r;
  for (i=0; i<8; i++) {
    c = xTile + offx[i];
    r = yTile + offy[i];
    if (0 <= c && c < size && 0 <=r && r < size) {
      if (table[c][r].isBomb) {
        count++;
      }
    }
  }
  return count;
}


Then go back to the Tile class, in the function clickHighlightedTile, and add the following code after setting the state of the tile as empty:

var c = table.countBombsAround(col, row);
textBox.text = c;
textBox.visible = true;


Test the program to see if this works correctly. You should now be able to play a full game yourself by clicking on all the tiles that surround any tile marked 0.

As an easy exercise, make the tile display the number only when it is not 0. If you want to fine-tune your display, you can also set the color of the text based on the number. For example, adding the following code to the lines above, will display the digit 1 in green:

if (c == 1) {
  textBox.textColor = 0x007700;
}


where 007700 is a hexadecimal representation of the color that is identical to the HTML code for colors. I will let you choose the other colors, if you want to do that.

The only thing left to do is to open all the tiles adjacent to the ones where the count of surrounding bombs is 0. The function will be similar to the one above, but it will have to call itself recursively. To avoid too many function calls, we also need to call the function open only those tiles that are still in a closed state.

First, turn the code that sets the text in the textBox based on a number, together with setting the color, into a function called setTextBox taking a parameter called count. Replace "c" in that code with the parameter count. Add an else to the main conditional in this function such that when the count is 0, the textBox is made invisible. This will be useful later when we restart the game.

Add the following function to the class MinesTable:

public function openTile(xTile, yTile) {
  var offx:Array = [-1,0,1,1,1,0,-1,-1];
  var offy:Array = [-1,-1,-1,0,1,1,1,0];
  var i,count = 0,c,r;
  count = countBombsAround(xTile,yTile);
  table[xTile][yTile].setState(emptySt);
  table[xTile][yTile].setTextBox(count);
  if (count == 0) {
    for (i=0; i<8; i++) {
      c = xTile + offx[i];
      r = yTile + offy[i];
      if (0 <= c && c < size && 0 <= r && r < size) {
        if (table[c][r].state == closedSt) {
          openTile(c, r);
        }
      }
    }
  }
}

Replace the entire code that handles opening the highlighted tiles that are not open yet with a simple call to this function

table.openTile(col,row);

Now the game should be fully functional.

Interface Elements

The game as it is, is a little bit bare. Let us add some interface elements to it, so that the player knows what is happening.

The first thing we would like to do is to add some text showing the number of bombs left to flag. First, add some text above the table in the stage with the caption "Bombs Left:". Use the text tool in the toolbar to the left of the stage to create this text, and set its type as Classic Text / Dynamic Text in the Properties area to the top right of the window. Give it the instance name "bombsLeftText". Set the font type, size, and color, the way you want. Make sure that the width of this text box will allow for a 2-digit number to be added to it. Create a similar one on the other side to display messages such as "Game Won" or "Game Lost". Give it the instance name "gameWonText".

Embedding Fonts

Click on one of these two text fields. In the Properties area on the right, you should see a button labeled Embed... next to the font name. Click on it. In the dialog that opens, check all the uppercase and lowercase letters, all the digits, and you can either check all the punctuation, or add in the box below the ones that you think you'll need, like the exclamation mark and the period. Then click ok. Save your project. You need to embed a font to be able to see it in the .swf file.

Now we need to be able to access these instances from the MinesTable class. Add two attributes to this class called bombText and gameText. Add the following function to the class:

public function setTextFields(bt, gt) {
  bombText = bt;
  gameText = gt;
  gameText.text = "";
}

Then go back to the .fla file, option the Actions panel, and after defining the table object t, call this function above the following way:

t.setTextFields(bombsLeftText, gameWonText); Thus, even though we've defined these two objects in the stage, the table will now have access to them and be able to modify the text that they display.

We already have a counter in the class MinesTable for the number of bombs on the table, initialized properly in the constructor. We need to add a counter for the flags, and a counter for the tiles that have been opened. Add an attribute called flagCount and another one called openCount and initialize both of them as 0 in the constructor. Then we need a function that will modify the flag count, updating the displayed text accordingly, keeping in mind that the count could go both up and down, since we allowed the player to remove flags too. Add the following function to the class:

public function addFlag(count) {   flagCount += count;
  bombText.text = "Bombs Left: " + (bombsCount - flagCount);
}


Call this function with the parameter equal to 0 in the function setTextFields at the end. Then call it with a parameter equal to 1 in the function clickHighlightedTile in the class Tile, in the situation where a flag was added, the following way:

table.addFlag(1);

And last, call it again in the function clickFlaggedTile, in the situation where we remove the flag, with the parameter -1.

Let us now find out if the game was won or lost. It is easy to see if the game was lost: in the function fireAll, in the class MinesTable, we can set the text of the gameText attribute to "Game Lost". For detecting a winning condition, first we need to count the tiles that are being open. In the function openTile, after opening the tile, we need to increment the openCount attribute. After that, if the number of open tiles plus the number of remaining bombs are equal to the total size of the table, then the game was won. Add the following code to this function, right after setting the text box of the tile based on the count and its state to empty:

openCount++;
if (openCount + bombsCount == size*size) {
  gameText.text = "Game Won!!"
}

Button

Easy exercise: Add a button to the stage (that you can find under Components in the small area between the Stage and the Properties - you'll have to drag it to the stage) with the label New Game and the instance name "newGameBtn". Add a function in the class table called newGame, with one parameter for the number of bombs on the table, in which you reset all the tiles as closed and without a bomb, and then call the function to generate a random bomb configuration again. You'll have to figure out which attributes that were initialized in the constructor need to be initialized again in this function.

Once you have this function, add the following code in the .fla file, after the code you already have there: newGameBtn.addEventListener(MouseEvent.CLICK, restart);

function restart(event) {
  t.newGame(10);
}

This creates a function in the stage that is linked to the button we just defined, and that calls the new game function from the table.

Challenging exercises:
1.
Set up your game in such a way that the player can choose the level of difficulty. 2. Modify the game such that when you click on a tile that has been opened and has a number on it, if the count of flags in it neighborhood is exactly equal to the counter written on the tile, all the adjacent tiles that have not been flagged are opened.

Back Start Over