Scroll to top

OOP can make development faster, and your applications run faster. During this tutorial I'll demonstrate how to build an ActionScript Connect 4 game, using an organized OOP approach.

Let's get started!

Step 1: Start Off

In the source files I've included a connect4_start.fla file. Start from this file; it contains all the movieclips needed to finish the tutorial. It contains 4 movieclips. A red chip movieclip, a yellow chip movieclip, a boardpiece movieclip and a winner dialog movieclip.

Step 2: Create the Connect4 Class File

Create a new ActionScript file and save it with the name Connect4. Then add the following code:

1
 
2
package
3
{
4
	import flash.display.Sprite
5
	import flash.events.MouseEvent;
6
	import flash.events.Event;
7
	import flash.geom.Point;
8
	import caurina.transitions.Tweener;
9
	
10
	public class Connect4 extends Sprite
11
	{
12
		private var columns:uint;
13
		private var rows:uint;
14
		private var board:Array = new Array();
15
		private var columnWidth:uint;
16
		private var rowHeight:uint;
17
		private var currentPlayer:uint = 1;
18
		private var currentChip;
19
		
20
		public function Connect4(columns:uint,rows:uint):void
21
		{
22
			this.columns = columns
23
			this.rows = rows
24
			
25
			columnWidth = new BoardPiece().width
26
			rowHeight = new BoardPiece().height
27
		}
28
	}
29
}

In this code block we import the classes (I also used the Tweener class which you can find here or in my source files). We define the field variables and we create the constructor. The constructor has two arguments, the number of columns and rows you want for your game. We put these values in our field variables.

Finally we init the columnWidth and rowHeight variable. One column has the same width as the width of one boardPiece (same principle for rowHeight).

Step 3: drawBoard Function

This function will draw the playboard of the game depending on how many columns and rows you want. Add this function to the constructor of the Connect4 class:

1
2
private function drawboard():void
3
{
4
	for(var i:uint = 0; i < rows; i++)
5
	{
6
		for(var j:uint = 0; j < columns; j++)
7
		{
8
			var boardpiece:BoardPiece = new BoardPiece();
9
			boardpiece.x = j * boardpiece.width;
10
			boardpiece.y = i * boardpiece.height;
11
			this.addChild(boardpiece);
12
		}
13
	}
14
}

Step 4: createBoardArray Function

Here we create a 2-dimensional array which will contain all the locations of the board.

We fill the array with 0's. A zero means there hasn't been a chip placed on that position. This function also needs to be added to the constructor.

1
2
private function createboardArray():void
3
{
4
	for(var i:uint = 0; i < rows; i++)
5
	{
6
		board[i] = []
7
		for(var j:uint=0; j < columns; j++)
8
		{
9
			board[i][j] = 0;
10
		}
11
	}
12
}

Step 5: putChipReady Function

In this function we identify who the current player is and add the matching chip to the display list. We give the chip a negative y value so it shows up above our playboard. Once again, add this function to the constructor.

1
2
private function putChipReady():void
3
{
4
	if(currentPlayer == 1)
5
	{
6
		currentChip = new RedChip();
7
	}
8
	else
9
	{
10
		currentChip = new YellowChip();
11
	}
12
	currentChip.y = -50;
13
	this.addChildAt(currentChip,0);
14
}

Step 6: enterFrameHander

On every frame this function calculates the column where the current chip should fall if you click at that moment. That way it's easier for the user to make a move. The function calculateColumn(explained in the next step) returns the column you are hovering.

Then we calculate the new x position of the chip. Therefore we multiply the currentColumn with the column width. Because the registration point of the chips in centered we have to add the column width divided by 2.

Finally we tween the current chip to the postion we just calculated.

1
2
private function enterFrameHandler(e:Event):void
3
{
4
	var currentcolumn:uint = calculateColumn(this.mouseX);
5
	var xPosChip:uint = currentcolumn * columnWidth + columnWidth/2
6
	Tweener.addTween(currentChip, {x:xPosChip, time:0.3, transition:"lineair"});
7
}

Add the enter frame Event Listener to the constructor.

1
2
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);

Step 7: calculateColumn Function

This helpfunction gets the x position of the mouse and returns whichever column matches.

If your mouse x position is less then zero then we return the first column(0 because array is zero-indexed). If your mouse x position is more then the width of your board we return the last column. If your mouse x position is on the board we devide the x postion by the width of one column.

1
2
private function calculateColumn(mouseXPos):uint
3
{
4
	if(mouseXPos < 0)
5
	{
6
		return 0;
7
	}
8
	else if(mouseXPos > this.width)
9
	{
10
		return columns - 1;
11
	}
12
	else
13
	{
14
		return mouseXPos/columnWidth;
15
	}
16
}

Step 8: boardClick Function

This function first checks which column you clicked (by using the calculate column function which I explained in the previous step).

