21 December 2009
Beginning
Note: This article was created basedon Flash Builder 4 beta. Minor changes in the description and code maybe necessary before it can be applied to Flash Builder 4.
Due to the ubiquity of Adobe Flash Player, the Adobe Flash Platform is the most popular global platform for building games. Adobe Flash is also increasingly being used in the enterprise community to develop RIAs. As a result, developers with a wide range of skill sets and expertise are developing for the Flash Platform.
In this article, I'll highlight concepts many RIA developers may already be familiar with (the Flex and Cairngorm frameworks) and describe how they can be used to build a simple four in a row game.
This is the beginning of a three part series. In this first part, you'll learn how to:
In Part 2, you'll update the game to add the game logic. You'll extend the game's features to detect when a player has won (or determine that there are no possible winning moves remaining). In Part 3, you'll take the game one step further by adding some simple artificial intelligence (AI) that allows a single user to play the game with the computer.
The sample project you'll build in this article is a simple four-in-a-row style game. The game board is comprised of rows and columns. Players take alternating turns by dropping a chip of their color (in this example, red or black) in a column. The chip lands in the lowest empty position in the column, stacking on top of previously played chips, if applicable. Only one player's chip can be contained in any one cell, and once a move is made the chip cannot be moved. The first player to line up four of their colored chips, aligned in a row, column or diagonal, is the winner.
In this first section, you'll begin by creating a new project. Then you'll import board images from a SWF file and draw the game board on the Stage.
Follow these steps to begin building the new game project:
If you're not familiar with the Cairngorm framework, it follows MVC (Model-View-Controller) design patterns common in many RIAs. You can use the same approach to develop games like the one described in this article. The Model keeps track of the game state (whose turn it is and what moves have been made). In part two of this series, the Model is also responsible for determining when the game is over (when a player wins or when there are no more winning moves to be made). The View handles display of the game board, and the Controller processes game events (when a player starts a game or makes a move).
Start building the MVC structure by creating the following empty folders under the new project:
The game board for a four-in-a-row style game is comprised of a two dimensional grid. The traditional version of the game contains 6 rows and 7 columns. This version is more customizable to make things a little more interesting—you'll create an interface to allow the user to choose the number of rows and columns, as well as choosing the number of chips that must match in a row to win.
The board model tracks the moves that have been played so far on the board. A simple array is used to keep an internal map of where each player's chips are positioned on the board. The index of the array makes it possible to reference each position on the board by starting at the bottom left corner of the game board, then increasing from left to right to fill out the entire row before moving up to the next row.
You can use the following function to locate any position in the game board array by its row and column number. The array index will store the number of the player who has a chip in that position.
index = (row * NUM_COLUMNS) + column
(This formula assumes your rows and columns start at 0.)
The following chart illustrates how each space on the board is indexed (see Figure 1):

Adding a Board class
Follow these steps to add the code to create the game board model:

public static const EMPTY:int = 0; // An empty position
public static const INVALID_MOVE:int = -1; // An invalid move
private var rows:uint; // Number of columns on the board
private var cols:uint; // Number of rows on the board
private var players:uint; // Number of players
private var connect:uint; // How many in a row to win
private var boardMap:Array; // Array of all the pieces that have been played
public function getCols():uint {
return cols; }
public function getRows():uint {
return rows; }
public function getPlayers():uint {
return players; }
public function getConnect():uint {
return connect; }
Next, you'll add the getMapIndex function. This function accepts a row and column number as the input and returns the position in the boardMap array that points to the location.
Copy or paste this code to add the getMapIndex function:
// Passed a row and column,
returns the index in the boardMap array
private function getMapIndex(r:uint, c:uint):uint {
return (r * cols) + c;
}
If you've used the Cairngormframework before, this step should be familiar to you. If not, don'tworry. Just follow along. A ModelLocator in the Cairngorm framework isused as a global singleton repository to hold shared or global objects.You can think of the ModelLocator as a central place to store globalvariables and objects. In this game, the ModelLocator is the locationfor all the other components of the game; you'll use it to access thecentral instantiation of the board model. Follow these steps:
Name the new ActionScript class ModelLocator under the model folder. It will implement the com.adobe.cairngorm.model.IModelLocator interface (see Figure 3).

