Skip to main content

Simple AngularJS app TicTacToe Game



A note from the author
: “After checking AngularJS I said to my colleague let’s blog about it… so here we are”.

This article is based on a simple example that it was quickly done with this MVC library. We started reading the book AngularJS from O’Reilly, by Brad Green & Shyam Seshadri and we wanted to use it right away. We are used to the MVC pattern because of MVC.net, when we saw AngularJS we immediately wanted to try it; also we had some influence from our front-end developers (Just to let you know part of our background is back-end development).

Basically we wanted to focus to the initial part of our study around this book providing examples around simple items. In this case we selected an example that can illustrate data binding, the watch function and CSS classes. We did not wanted to do the “Hello world” example from the book; as always we tried to do something fun, so we thought, let’s do a simple TIC-TAC-TOE game experience, THAT YOU PLAY ON THE SAME DEVICE (the 2 users should share it... yes I little to simple, we know!).

It had to be quick as well, so just a simple example would do the trick. Keep in mind this code can be refactor and make it way much better. There is a potential to use ng-repeat, which we did not include it here. I believe refactoring can reduce the amount of lines of code in our example. This was thought –created-done in less than 2 hours. Writing this article took us longer.

Try the TicTacToe here: http://www.sunitedsolutions.com/tictactoe/default.html

Download it from here: https://github.com/consultingti/simpletictactoe


What you won't find in this article
  • as I mentioned before this is a basic example, no complex work, but rather demonstrating the $watch function. No security discussion, we might work on that later on. But if you are interest and want to read or learn more about sanitizing or how to make your app more secure when using angularjs then this is a good place to start: http://www.youtube.com/watch?v=18ifoT-Id54 . In that youtube video the host is using laravel, which is a php framework, but you can do the same thing by using .Net (which is my preference because of my .Net background) or any other language and framework you feel comfortable.
  • Another good read with good comments and suggestions here: http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app
  • There is no API calls or back-end functionality (data, authorization functionality, etc.). Perhaps in the future if we move to a second version of this example.

For our little experiment we used AngularJS (of course, the star of the article) and also BootStrap as we wanted to play a little more and combined tools. Below you can find a brief definition of both items. After we’ll go into the example:

  1.  AngularJS: open-source javascript framework, maintained by Google that assists with running single-page applications. Its goal is to augment browser-based applications with MVC capability, in an effort to make both development and testing easier. – definition from Wikipedia.
  2.  $watch: this is from AngularJS. It is a $scope function that is called with an expression to observe and a callback that gets invoked whenever that expression changes.
  3. Bootstrap: sleek, intuitive and powerful mobile front-end framework for faster and easier web development. – definition from Bootstrap.

The Example:

It is about a web-base, simple Tic-Tac-Toe game.

Some requirements:

  •       Responsive app, so you can load it in your phone (this is where BootStrap comes into play)
  •       MVC (angularJS role)
  •       2 users: 1 named “X” the other named “O”. The site will make it clear the turn for each user.
  •       Honor the rule of the game:  two players, X and O, who take turns marking the spaces in a 3×3 grid. The player who succeeds in placing three respective marks in a horizontal, vertical, or diagonal row wins the game.

What do you need?

  1. Download Bootstrap from http://getbootstrap.com
  2. Download AngularJS from http://angularjs.org
  3. Some knowledge about HTML, CSS, Javascript. But no expert level is required for this simple example.

Some key part from the code:

  1. The ng-app attribute tells Angularjs which parts of the page it should manage. Since we’ve placed it on the <html> element, we’re telling Angularjs that we want it to manage the whole page.<html lang="en" ng-app>
  2. The bootstrap css was added, along with a custom table.css that I created for the tic-tac-toe html section.
  3. The angular.js.  <script src="./assets/js/angular.js"></script>
  4. The code that makes the little game possible. It may need refactoring and you may even want to put it in a different file and not in the same HTML as I did. As I said this is just a simple example, introduction level.  I’ll describe the code on the next section.
  5. The ng-controller. With it you manage areas of the page with JavaScript classes called controllers. By including a controller in the body tag, you will manage everything inside the <body> area.<body ng-controller="simpleGameController" onload="inigame();" >
  6.  <div id="d11" class="cell"><a  style="color:red" href="/" ng-click="clickrow('d11')" ng-class="{playing: row1[0].played == 'true'}">{{row1[0].title}}</a></div> The ng-click. A call to action to a method of the controller when you click this element.    The ng-class. In this case we have a condition that if an object has a particular value then we’ll add the “playing” class to the element.    {{object}}. To display the value.

