Creating Minesweeper in Flash

Tutorial parts

Source: Game.as

package Minesweeper
{
    import Minesweeper.Cell;
    import flash.display.MovieClip;
    import flash.display.SimpleButton;
    import flash.display.Stage;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.events.MouseEvent;
    import flash.events.TimerEvent;
    import flash.events.KeyboardEvent;
    import flash.events.Event;
    import flash.utils.Timer;
    import flash.ui.Keyboard;

    public class Game extends MovieClip
    {
        private static const LargeText:TextFormat = new TextFormat();
        LargeText.color = 0x333333;
        LargeText.size = 16;
        LargeText.font = "Arial";

        public static var STAGE:Stage;
        public static var Playing:Boolean;
        private static var Rows:int;
        private static var Columns:int;
        private static var Mines:int;
        public static var MinesRemaining:int;
        private static var Seconds:int;
        public static var Grid:Array;
        private static var Clock:Timer;
        public static var FlagMode:Boolean;
        private static var GameBoard:MovieClip;
        private static var TimerField:TextField;
        public static var MinesField:TextField;
        public static var WinNotice:MovieClip;

        public function Game()
        {
            STAGE = this.stage;
            loaderInfo.addEventListener(Event.COMPLETE, Initialise);
        }

        public function Initialise(e:Event):void
        {
            // Set up our button handlers
            this.EasyButton.addEventListener(MouseEvent.MOUSE_UP, StartEasy);
            this.MediumButton.addEventListener(MouseEvent.MOUSE_UP, StartMedium);
            this.HardButton.addEventListener(MouseEvent.MOUSE_UP, StartHard);

            // Set up our clock
            Clock = new Timer(1000);
            Clock.addEventListener(TimerEvent.TIMER, Tick);

            // Set up our game board which holds all of the cells
            GameBoard = new MovieClip();
            GameBoard.buttonMode = true;
            GameBoard.useHandCursor = true;
            this.addChild(GameBoard);

            // Set up our timer and mine display fields
            TimerField = new TextField();
            TimerField.x = 15;
            TimerField.y = 354;
            TimerField.defaultTextFormat = LargeText;
            TimerField.selectable=  false;
            this.addChild(TimerField);

            MinesField = new TextField();
            MinesField.x = 151;
            MinesField.y = 354;
            MinesField.defaultTextFormat = LargeText;
            MinesField.selectable = false;
            this.addChild(MinesField);

            // Set up our win notice
            WinNotice = new Minesweeper.WinNotice();
            WinNotice.x = 0;
            WinNotice.y = 56;
            WinNotice.visible = false;
            this.addChild(WinNotice);

            // Set up our key handler for placing flags
            STAGE.addEventListener(KeyboardEvent.KEY_DOWN, FlagModeOn);
            STAGE.addEventListener(KeyboardEvent.KEY_UP, FlagModeOff);
            StartHard(null);
        }

        private static function StartEasy(e:MouseEvent):void
        {
            Rows = 9;
            Columns = 9;
            Mines = 10;
            StartGame();
        }

        private static function StartMedium(e:MouseEvent):void
        {
            Rows = 16;
            Columns = 16;
            Mines = 40;
            StartGame();
        }

        private static function StartHard(e:MouseEvent):void
        {
            Rows = 30;
            Columns = 16;
            Mines = 99;
            StartGame();
        }

        private static function StartGame():void
        {
            // Reset the game data
            Playing = true;
            MinesRemaining = Mines;
            Seconds = 0;
            FlagMode = false;
            TimerField.text = "0:00";
            MinesField.text = MinesRemaining + " mines";
            WinNotice.visible = false;

            // Reset the game grid
            while(GameBoard.numChildren > 0)
            GameBoard.removeChildAt(0);

            GameBoard.x = (600 - (Rows * 19)) / 2;
            GameBoard.y = (400 - (Columns * 19)) / 2;

            // Set up the mines
            Grid = new Array(Rows);
            var cell:Cell;
            var x:int;
            var y:int;

            for(x=0; x<Rows; x++)
            {
                Grid[x] = new Array(Columns);

                for(y=0; y<Columns; y++)
                {
                    cell = new Cell(x, y);
                    cell.x = x * 19;
                    cell.y = y * 19;
                    Grid[x][y] = cell;
                    GameBoard.addChild(cell);
                }
            }

            // Place the mines
            var adjacentcells:Array;
            var i:int;
            var ax:int;
            var ay:int;
            var acell:Cell;

            while(Mines > 0)
            {
                // Choose a random cell to put the mine in
                x = Math.round(Math.random() * Rows - 1);
                y = Math.round(Math.random() * Columns - 1);

                // Make sure it's a valid cell
                if(!InRange(x, y))
                continue;

                cell = Grid[x][y];

                if(cell.Mined)
                continue;

                // We've placed a mine
                Mines--;
                cell.Mined = true;
                cell.Adjacent = 0;

                // Determine the cells adjacent to our new mine
                adjacentcells = Cell.AdjacentCells(x, y);

                if(adjacentcells.length == 0)
                continue;

                // Increase their adjacent count on all valid cells
                for(i=0; i<adjacentcells.length; i++)
                {
                    ax = adjacentcells[i].X;
                    ay = adjacentcells[i].Y;
                    acell = Grid[ax][ay];

                    if(!acell.Mined)
                    {
                        acell.Adjacent += 1;
                    }
                }
            }

            // Start the clock
            Clock.start();
        }

        // Key handlers for turning flag mode on/off
        private static function FlagModeOn(e:KeyboardEvent):void
        {
            if(e.keyCode == Keyboard.SPACE)
            {
                FlagMode = true;
            }
        }

        private static function FlagModeOff(e:KeyboardEvent):void
        {
            if(e.keyCode == Keyboard.SPACE)
            {
                FlagMode = false;
            }
        }

        // Our clock tick
        private static function Tick(e:TimerEvent):void
        {
            Seconds++;

            var minutes:int = Math.floor(Seconds / 60);
            var seconds:int = Seconds % 60;

            TimerField.text = minutes + ":" + (seconds > 9 ? seconds : "0" + seconds);
        }

        public static function CheckWin():void
        {
            var cell:Cell;

            for(var x:int=0; x<Rows; x++)
            {
                for(var y:int=0; y<Columns; y++)
                {
                    cell = Grid[x][y];

                    // If a cell has a mine but isn't flagged then we can't have won
                    if(cell.Mined == true && !cell.Flagged)
                    return;
                }
            }

            Playing = false;
            Clock.stop();
            WinNotice.visible = true;
        }

        public static function Lose():void
        {
            var cell:Cell;

            for(var x:int=0; x<Rows; x++)
            {
                for(var y:int=0; y<Columns; y++)
                {
                    cell = Grid[x][y];
                    Cell.FinalReveal(cell);
                }
            }

            Playing = false;
            Clock.stop();
        }

        // Removes every blank adjacent cell when we click a blank
        public static function ClearEmptySpaces(x:int, y:int):void
        {
            var adjacentcells:Array = Cell.AdjacentCells(x, y);

            if(adjacentcells.length == 0)
            return;

            var cell:Cell;
            var ax:int;
            var ay:int;

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

                if(cell.Mined || cell.Flagged)
                continue;

                if(cell.Adjacent == 0)
                {
                    Cell.Blank(cell);
                    ClearEmptySpaces(ax, ay);
                }
                else
                {
                    Cell.Reveal(cell);
                }
            }
        }

        // Checks if a cell coordinate is valid
        public static function InRange(x:int, y:int):Boolean
        {
            return (x > -1 && y > -1 && x < Rows && y < Columns && Grid[x][y] != null && Grid[x][y].Ignore == false);
        }
    }
}

Tutorial parts