Creating Minesweeper in Flash

Tutorial parts

Part 3: Programming the cells

Bookmark and Share

Set up your file

This file controls an actual cell in the game. A cell consists of a movieclip that shows each 'state' of a cell, and a handful of methods and properties that control its behaviour.

In your Cell.as the first thing we need to do is set it up so that it is in the right package and class.

package Minesweeper
{
    import flash.display.MovieClip;

    public class Cell extends MovieClip
    {
    }
}

Properties

The text colors are going to be shared by all cells. These are the colors when you have 1 - 8 adjacent mines, the number is blue, red, whatever.

private static const Colors:Array = new Array(9);
Colors[1] = 0x0004FF;
Colors[2] = 0x007000;
Colors[3] = 0xFE0100;
Colors[4] = 0x05006C;
Colors[5] = 0x840800;
Colors[6] = 0x008284;
Colors[7] = 0x840084;
Colors[8] = 0x000000;

Our cell-specific data

Every cell has its own copy of this data. As you can see it holds information regarding the state of the mine.

private var X:int;
private var Y:int;
public var Mined:Boolean;
public var Flagged:Boolean;
private var Questioned:Boolean;
public var Ignore:Boolean;
public var Adjacent:int;

Our methods

The cell behaviour in Minesweeper is very simple so we only have a handful of methods. These methods are mostly static which is more efficient when you have a lot of instances - 480 on Hard - that need to do the same things.

The constructor

This is the code that is run when we create an instance of a Cell. In Minesweeper.Game.Initialise we call this constructur to generate our cells.

public function Cell(x:int, y:int)
{
    this.Mined = false;
    this.Flagged = false;
    this.Questioned = false;
    this.Ignore = false;
    this.X = x;
    this.Y = y;
    this.Adjacent = 0;

    this.gotoAndStop(1);
    this.buttonMode = true;
    this.useHandCursor = true;
    this.mouseChildren = false
    this.addEventListener(MouseEvent.MOUSE_UP, Click);
}

Clicking on a cell

The click method is long because there are a variety of possible outcomes when you click on a cell - reveal it, flag it, question mark it, unflag/unquestion mark it.... is it blank? Does it have adjacent mines? Are any of the adjacent cells empty? Even though it is long you should be able to follow it, it's not complicated we just need to work through each scenario.

private static function Click(e:MouseEvent):void
{
    if(!Game.Playing)
    return;

    var cell:Cell = e.target as Cell;

    // When we're in flag mode
    if(Game.FlagMode == true)
    {
        // Make questioned if it's flagged
        if(cell.Flagged)
        {
            cell.gotoAndStop(6);
            cell.Questioned = true;
            cell.Flagged = false;
            Game.MinesRemaining++;
            Game.MinesField.text = Game.MinesRemaining + (Game.MinesRemaining != 1 ? " mines" : " mine");

        }

        // Make normal if it's questioned
        else if(cell.Questioned)
        {
            cell.gotoAndStop(1);
            cell.Flagged = false;
            cell.Questioned = false;
        }

        // Make flagged if it's normal and there's available mines
        else if(Game.MinesRemaining > 0)
        {
            Game.MinesRemaining--;
            Game.MinesField.text = Game.MinesRemaining + (Game.MinesRemaining != 1 ? " mines" : " mine");
            cell.gotoAndStop(4);
            cell.Flagged = true;
            cell.Questioned = false;
        }

        return;
    }

    // When the cell is blank
    if(cell.Ignore)
    {
        if(cell.AdjacentField.text != "")
        {
            var adjacentcells:Array = AdjacentCells(cell.X, cell.Y);

            if(adjacentcells.length == 0)
            return;

            var adjacentminesfound:int = 0;
            var acell:Cell;
            var ax:int;
            var ay:int;

            for(var i:int=0; i<adjacentcells.length; i++)
            {
                ax = adjacentcells[i].X;
                ay = adjacentcells[i].Y;
                acell = Game.Grid[ax][ay];

                if(acell.Mined && acell.Flagged)
                {
                    adjacentminesfound++;
                }
            }

            if(adjacentminesfound == cell.Adjacent)
            {
                Game.ClearEmptySpaces(cell.X, cell.Y);
            }
        }

        return;
    }

    // Otherwise if we've flagged or questioned the cell we leave
    if(cell.Flagged || cell.Questioned)
    return;

    // If there's a mine and it's not flagged we lose
    if(cell.Mined && cell.Flagged == false)
    {
        cell.gotoAndStop(5);
        Game.Lose();
        return;
    }

    // Clear the mine, and possibly any adjacent cells
    if(cell.Adjacent == 0)
    {
        cell.gotoAndStop(2);
        Game.ClearEmptySpaces(cell.X, cell.Y);
    }
    else
    {
        Reveal(cell);
    }

    // Check to see if we won the game
    Game.CheckWin();
}

