Flash Tutorial Links:
Play Games: HTML5 Tutorials:

How To Make A Tic Tac Toe Game

Most games end in a draw ... so it's often considered very embarrassing if you lose.

What is TicTacToe?

Also known as noughts and crosses, this is a game for two. Players take turns putting their own marks (either a O or a X) onto a 3x3 grid. The first person who gets 3 of his own marks in a row (straight or diagonally) wins!

Download the Game Files

The files you need to create this game can be downloaded from here.

Part 1

import flash.display.*;
import flash.events.*;
import flash.ui.*;

public class TicTacToe extends MovieClip
{
    private var score1, score2, delay:Number;
    private var phase, currWinner:String;
    private var gotoWin, gotoLose:Boolean;
    
    public function TicTacToe()
    {
        
    }

The 3 standard packages are imported. You will almost always need display and events. As for ui, it'll be useful to import it in too.

The following variables will be used for the game. score1 keeps track of the score for player 1, and score2 keeps track of the score for player 2.

delay is a variable which we'll use later on to pause the game for a little while.

phase denotes the different states of the game. A value of "player1" indicates that it is currently the turn for player 1 to place his mark. A value of "player2" indicates that it is player 2's turn.

currWinner is used to remember who the winner is per game. This will be used later when displaying it out at the end of each game.

gotoWin and gotoLose are standard boolean variables you'll see throughout the tutorials. Once they are set to true, the game jumps to the respective Win or Lose frames.

Part 2

    //All Start Functions
    public function startMenu()
    {
        stop();
        btnStartGame.addEventListener(MouseEvent.CLICK, gotoStartGame);
        btnHowToPlay.addEventListener(MouseEvent.CLICK, gotoHowToPlay);
    }
    
    public function startHowToPlay()
    {
        btnBack.addEventListener(MouseEvent.CLICK, gotoMenu);
    }
    
    public function startWin()
    {
        btnBack.addEventListener(MouseEvent.CLICK, gotoMenu);
        if (score1 > score2)
            txtStatus.text = "Player 1 has won!";
        else
            txtStatus.text = "Player 2 has won!";
    }
    
    public function startLose()
    {
        btnBack.addEventListener(MouseEvent.CLICK, gotoMenu);
    }

Take a look at the ActionScript embedded in the different frames in the FLA file. You will see that in the menu labelled frame, it has a single function call to startMenu. Likewise, the win labelled frame will call startWin.

All the functions here handle those calls. We often refrain from writing codes in the FLA file so that everything can be seen in the AS file, and it helps tremendously in debugging your codes.

Basically, the function calls here only set up the codes for the buttons in that frame to make them function.

Since this is a 2 player game, the frame labelled win will display the winner of the game. In the startWin function, both scores are checked, and the winner will be displayed on the text field named txtStatus.

Part 3

    public function startGame()
    {			
        score1 = 0;
        score2 = 0;
        delay = -1;
        
        gotoWin = false;
        gotoLose = false;
        
        phase = "player1";
        currWinner = "";

        for (var i=1; i<=9; i++)
        {
            this["btn"+i].buttonMode = true;
            this["btn"+i].addEventListener(MouseEvent.CLICK, placeSymbol);
        }
        
        addEventListener(Event.ENTER_FRAME,update);
        
        stage.focus = this;
    }

startGame is called in the frame labelled game.

This will be the heaviest start-kind-of-function as you can see. Basically, the role of this function is to kick start all the necessary variables and states in the game. You can see that we set both the score1 and score2 to 0, because, well, at the start of the game, both players should start with 0 scores.

phase is set to player1 because we want player 1 to start first.

 

The game loop is created in the game by adding an event listener that listens to the ENTER_FRAME event. So, if you set the fps of the game to 30, this update function will run 30 times a second (assuming the computer is able to handle the complexity of each game loop).

 

What "stage.focus = this" does is to set the focus back into the game. When you clicked on a button to get from the Menu frame into the game, the focus is actually given to the button. Hence, if the game is played using the keys on the keyboard, they will not work unless you click on the game screen again to give it focus. You can try commenting this off to see what I mean.

Part 4