Type or paste the following code into the class file:
package model {
import com.adobe.cairngorm.model.IModelLocator;
[Bindable]
public class ModelLocator implements IModelLocator {
public var board:Board = new Board(); // board model
public var playerTurn:uint = 1; // Which player's turn is it
//**********************************************************
// Singleton methods
//**********************************************************
/**
* This returns and instance of the custom ModelLocator.
* @return ModelLocator
*/
public static function getInstance() : ModelLocator {
if ( __modelLocator == null ) { __modelLocator = new ModelLocator(new ConstructorBlock); }
return __modelLocator;
}
/**
* Default constructor.
* @param block Private class required.
* Prevents public, direct instantiation of multiple instances
*/
public function ModelLocator(block:ConstructorBlock) {
if ( __modelLocator != null ) {
throw new Error( "Only one ModelLocator instance should be instantiated" );
}
__modelLocator = this;
}
// **********************************************************
// Singleton attribute
// **********************************************************
private static var __modelLocator:com.adobe.games.fourinarow.model.ModelLocator = null;
}
}
// Declared outside package so it can be used
// to prevent direct construction of ModelLocator instances
class ConstructorBlock { }
Next, you'll edit the FourInARow.mxml file (from the sample files folder) to instantiate the ModelLocator you just created.
<mx:Script>
<![CDATA[
import com.adobe.games.fourinarow.model.ModelLocator;
private var __model:ModelLocator = ModelLocator.getInstance();
]]>
</mx:Script>
While it is possible to create a game just using the board model, its not much fun to play unless you can actually see what is happening at each turn. In this section, you'll create a canvas that will draw the game board.
To give the board some visual depth, the board itself will be drawn on two separate layers (front and back). A third layer in between those two will hold the chips that are played during the game. This approach simulates the appearance of the chips sitting inside the board. It also makes the animation look more convincing when a chip falls down into place. The following figure shows how the layers line up and how an individual cell on the board looks when a player's chip is held inside (see Figure 4).

The next task is to start creating the view. Follow these steps:
Create a new MXML Component named boardCanvas under the view folder, based on mx.containers.Canvas (see Figure 5).

Add the following code to create the three layers you'll need to draw the board:
<mx:Canvas id="backCanvas" x="0" y="50"
width="100%" height="100%" horizontalScrollPolicy="off"
verticalScrollPolicy="off" />
<mx:Canvas id="chipCanvas" x="0" y="50"
width="100%" height="100%"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
/>
<mx:Canvas id="frontCanvas" x="0" y="50"
width="100%" height="100%" horizontalScrollPolicy="off"
verticalScrollPolicy="off" />
Since the board is drawn dynamically, some programming is needed to draw out the elements. To create a simple board like the one required for a four in a row game, you could import bitmap images or even use the built-in drawing functions. However, for the purposes of this article, I'll demonstrate the process of importing elements from a SWF file. I've found this is a common workflow, because the design team often uses Flash CS4 to create the graphic assets for games.
Locate the file named board.swf in the sample files folder. Place a copy of the file in your src/assets directory.
Note: The board.fla file that was used to create board.swf is also provided in the sample files folder for your reference, but you don't need it to follow along with these instructions.
To make a symbol in a Flash CS4 file accessible to your Flash Builder project, use the Library to access the symbol properties for the movie clip and make sure the option to Export for ActionScript is selected. You'll use the Class name you enter in the Symbol Properties dialog box (blackChip) to reference the symbol in Flash CS4 (see Figure 6).