Explanation of the Controller code:

  • simpleGameController: is our AngularJS controller receiving the scope object, which refers to the application model.
  • Player variable: to flag if is “X” or “O” that is playing. In our example “X” is the starting user always. As I said it is a simple demo so in this example you don’t get to choose who plays first, in this case the “X” will always play first.
  • There are 3 arrays (row1, row2, row3) for the rows. In each array you have:
    • Title. Initialize with string ”xod”. As the player clicks this value will change either to “X” or “O”, depending on the user.
    • Played. Initialize to “false”. As the player clicks it will change to “true” indicating you cannot click again on that cell.
  • Winner: function that indicates if a user won.
  • Clickrow: it is triggered when you click a cell where the user is playing. It received the parameter rowCoord, which indicates what cell was clicked. It also loads the modal window indicating the turn of the user.
  • Inigame: just called when the page loads to start the game, indicating the first player.
  • $watch: angularjs goodie that we use to observe the player variable. When this variable changes the value the winner function gets trigger to check if there was a winner or not.

The controller code:
<script>
     
     function simpleGameController($scope)
     {
     $scope.test = "1";
     $scope.player="X";
     $scope.win = "";
     $scope.lost="";
   
   
      $scope.row1 =  [{title: 'xod', played: 'false'},
                       {title: 'xod',played: 'false'},
                       {title: 'xod',played: 'false'}];
      $scope.row2 =  [{title: 'xod',played: 'false'},{title: 'xod',played: 'false'},{title: 'xod',played: 'false'}];
      $scope.row3 = [{title: 'xod',played: 'false'},{title: 'xod',played: 'false'},{title: 'xod',played: 'false' }];
                      
          
       $scope.winner = function()
       {
          var a = [
                    [$scope.row1[0].title,$scope.row1[1].title, $scope.row1[2].title],
                    [$scope.row2[0].title,$scope.row2[1].title, $scope.row2[2].title],
                    [$scope.row3[0].title,$scope.row3[1].title, $scope.row3[2].title]
                   
                  ]
          var matches = 1;
          //check the winner by evaluating who hit 3 in a line (vertical, horizontal, diagonal)
          //check horizontal
             //a. checking 1st array
             //b. checking 2nd array
             //c. checking 3rd array
           for (var i=0; i< 3;i++)
           {
             for (var j=0; j<3;j++)
             {
               if ((matches<=2)&& (j>0))
                {
                  if ((a[i][j-1]==a[i][j])&&(a[i][j-1]!="xod"))
                  {
                    matches=matches+1;
                  }
                }
             }
             if (matches<3)
                  matches=1;
             else
                {
                   $scope.test="winner";
                   $scope.lost = $scope.player;
                if ($scope.player=="X")
                     $scope.win = "O";
                     else
                     $scope.win = "X";
                return "winner";                    
                }
           }
            
          //check vertical
          for (var i=0; i< 3;i++)
           {
             for (var j=0; j<3;j++)
             {
               if ((matches<=2)&& (j>0))
                {
                  if ((a[j-1][i]==a[j][i])&&(a[j-1][i]!="xod"))
                  {
                    matches=matches+1;
                  }
                }
             }
             if (matches<3)
                  matches=1;
             else
              {
                $scope.test="winner";
                $scope.lost = $scope.player;
                if ($scope.player=="X")
                     $scope.win = "O";
                     else
                     $scope.win = "X";
               
                return "winner";           
              }
           }
          //check diagonal
          for (var i=0; i< 3;i++)
           {
            // for (var j=0; j<3;j++)
             //{
               if ((matches<=2)&& (i>0))
                {
                  if ((a[i-1][i-1]==a[i][i])&&(a[i-1][i-1]!="xod"))
                  {
                    matches=matches+1;
                  }
                }
             //}
             if (i==2)
             {
             if (matches<3)
                  matches=1;
             else
              {
                $scope.test="winner";
                $scope.lost = $scope.player;
                if ($scope.player=="X")
                     $scope.win = "O";
                     else
                     $scope.win = "X";
                return "winner";           
              }
              }
           }
        if (!($scope.test=="winner"))
        {
         $('#myModal').modal('show');
         }
       }
      
   
       $scope.clickrow = function(rowCoord)
       {
         //$scope.row1[0].title="wao";
         if (($scope.player.length > 0)&& !($scope.test=="winner"))
         {
          
            if (rowCoord.length >0)
            {
                var s = rowCoord.split("");
             
                if (s[1]=="1")
                {
                //row1
                     if ($scope.row1[parseInt(s[2])-1].title=="xod")
                     {
                         $scope.row1[parseInt(s[2])-1].title = $scope.player; 
                         //turn the value visible by chaging the background to white
                         $scope.row1[parseInt(s[2])-1].played="true";
                     }   
                }
                if (s[1]=="2")
                {
                //row2
                 if ($scope.row2[parseInt(s[2])-1].title=="xod")
                     {
                         $scope.row2[parseInt(s[2])-1].title = $scope.player; 
                         //turn the value visible by chaging the background to white
                         $scope.row2[parseInt(s[2])-1].played="true";
                     }   
                }
                if (s[1]=="3")
                {
                //row3
                 if ($scope.row3[parseInt(s[2])-1].title=="xod")
                     {
                         $scope.row3[parseInt(s[2])-1].title = $scope.player; 
                         //turn the value visible by chaging the background to white
                         $scope.row3[parseInt(s[2])-1].played="true";
                     }   
                }
               
                 
                  if ($scope.player=="X")
                     $scope.player="O";
                     else
                     $scope.player="X";
            }
         }
       
       }
      
       $scope.userturn = function(turn)
       {
          if (turn==null)
          {
            
             $('#myModal').modal('show');
             }
       }
      
       $scope.$watch('player',$scope.winner);
     }
   
     function inigame()
     {
     //$scope.player="X";
         $('#myModal').modal('show');
     }
   
      function restart()
       {
         location.reload();
       }
   
    </script>