The for loops through all the rows and as soon as board[row][columnclicked] == 0 we know the chip has to be placed on that location. Remember 0 in the BOARD array means the location is empty.

If we find the location where the chip has to fall, we fill that postion in the board array with the number of the current player (we need these numbers later on to check for the winner) and we place that chip with the placeChip function (explained in the next step).

Finally we toggle the player (explained 2 steps further) and we put another chip ready. The return makes sure we exit the for loop.

1
2
private function boardClick(e:MouseEvent):void
3
{
4
	var columnclicked:uint = calculateColumn(e.currentTarget.mouseX);
5
	
6
	for(var row:int=rows-1; row>=0; row--)
7
	{
8
		
9
		if(board[row][columnclicked]==0)
10
		{
11
			board[row][columnclicked] = currentPlayer;
12
			placeChip(new Point(row,columnclicked))
13
			togglePlayer();
14
			putChipReady();
15
			return
16
		}
17
	}		
18
}

Add the click Event Listener to the constructor.

1
2
this.addEventListener(MouseEvent.CLICK, boardClick)

Step 9: placeChip Function

This function gets the row(position.x) and column(position.y) where the chip has to enter, then calculates the y- and x distance.

distanceY: you multiply the row you clicked with the height of one row. Because the registration point of the chips is centered we have to add the row height divided by 2.

distanceX uses the same principle.

Then we use tweener to tween the current chip to the right position.

1
2
private function placeChip(position:Point):void
3
{
4
	var distanceY:int = position.x * rowHeight + rowHeight/2;
5
	var distanceX:int = position.y * columnWidth + columnWidth/2;
6
	Tweener.addTween(currentChip, {x: distanceX, y:distanceY, time:0.7, transition:"easeOutBounce"});
7
}

Step 10: togglePlayer Function

This function is pretty straight-forward. If the current player is 1 switch to player 2, else switch back to player 1.

1
2
private function togglePlayer():void
3
{
4
	if(currentPlayer == 1)
5
	{
6
		currentPlayer = 2
7
	}
8
	else
9
	{
10
		currentPlayer = 1
11
	}
12
}

At this point you can already place the chips, but there's currently no check to see if a player managed to connect 4 chips. The next step is to code this check.

Step 11: checkForWinner Function

This function has 1 argument, the position of the last placed chip and return true or false. The function uses 4 subfunctions each of which checks for a winner.

We pass through the position for each subfunction. If one of the 4 returns true we have a winner.

1
2
private function checkForWinner(position:Point):Boolean
3
{
4
	if(verticalCheck(position))return true;
5
	if(horizontalCheck(position))return true;
6
  	if(leftUpDiagonalCheck(position))return true;
7
    if(rightUpDiagonalCheck(position))return true;
8
    return false;
9
}

Step 12: verticalCheck