Temporarily minimize Flash CS4 and return to Flash Builder. Then follow these steps:
Create an <mx:Script> block at the top of your boardCanvas.mxml file, and then add the following code to pull in the classes from the SWF file:
[Embed(source="assets/board.swf",
symbol="BoardElementBack")]
[Bindable]
public var BoardElementBack:Class;
[Embed(source="assets/board.swf",
symbol="BoardElementFront")]
[Bindable]
public var BoardElementFront:Class;
[Embed(source="assets/board.swf",
symbol="redChip")]
[Bindable]
public var redChip:Class;
[Embed(source="assets/board.swf",
symbol="blackChip")]
[Bindable]
public var blackChip:Class;
Review the code above and notice that the symbol name in the Embed() function here exactly matches the name of the Class exported in the Flash CS4 file. To use the element in MXML, you'll create a new Image object and set its src value to the Class variable you created previously.
Each cell in our game board is symmetrical and uses the same width and height.
Near the top of the <mx:Script> block, define the width and height values, like this:
private static const
CELL_WIDTH:int = 80;
private static const
CELL_HEIGHT:int = 80;
Next, you'll add the code to connect to the ModelLocator in order to access the board model.
Use the helpful code completion functions available in Flash Builder to ensure that the ModelLocator class gets imported:
private var __model:ModelLocator = ModelLocator.getInstance();
Create the drawBoard() function that handles sizing the Canvas and laying out the game board view. Add the following code:
public function drawBoard():void {
// Clear the board canvas
chipCanvas.removeAllChildren();
backCanvas.removeAllChildren();
frontCanvas.removeAllChildren();
// Set the size of our main canvas
this.width = 30 + (CELL_WIDTH * __model.board.getCols());
this.height = 80 + (CELL_HEIGHT * __model.board.getRows());
for (var r:int=__model.board.getRows()-1; r >= 0; r--) {
// Loop through each row (bottom up to handle overlap)
for (var c:int=__model.board.getCols()-1; c >= 0; c--) {
// Loop through each col (right to left to handle overlap)
// Create Back of cell
var b:Image = new Image;
b.source = BoardElementBack;
// Create Front of cell
var f:Image = new Image;
f.source = BoardElementFront;
b.x = f.x = 15 + (CELL_WIDTH * c);
b.y = f.y = 15 + (CELL_HEIGHT * r);
backCanvas.addChild(b);
frontCanvas.addChild(f);
}
}
}
Double-check that the mx.controls.Image class is imported at the top of your <mx:Script> block:
import mx.controls.Image;
The process for drawing the board involves looping through each column for each row, to create the cell that holds a single player chip. Images for the front and back of the board are created and added to the appropriate Canvas layer. The virtual sides of the board are drawn as part of the front cell. To make sure everything overlaps properly, start by adding the objects to the right side of the board and moving left (to ensure that each cell is always layered on top of the one on its right).
There are only a few things left in order to set up the view and get a graphic to appear on the Stage. In this game, the user gets to choose the number of rows and columns, and these are displayed dynamically. Later in this article series I'll provide the instructions to create the control panel used to set those values, but for now you need a way to set the initial default values. The constructor of the board model is a fine place to set those values for testing purposes.
Open the file Board.as and add the following code to your Board constructor:
public function Board() {
rows = 6;
cols = 7;
players = 2;
connect = 4;
}
At this point, you can add the boardCanvas MXML object and a call to the drawBoard() function in the creationComplete event of your application. Update your FourInARow.mxml file to match the code example shown below:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
minWidth="1024" minHeight="768"
xmlns:view="view.*"
creationComplete="init();">
<mx:Script>
<![CDATA[
private function init():void {
board_C.drawBoard();
}
]]>
</mx:Script>
<view:boardCanvas id="board_C" x="25" />
</mx:Application>
After making these changes, you should see an empty game board displayed (see Figure 7).

Continue on to the next section of this article, where you'll add the code to begin allowing each player to take a turn when playing the game.
In the previous section of this article, you learned how to build the game board and display it on the screen. In this section, you'll add the functionality to process each move in the game when a player takes their turn. In the previous instructions, you created a model and a view. The next part involves creating a controller that handles the processing of game events, such as tracking the data when each player makes a move.
Before you start building the controller, you'll create a CairngormEvent class to represent a player move event. The class will define the data that will be sent when the event occurs (for example, passing which player completed the move and which column they selected). Follow these steps:
Create a new ActionScript Class and name it PlayerMoveEvent. Save it under the control/events folder. The Superclass it inherits from is: com.adobe.cairngorm.control.CairngormEvent.
It is common practice to define an EVENT_ID string to reference your event. This is the unique name for each of your Cairngorm events that makes it easy to tell which event was fired, in order to handle processing for it. It's also common to define variables that hold the player number and the column played.
Open the PlayerMoveEvent.as file that exists at this location: control/events/PlayerMoveEvent.as. Update the ActionScript to match the code example shown below:
package control.events {
import com.adobe.cairngorm.control.CairngormEvent;
public class PlayerMoveEvent extends CairngormEvent {
public static const EVENT_ID:String = 'playerMove';
public var player:uint;
public var column:uint;
public function PlayerMoveEvent() {
super(EVENT_ID);
}
}
}
Next, you'll create a function in the view of the boardCanvas that will take as its input the number of the column that a player selects. Players take their turn by clicking on the board (the view), so it makes sense for the PlayerMoveEvent to be dispatched from the view.
private function onPlayerSelectColumn(column:uint):void {
if (column < __model.board.getCols()) {
var event:PlayerMoveEvent = new PlayerMoveEvent();
event.player = __model.playerTurn;
event.column = column;
event.dispatch();
}
}
import control.events.PlayerMoveEvent;
The game play takes place as follows: Players take their turn by clicking on the game board to indicate the column where they would like to place their chip. The chip will fall to the next available space, closest to the bottom). To reduce confusion, it's best to set up the code to ignore clicks that are too close to the edge of a column. Players must click near the center of the column they choose to place the chip. Follow these steps:
Add the following function to handle processing mouse click events:
private function onMouseClick(e:MouseEvent):void {
// Figure out which column (if any) we've clicked on
var pos:int = e.stageX - this.x;
var col:int;
pos -= 15; // board offset
if (pos > 0) {
col = pos / 80;
if ((15 < pos%CELL_WIDTH) && (pos%CELL_WIDTH < (CELL_WIDTH-15))) {
trace(‘[boardCanvas] onMouseClick – col: ‘ + col);
onPlayerSelectColumn(col);
}
}
}
private function init():void {
addEventListener(MouseEvent.CLICK, onMouseClick);
}
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="400" height="300"
creationComplete="init();"
>
If everything is working as expected, you'll see the Debug console display a message every time you click on a column, to indicate which column was clicked. Spend some time testing the game, by clicking all over the board. As each message appears, verify that the number of the clicked column is correct. Also try clicking outside or on the edge of the columns to see what happens. When you click between two columns, the Debug console should not display a message, because the click is ignored. If your project is not functioning this way, check the code examples provided above or compare your version with the code in the provided sample files to make sure they match.
To complete this part of the project, there's one more function to add to the boardCanvas.mxml file; this function will display a player's chip on the board. To accomplish this, you first need to find an easy way to determine which color chip (red or black) is to be displayed. Since the player number is an integer (either 1 or 2), the player number can be used as an index for an ArrayCollection that contains the chip images for the players. Follow these steps:
Add an ArrayCollection variable near the top of the <mx:Script> block, and be sure to import the mx.collections.ArrayCollection class, like this:
public var playerChips:ArrayCollection = new ArrayCollection();
// Set the chip colors for each player
playerChips.addItem(null);
playerChips.addItemAt(redChip, 1);
playerChips.addItemAt(blackChip, 2);
public function dropChip(p:uint, c:uint, r:uint):void {
var newChip:Image = new Image;
newChip.source = playerChips[p];
newChip.x = 15 + 30 + (CELL_WIDTH * c);
newChip.y = (CELL_HEIGHT * (__model.board.getRows() - r)) - 30;
chipCanvas.addChild(newChip);
}
board_C.dropChip(1, 3, 0);
board_C.dropChip(2, 4, 0);
board_C.dropChip(1, 4, 1);
The placeholder code above calls the dropChip() function, as if the first three moves of a game have been played, and places three chips on the board.