Revealing cells and setting them blank

These 3 small methods handle some of the basic behaviour of cells - revealing and blanking them. A cell is Reveal'd by mouse clicking, and FinalReveal'd by the game itself if you lose. Cells are Blank'd when the grid is generated.

public static function Blank(cell:Cell):void
{
    cell.Ignore = true;
    cell.gotoAndStop(2);
}

public static function Reveal(cell:Cell):void
{
    cell.Ignore = true;
    cell.gotoAndStop(3);
    cell.AdjacentField.text = String(cell.Adjacent);
    cell.AdjacentField.textColor = Colors[cell.Adjacent];
}

public static function FinalReveal(cell:Cell):void
{
    if(cell.Mined == false)
    {
        cell.gotoAndStop(2);

        if(cell.Adjacent > 0)
        {
            cell.AdjacentField.text = String(cell.Adjacent);
            cell.AdjacentField.textColor = Colors[cell.Adjacent];
        }
    }
    else if(cell.Flagged == false)
    {
        cell.gotoAndStop(5);
    }
}

Adjacent cells

At several points during the game we want to know what cells are adjacent to another. We don't want to know it enough to justify saving that information for every cell, instead we use this method to retrieve it when we want it. If we were to save it on a Hard game it would be up to 3,840 more pieces of data to hold in memory!

The coordinates we check are probably going to look like rubbish at first, but you will see in this image they do make sense. The center of the image is the x and y parameters passed to the method.

Map of adjacent cells Mapping adjacent cells
public static function AdjacentCells(x:int, y:int):Array
{
    var adjacentcells:Array = new Array();

    if(Game.InRange(x - 1, y - 1))
    adjacentcells.push(new Coordintes(y - 1, x - 1));

    if(Game.InRange(x, y - 1))
    adjacentcells.push((new Coordintes(y - 1, x));

    if(Game.InRange(x + 1, y - 1))
    adjacentcells.push((new Coordintes(y - 1, x + 1));

    if(Game.InRange(x - 1, y))
    adjacentcells.push((new Coordintes(y , x - 1));

    if(Game.InRange(x + 1, y))
    adjacentcells.push((new Coordintes(y, x + 1));

    if(Game.InRange(x - 1, y + 1))
    adjacentcells.push((new Coordintes(y + 1, x - 1));

    if(Game.InRange(x, y + 1))
    adjacentcells.push((new Coordintes(y + 1, x));

    if(Game.InRange(x + 1, y + 1))
    adjacentcells.push((new Coordintes(y + 1, x + 1));

    return adjacentcells;
}

The AdjacentCells method uses a very primitive class created just for it. It needs to go in "Coordinates.as" in the same folder as Game.as and Cell.as, and it simply consists of:

package Minesweeper
{
    public class Coordinates
    {
    public var X:int;
    public var Y:int;

    public function Coordinates(y:int, x:int)
    {
        this.X = x;
        this.Y = y;
    }
    }
}

That's it, we're done!

Tutorial parts