    //All Goto Functions
    private function gotoStartGame(evt:MouseEvent)
    {
        btnStartGame.removeEventListener(MouseEvent.CLICK, gotoStartGame);
        btnHowToPlay.removeEventListener(MouseEvent.CLICK, gotoHowToPlay);
        gotoAndStop("game");
    }
    
    private function gotoHowToPlay(evt:MouseEvent)
    {
        btnStartGame.removeEventListener(MouseEvent.CLICK, gotoStartGame);
        btnHowToPlay.removeEventListener(MouseEvent.CLICK, gotoHowToPlay);
        gotoAndStop("howtoplay");
    }
    
    private function gotoMenu(evt:MouseEvent)
    {
        btnBack.removeEventListener(MouseEvent.CLICK, gotoMenu);
        gotoAndStop("menu");
    }

These functions handle the interaction when the player clicks on the buttons in the menu, how to play section, etc.

Before it jumps to the right frame, it removes the event listener from the button first. This is just good programming practice.

Part 5

    private function keyDownHandler(evt:KeyboardEvent)
    {
        
    }
    
    private function keyUpHandler(evt:KeyboardEvent)
    {
        
    }

The entire game is actually played with the mouse, so there's no need for any key handlers, up or down. I'm just leaving them here for consistency's sake because most of the other games have them.

If it bothers you, remove them.

Part 6

    public function update(evt:Event)
    {
        handleUserInput();
        handleGameLogic();
        handleDraw();
        
        if (gotoWin)
            triggerGoToWin(); 
        //if (gotoLose)
        //	triggerGoToLose();
    }

This is how the game loop will look like. I've further broken down what the game loop should be doing by creating 3 other functions.

handleUserInput will take care of the player's inputs, be it mouse, or keyboard.

handleGameLogic will almost always be the biggest function. It handles everything from updating the player's movements, bullets' movements if any, to even more fanciful stuff like calculating advanced enemy AI.

handleDraw does little, but it is important in presenting the game states to the player by updating the UI. For example, if the player gains score, the Score textfield will be updated here to reflect the correct score.

After these 3 functions have executed, the gotoWin variable is checked to see if it is true. If so, the triggerGoToWin function will be called, and the game will end thereafter.

In this case, there is no loss condition at all, because one player has to win. I'm leaving the gotoLose codes here, albeit commented, so that you can get a sense of how a loss state is implemented if need be. The other tutorials will use it.

Part 7

    private function handleUserInput()
    {
        //All user input handled in placeSymbol function
    }

Usually, the handleUserInput function will have codes that will make the game react to the players' keyboard inputs, mouse inputs, etc.

In this game, I've coded all that logic in the placeSymbol function instead. Take a look at it later.

Part 8