To check for a vertical connect 4 we only have to look at the chips beneath the current chip. (There can't be a chip above the current chip at this point).

First we check if there are 3 locations beneath the current chip. If not, there's no way you can connect 4 and we return false.

If there are 3 or more locations beneath, we start a loop which goes through the 3 rows beneath. If one of the chips beneath is from the other player we did not connect 4 chips (return false).
If the loop can be finished we know there are 4 connected (return true).

1
2
function verticalCheck(position:Point):Boolean
3
{
4
	var row:uint = position.x;
5
	var column:uint = position.y;
6
	var player:uint = board[row][column];
7
8
	if (row >= rows - 3)
9
	{
10
		return false;
11
	}
12
	
13
	for (var i:uint = row+1; i <= row + 3; i++)
14
	{
15
		if (board[i][column] != player)
16
		{
17
			return false;
18
		}
19
	}
20
	return true;
21
}

Step 13: horizontalCheck

What we do here is first check the chips on the left of the current chip and then the chips on the right side.

Therefore we init a counter with 1 (the chip you just placed is the first one in the row). Then we go to the left until we reach a chip of an other player and meanwhile we count the chips of the current player.

We do the same for the right side. So if our counter is 4 or more we have connected 4 chips (return true).

1
2
function horizontalCheck(position:Point):Boolean
3
{
4
	var row:uint = position.x;
5
	var column:uint = position.y;
6
	var player:uint = board[row][column];
7
	var counter:uint = 1;
8
			
9
	for(var i:uint = column-1; i>=0; i--)
10
	{
11
		if(board[row][i] != player)
12
		{
13
			break;
14
		}
15
		counter++;
16
	}
17
			
18
	for(var j:uint = column+1; j<columns; j++)
19
	{
20
		if(board[row][j] != player)
21
		{
22
			break;
23
		}
24
		counter++;
25
	}
26
			
27
	if(counter >=4)
28
	{
29
		return true;
30
	}
31
	else
32
	{
33
		return false;
34
	}
35
}

Step 14: leftUpDiagonalCheck

This functions is similar to the horizontal check. The only difference is that we now check diagonally.

First we go left upwards: we count the chips of the current player and if we come across a chip of the other player we break the loop. To make sure we don't go outside our board array the while loop has to stop when our row or our column is less then 0.

We use the same principle to check the positions right downwards.

1
2
function leftUpDiagonalCheck(position:Point):Boolean
3
{
4
	var player:uint = board[position.x][position.y];
5
		
6
	var row:Number = position.x - 1;
7
	var column:Number = position.y - 1;
8
		
9
	var counter:uint = 1;
10
11
	while (row >= 0 && column >= 0)
12
	{
13
		if (board[row][column] == player)
14
		{
15
			counter++;
16
			row--;
17
			column--;
18
		} 
19
		else
20
		{
21
			break;            
22
		}
23
	}
24
		
25
	row = position.x + 1;
26
	column = position.y + 1;
27
		
28
	while (row < rows && column < columns)
29
	{            
30
		if (board[row][column] == player)
31
		{
32
			counter++;
33
			row++;
34
			column++;
35
		} 
36
		else
37
		{
38
			 break;
39
		}
40
	}
41
	if(counter >=4)
42
	{
43
		return true;
44
	}
45
	else
46
	{
47
		return false;
48
	}
49
}

Step 15: rightUpDiagonalCheck

This function is almost identical to the previous function. The only difference is the direction of the diagonal we check.

1
2
private function rightUpDiagonalCheck(position:Point):Boolean
3
{
4
	var player:uint = board[position.x][position.y];
5
	
6
	var row:Number = position.x + 1;
7
	var column:Number = position.y - 1;
8
		
9
	var counter:uint = 1;
10
		
11
	while (row < rows && column >= 0)
12
	{
13
		if (board[row][column] == player)
14
		{
15
			counter++;
16
			row++;
17
			column--;
18
		} 
19
		else
20
		{
21
			break;            
22
		}
23
	}
24
		
25
	row = position.x - 1;
26
	column = position.y + 1;
27
28
	while (row >= 0 && column < columns)
29
	{            
30
		if (board[row][column] == player)
31
		{
32
			counter++;
33
			row--;
34
			column++;
35
		} 
36
		else
37
		{
38
			 break;
39
		}
40
	}
41
	if(counter >=4)
42
	{
43
		return true;
44
	}
45
	else
46
	{
47
		return false;
48
	}
49
}

Step 16: Updating the boardClick Function

Now we have to implement the code we just wrote. After we place the chip we check for a winner. If we have a winner we remove our EventListeners so you can't place a new chip and we show who's won (explained in the next step). If we don't have a winner we togglePlayer and put a new chip ready.

1
2
private function boardClick(e:MouseEvent):void
3
{
4
	var columnclicked:uint = calculateColumn(e.currentTarget.mouseX);
5
			
6
	for(var row:int=rows-1; row>=0; row--)
7
	{
8
				
9
		if(board[row][columnclicked]==0)
10
		{
11
			board[row][columnclicked] = currentPlayer;
12
			placeChip(new Point(row,columnclicked))
13
			if(checkForWinner(new Point(row,columnclicked)))
14
			{
15
				this.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
16
				this.removeEventListener(MouseEvent.CLICK, boardClick);
17
				showWinnerDialog();
18
			}
19
			else
20
			{
21
				togglePlayer();
22
				putChipReady();
23
			}
24
			return
25
		}
26
	}	
27
}

Step 17: showWinnerDialog Function

This function simply adds a new instance of the WinnerDialog which you can find in the library. If the current player is 1 red wins, else yellow wins.

1
2
private function showWinnerDialog():void
3
{
4
	var dialog:WinnerDialog = new WinnerDialog();
5
	var winner:String = (currentPlayer == 1)? "red" : "yellow"
6
	dialog.txtWinner.text = winner + " wins!!!";
7
	dialog.x = (this.width - dialog.width)/2;
8
	dialog.y = 100;
9
	this.addChild(dialog);
10
}

Step 18: Create the Document Class

Create a new ActionScript file and save it with the name "Application".

In this class we add an instance of the Connect4 Class we wrote to the display list. Don't forget the constructor arguments.

1
2
package
3
{
4
	import flash.display.MovieClip;
5
	import flash.events.Event;
6
	
7
	public class Application extends MovieClip
8
	{
9
		public function Application():void
10
		{
11
			var connect4:Connect4 = new Connect4(7,6);
12
			connect4.x = (stage.stageWidth - connect4.width)/2;
13
			connect4.y = (stage.stageHeight - connect4.height)/2 + 50;
14
			this.addChild(connect4);
15
		}
16
	}
17
}

Finally, click on the stage and set the Document Class to "Application".

Conclusion

You just learned how to create a connect 4 game and how multiple dimensions can make life a lot easier! I hope you enjoyed the tutorial and thank you for reading.

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.