Important: Don't forget to delete those temporary calls you added in Step 3 before continuing on to the next part of this article.
At this stage in the project, you've learned how to place chips on the view of the board, but the view does not contain data about the current state of the game—the status is controlled by the board model. If you click on the board in the view, you trigger an event that tells you which column was selected, but unless you check the board model data (to determine the current game's state) it is impossible to tell how far down the column (which row position) the chip dropped. Follow these steps to pass the data and complete the move (if it is a valid selection; the selected column could also be full).
Add the following code that takes the value of a player and a column as an argument and determines the lowest empty row position on the board to display the player's chip:
public function playerMove(player:uint, c:uint):int {
for (var r:int=0; r < rows; r++) {
var pos:uint = getMapIndex(r, c);
if (boardMap[pos] == EMPTY) {
// Found empty space
// Mark player has placed a chip in this space
boardMap[pos] = player;
// Return the row that was played
return r;
}
}
return INVALID_MOVE; // No valid moves
}
The function above loops through each row on the board, starting with the bottom row, and searches for the first EMPTY position in the boardMap[] array.
If an empty position is found, the function updates boardMap[] with the player number that made the move and returns the row that was played.
If the function loops through to the end, it means that all rows in that column already have chips in them and there are no empty spaces. This means that the player made an invalid move by selecting that column, and a INVALID_MOVE (-1) value is returned.
Update the code again. Since it is necessary to begin tracking actual moves in the model, initialize the boardMap[] array setting of each position to EMPTY, like this:
public function clearBoard():void
{
for (var i:int=0; i < (rows * cols); i++) {
boardMap[i] = EMPTY;
}
}
In the next section of this article series, you'll add the ability to reset the game and you'll call this function to clear the board model.
For now, just initialize a new array and add a call to clearBoard() to the constructor, as shown below:
public function Board() {
rows = 6;
cols = 7;
players = 2;
connect = 4;
boardMap = new Array;
clearBoard();
}
The controller must update both the board model and the view every time a player takes their turn. Although you could lookup the Stage object and try and hunt for the view, that's not always the ideal solution; you'd have to keep updating your code if you change where the boardCanvas is displayed. In a previous section, you learned that the ModelLocator is already being used to access to global board model, so it makes sense to also add a pointer to the view here. Follow these steps:
Update the Board.as code, to match the following code example:
import view.boardCanvas;
public class ModelLocator
implements IModelLocator {
public var board:Board = new Board();
public var boardView:boardCanvas;
public var playerTurn:uint = 0;
Update the init() function, to connect the boardCanvas to the ModelLocator:
<mx:Script>
<![CDATA[
import com.adobe.games.fourinarow.model.ModelLocator;
private var __model:ModelLocator = ModelLocator.getInstance();
private function init():void {
__model.boardView = board_C;
board_C.drawBoard();
}
]]>
</mx:Script>
At this point, the game dispatches a playerMoveEvent every time a player makes a move. The view responds and displays a chip on the board to represent the player's move. The model tracks the moves that have been played, to determine valid moves. In this section, you'll hook up the game controller to connect all of the pieces and create a game that you can begin playing.
In the Cairngorm framework, a FrontController is used as the central place to process all CairngormEvents. In this game, the FrontController object is called the GameController. The GameController listens for all CairngormEvents that are broadcast. Whenever it receives a broadcast, it launches the designated command class to process it. At the moment, the game only has one event type to process, but as you continue to extend the game and add more functionality, more events and commands will be added. Follow these steps to create the GameController:
Add the following code to create the registerAllCommands function. This function adds a listener for the PlayerMoveEvent and registers the PlayerCommand class as the responder for those events. Your GameController.as file should look like this:
package control {
import com.adobe.cairngorm.control.FrontController;
import control.commands.*;
import control.events.*;
public class GameController extends FrontController {
public function GameController() {
super();
registerAllCommands();
}
private function registerAllCommands():void {
// Register Player Moves
addCommand(PlayerMoveEvent.EVENT_ID, PlayerCommand);
}
}
}

Unlike the previous classes you created that extend a Superclass, the PlayerCommand needs to have the following two Interfaces implemented:
com.adobe.cairngorm.commands.ICommand
mx.rpc.IResponder
In every ICommand class that you implement, you'll need to add an execute() function. This function is called to respond to the event, and is passed a CairngormEvent object. An ICommand can respond to multiple event types, so inspect the event.type to determine the exact event that was passed. Cast the event for the specific type you are handling so you can access event-specific variables, such as player and column.
package control.commands {
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;
import control.events.PlayerMoveEvent;
import model.Board;
import model.ModelLocator;
import mx.controls.Alert;
import mx.rpc.IResponder;
public class PlayerCommand implements ICommand, IResponder {
private var __model:ModelLocator = ModelLocator.getInstance();
public function PlayerCommand() {
}
public function execute(event:CairngormEvent):void {
switch (event.type) {
case PlayerMoveEvent.EVENT_ID:
onPlayerMoveEvent(event as PlayerMoveEvent);
break;
default:
break;
}
}
private function onPlayerMoveEvent(e:PlayerMoveEvent):void {
var moveRow:int = __model.board.playerMove(e.player, e.column);
if (moveRow == Board.INVALID_MOVE) {
Alert.show(‘Sorry, that move is not valid.', 'Invalid Move', Alert.OK);
} else {
// Tell the view to place a chip
__model.boardView.dropChip(e.player, e.column, moveRow);
// Player turn over, increment/loop
__model.playerTurn++;
if (__model.playerTurn > __model.board.getPlayers())
__model.playerTurn = 1;
}
}
public function result(data:Object):void { }
public function fault(info:Object):void { }
}
}
The onPlayerMoveEvent function controls what happens when a player makes their move. First, it updates the model to verify that the move that was made is valid; it also determines which row was played. If the move is valid, the player, column and row position data is passed to place a chip in the correct location on the board. The dropChip() function is called to update the view. Finally, the playerTurn variable is incremented to allow the next player to take their turn.
To make everything work, you just need to load the GameController. Follow these steps:
Add the following line of code inside the <mx:Application> tag:
<control:GameController xmlns:control="control.*" />
The line above is the last piece necessary to hook up the GameController code into your main application.
As you continue to click, you'll see the chip colors alternate with each player's turn. If you fill up a column with chips and then select it again, an alert box appears to display the message that the move is not valid.
In this article, you've learned how you can leverage familiar concepts from an RIA framework (Model-View-Controller) and use them to build a game.
The four in a row game is far from complete at this point. Continue following along with part two of this article series (coming soon), where you'll use the files you've developed up to this point and create a working game with an interface. You'll learn how to add a control panel, sound and animation; you'll also incorporate detection logic to indicate when the game is over. In part three of this series, you'll finish this project by adding logic to allow the user to play the game and compete against the computer.
To learn more about developing with the Cairngorm framework, see the following online resources:

This work is licensed under a Creative Commons Attribution-Noncommercial 3.0 Unported License.