    private function handleGameLogic()
    {
        if (delay > 0) //if this is true, means game has been won or draw
        {
            delay--;
            
            if (delay == 0)
            {
                if ((score1 >= 5) || (score2 >= 5))
                    gotoWin = true;
                else
                    resetGame();
                
            }
            
            return;
        }

At the start, we check if the variable delay contains a number greater than 0. If it is, it means to say that the game has already ended because when the game is won or it ended up in a draw, we will assign a number 60 to the delay variable. Back in the startGame function, delay was initialised to -1.

Since handleGameLogic will run 30 times a second, we remove a value of 1 from delay (because of the code delay--) per run of a game loop. Since we assign a number 60 to delay when the game is won, it means to say that we'll be delaying the game by 2 seconds.

Because when delay eventually reaches 0, we check if score1 or score2 is greater than 5. If so, it means either player has won the game 5 times, and we will advance to the win screen.

Otherwise, it means that the current game ended in a draw, and we will carry on with the next game. But before that, we will reset the game first.

Part 9

        //Check for win here
        var symbols = new Array("cross","circle");
        var foundWin = false;
        
        for (var i in symbols)
        {
            //check for horizontal first row
            if (btn1.currentLabel == symbols[i] &&
                btn2.currentLabel == symbols[i] &&
                btn3.currentLabel == symbols[i])
                foundWin = true;
            //check for horizontal second row
            else if (btn4.currentLabel == symbols[i] &&
                    btn5.currentLabel == symbols[i] &&
                    btn6.currentLabel == symbols[i])
                        foundWin = true;
            //check for horizontal third row
            else if (btn7.currentLabel == symbols[i] &&
                    btn8.currentLabel == symbols[i] &&
                    btn9.currentLabel == symbols[i])
                        foundWin = true;
            //check for vertical first row
            else if (btn1.currentLabel == symbols[i] &&
                    btn4.currentLabel == symbols[i] &&
                    btn7.currentLabel == symbols[i])
                        foundWin = true;
            //check for vertical second row
            else if (btn2.currentLabel == symbols[i] &&
                    btn5.currentLabel == symbols[i] &&
                    btn8.currentLabel == symbols[i])
                        foundWin = true;
            //check for vertical third row
            else if (btn3.currentLabel == symbols[i] &&
                    btn6.currentLabel == symbols[i] &&
                    btn9.currentLabel == symbols[i])
                        foundWin = true;
            //check for diagonal top left to bottom right
            else if (btn1.currentLabel == symbols[i] &&
                    btn5.currentLabel == symbols[i] &&
                    btn9.currentLabel == symbols[i])
                        foundWin = true;
            //check for diagonal top right to bottom left
            else if (btn3.currentLabel == symbols[i] &&
                    btn5.currentLabel == symbols[i] &&
                    btn7.currentLabel == symbols[i])
                        foundWin = true;
                        
            if (foundWin)
            {
                if (symbols[i] == "circle")
                {
                	currWinner = "Player 1";
                    score1 += 1;
                    delay = 60;
                }
                else
                {
                	currWinner = "Player 2";
                    score2 += 1;
                    delay = 60;
                }
                
                break; //only 1 player can win.
            }
        }

These codes here do a check for whether either player has won the game. There are basically 8 ways to win a Tic Tac Toe game in all the horizontal, vertical and diagonal patterns, so we have to check for each pattern.

Since both players have a chance to win, we do not want to repeat the codes two times for each player. So, a smart way is to create an array that contains only two values, a cross and a circle value, each denoting for each player.

Then we loop through them, and the codes will dynamically just compare against symbols[i], which will alternate between cross and circle.

foundWin will be set to true if a winning pattern has been detected. We check which current iteration of the loop it is in to determine who the winner should be.

currWinner is used to remember the winning player so that it can be displayed on the txtStatus text field, score is increased, and the delay variable is set to 60 so that we will experience a 2 seconds delay, as explained earlier.

Part 10

        //Check for draw here
        var allFilled = true;
        for (var i=1; i<=9; i++)
        {
            if (this["btn"+i].currentLabel == "empty")
                allFilled = false;
        }
        if (allFilled) //it is a draw
            delay = 60;
    }

If the game is not won yet, we check to see if all the tiles have been filled. If so, it is a draw.

To check for a draw, we loop through all the placement areas for the tiles, and see if they their states are still "empty". Because when each player clicks on the placement areas to place their mark, the placement areas will switch to a "circle" or "cross" state depending on who placed it.

Open up the symbol "Place" to see what I mean.

Since we'd also like the game to pause for say 2 seconds after a Draw, we increase the delay to 60 as well.

Part 11

    private function handleDraw()
    {
        //Handle display
        txtScoreP1.text = String(score1);
        txtScoreP2.text = String(score2);
        
        if (delay == -1)
        {
            if (phase == "player1")
                txtStatus.text = "Player 1's Turn ( O )";
            else if (phase == "player2")
                txtStatus.text = "Player 2's Turn ( X )";
        }
        else if (currWinner != "")
        {
            if (phase == "player1")
                txtStatus.text = "Player 2 Won!";
            else if (phase == "player2")
                txtStatus.text = "Player 1 Won!";
        }
        else
        {
        	txtStatus.text = "Draw!";
        }
    }

Typically, we use the handleDraw function to update the UI displays. In this case, we update the text field that shows the scores of both players. Since score1 and score2 are both numbers, we need to cast them into a string by using String(score1) before assigning them to the text fields.

If the delay is still set at -1, we will just change the txtStatus text field to alternate showing whose turn it currently is. We can do this by checking against the variable phase, which will show either "player1" or "player2".

Otherwise, if delay is not -1, it means that either a Draw or Win has occurred.

In that case, we first check if currWinner is empty. If so, it means that it is a Draw since we will always set currWinner to have the value of the winning player.

Part 12

