Skip to main content

Simple AngularJS app TicTacToe Game

[PLACEHOLDER]


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.

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

Rethinking Canadian Economic Sovereignty Amid Foreign Acquisitions

About a year ago, I attended the Elevate Festival in Toronto, which I recommend if you haven't had the chance to experience it. The event was very inspiring because of its focus on business, technology, and entrepreneurship. It was interesting to see people at various stages of their careers; some executives, others developing their startups, a few in between, and some simply there to gain insights and knowledge. There was a unique atmosphere, influenced by the current political situation between Canada and the US. Many discussions centred around data sovereignty, cloud infrastructure independence, and ensuring that Canadians' data remained safe and secure within the country's borders or trusted allies. Building trust requires considerable effort, yet it can be shattered very quickly… and once broken, repairing it becomes extremely difficult, if not impossible. Considering the current geopolitical landscape, it is unclear where we go from here... but it seems the rules hav...

Reimagining Digital Experience Management: How Agentic AI is Transforming Adobe Experience Manager

 Adobe Experience Manager (AEM) has introduced powerful new Agentic AI capabilities designed to continuously improve and adapt digital experiences at the speed of AI. By integrating advanced AI orchestrators through Agent-to-Agent (A2A) and Model Control Protocol (MCP) tools, AEM enables brands to automate complex workflows and enforce compliance seamlessly across enterprise ecosystems. Through a suite of specialized agents, teams can transition from manual, weeks-long processes into fast, AI-assisted workflows powered by simple natural language prompts. Photo by Tunahan KALAYCI via Pexels   Here is a breakdown of the key agents driving AEM’s new Agentic capabilities, their value propositions, their guardrails, and their current availability status. 1. Brand Experience Agent. Overview. The Brand Experience Agent accelerates digital modernization through specialized sub-agents—the Experience Modernization Agent, Experience Production Agent, and Experience Development Agent. Tog...

Steer for a talent transformation strategy (and avoiding AI fatigue)

 There was a debate on whether to feature the term “AI” in the title of this article. Honestly, a key motivation for pursuing the research that led to this post was sparked by the widespread excitement about AI appearing constantly in our LinkedIn feed, to the point of feeling the fatigue, and even a bit disappointed in the algorithm of this, and the others, social media and content curated apps.  We soon discovered that there is an entire concept called "AI fatigue", not exactly how we were feeling it, but more about the mixed emotions people in the workforce have regarding the use of AI tools. Photo by Mart Production via Pexels (background updated with AI and Adobe  tech) From micro blog posts to video podcasts, lately, most of the tech content we encounter revolves around AI. They often sound or read very similar, usually mentioning the same few top providers. The articles (and social posts... at least the popular ones with paid-campaigns behind it) tend to focus less...

Digital Sovereignty in a Polarised World - Data, Cloud Power, and the Search for Trusted Alternatives

 Relationships have deteriorated, with trust diminished to an extent that may preclude restoration. The world, once structured to favour certain regions, has undergone significant shifts; for numerous countries, such advantages never existed. In this polarised reality, stakeholders are re-evaluating alliances, as former partners now often embody the role of "frenemy," thereby threatening freedom. This phenomenon is longstanding, rooted in historical power dynamics. When politics and influence supersede principles of fairness, respect, and integrity, ethical boundaries become blurred. Previously, issues that did not directly affect you would get overlooked out of principle, but current risks necessitate action to safeguard sovereignty. Information has consistently served as a key strategic asset, a trend only intensified by technological advancements that have elevated data as the principal factor. In other words, technology has amplified that, and data is the name of the game...

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