Trending posts

Headless CMS - yet another CMS comparison

If you have used, or are familiar with the term headless CMS (content management system), then you will know the reason behind it. In summary a CMS is called headless because there is no Front end (FE), or output coupled with the CMS. Instead the CMS supports APIs which expose the content that can be consumed by external applications.                                                                 Photo by fotografierende from Pexels We could say that a decoupling from the CMS is provided, allowing, for those that have chosen this alternative, a certain sense of “freedom” in selecting the FE of the solution. There are many reasons for selecting the most popular, and so called “traditional” CMSs in the market. Even though, not sure if it is proper to call them “traditional” anymore as they keep evolving, becoming stronger and versatile and offering new services including SaaS. Even some are providing a flavor of headless. I have my fair share of experience using some of the greatest

Essence of email deliverability - SPF, DKIM, DMARC and segmentation

Recently I attended a lively discussion on email deliverability hosted by Litmus, one of the leading providers of email marketing tools. As email marketers one of the core metrics we often rely on is email deliverability and the discussion was around how to improve the email deliverability in today’s world where our audiences are ever inundated with emails. In addition we often find ourselves operating in a very competitive landscape vying for these audiences' precious attention. Photo by Ivan Samkov from Pexels   Hence it’s becoming increasingly important to ensure our emails are reaching our audiences’ inbox and driving engagement. This is why email clicks and engagement are strong indicators of the performance of our email campaigns instead of just the delivery rate of our campaigns partly thanks to Apple’s Mail Privacy Protection . So let’s start with the distinction between email delivery and email deliverability . This is very well articulated by Dimiter Batmaziam in the Li

Blue Ocean Strategy in the new reality

Photo by Kammeran Gonzalez-Keola from Pexels Since the COVID lockdown started over a year ago I have seen many restaurants in my neighborhood go through transformations - many became available in the top three delivery service: Ubereats, Doordash and Skipthedishes while the rest transformed to support pick up service and patio dining. However, among all of them, one of my favorite Indian restaurants impressed me the most.  The restaurant initially became available via the delivery services just after the lockdown in early 2020. I first ordered from the restaurant via Ubereats. In the delivery the restaurant included a pamphlet encouraging me to order from them directly on their website with an initial enticing offer. I ordered immediately after a few days from their website. I was guided to create an account and subscribe to their newsletter in the process.  The delivery for the order was on time and the food as always was delicious and of high quality. In a few days I received a

Goal setting frameworks for Product Management - OKR and HOSKR

As a business analyst and product manager we often use various frameworks to synthesize and organize our product ideas and goals. I think of frameworks as tools in our product management tool kit which we use depending on the task at hand.  And speaking of goals, OKR is a very popular framework that I often use to set the goals for the products I am managing. However recently I participated the #ProductCon conference hosted by Product School  and I stumbled upon one of the talks in which Rapha Cohen, the CPO at Google Waze introduced a more effective framework for setting product goals. The framework is called HOSKR.  In this post I'll describe both the OKR and HOSKR frameworks in more details using examples. I hope this will provide you, our readers, more practical insights on how to effectively use these frameworks to set your product goals.  OKR OKR stands for O bjectives and K ey R esults. If you are reading this post then you are on our Beolle blog and I am going to use one o

This blog uses cookies to improve your browsing experience. Simple analytics might be in place for pageviews purposes. They are harmless and never personally identify you.

Agreed