    private function triggerGoToWin()
    {
        removeEventListener(Event.ENTER_FRAME, update);
        gotoAndStop("win");
    }
    
    private function triggerGoToLose()
    {
        removeEventListener(Event.ENTER_FRAME, update);
        gotoAndStop("lose");
    }

The two functions triggerGoToWin and triggerGoToLose are called whenever the respective win or lose states happen.

To prevent the game from continuing to run, we will remove the event listener to ENTER_FRAME, hence stopping the game loop.

Part 13

    //Misc Functions
    private function resetGame()
    {
        for (var i=1; i<=9; i++)
        {
            this["btn"+i].gotoAndStop("empty");
            this["btn"+i].buttonMode = true;
            this["btn"+i].addEventListener(MouseEvent.CLICK, placeSymbol);
        }
        delay = -1;
        currWinner = "";
    }

In the resetGame function, we need to clear all the placement areas back to their "empty" state. Because we remove the event listeners for those placement areas whenever we click on them, we have to add the event listeners back.

buttonMode is a property that will change the mouse cursor to a Hand symbol when we mouse over. This would be ideal since we want the players to know that they can click on the placement area when they move their mouse cursor over.

Part 14

    private function placeSymbol(evt:MouseEvent)
    {
        if (phase == "player1")
        {
            (evt.currentTarget as Place).gotoAndStop("circle");
            phase = "player2";
        }
        else if (phase == "player2")
        {
            (evt.currentTarget as Place).gotoAndStop("cross");
            phase = "player1";
        }
        
        (evt.currentTarget as Place).buttonMode = false;
        (evt.currentTarget as Place).removeEventListener(
        				MouseEvent.CLICK, placeSymbol);
    }
}//end class

The placeSymbol function is the core of what makes the game work. You should be able to see 8 Place symbols, each occupying a grid space of the 3x3 tic tact toe play area.

If you take a peek into the Place sybmol, you can see that it has 3 states, empty, cross and circle.

When the Place object is clicked upon, the phase is first checked to see whether it is player1 or player2. Depending on it, we instruct the Place object to gotoAndStop at either circle or cross.

evt.currentTarget is a reference to the Place object that you clicked. As a good practice, we cast it back into a Place object (using as Place) before we ask it to gotoAndStop at the right frame. You'll encounter this coding style very often.

After the Place object has been used, we remove its event listener to mouse clicks so that another player cannot place his mark on that same square again. Likewise, we set its buttonMode to false so that the player gets the impression that it cannot be clicked.

Download the Game Files

The files you need to create this game can be downloaded from here.

The Game

Since this is a 2 player game, find a friend to play against you now.




How To Play

Objective: Form a straight line with 3 of your marks (O or X). Diagonal counts as well.

Controls: Click on the shaded grey square to put down your mark.


Content on this page requires a newer version of Adobe Flash Player.

Get Adobe Flash player

Flash Resources
Preloader FPS Display Sounds & Music
Keycodes Name Generator
Game Development Resources
Sprite Sheets


Posted by Rollie

on 2014-10-05 11:58:15

since im a noob here... where do i put this codes receptively? im very confused at the moment and the codes are not incoded in the flash that i downloaded from this site


Posted by Joseph

on 2014-03-21 09:56:45

Sheila, this is not a bug. Just a warning because we are reusing variable names. :)

If you want, you can use any var name.


Posted by Shiela

on 2014-03-16 14:51:00

I have downloaded this tutorial and many thanks to that. But I have a question, how should I fix this bug? It says: Line 221 Column 13 Warning: Duplicate Variable Definition. Please help.


Flash Tutorial Links:
Play Games: HTML5 Tutorials:
gaming tools download on app store now!
Home | About Us | Contact Us
Powered by Make Flash Games. All Rights Reserved.
By Joseph Tan