How to build a JavaScript Library

If you’re a developer it’s almost certain that you’ve used Code Libraries. In simple terms, a Code Library is a collection of reusable code that you can use in your program.

Like most of us, I know how to plug libraries into my programs and use them, but used to get scary for me each time I went through the actual source code of that particular library. Then I got to learning Vanilla JS and it all stopped seeming so scary. Vanilla JS is plain JavaScript code that you write without the use of any external libraries.

When it comes to building a JavaScript library that you will use to create a website, It’s important that you learn DOM manipulation using Vanilla JS before attempting to actually build it. The reason that it’s important is that you won’t have any dependencies and your library will be a simply plug-n-play for your users. You may be familiar with  DOM manipulation using jQuery but that will create a dependency on jQuery and your library won’t function on a webpage that doesn’t include a reference to the jQuery library.

If you’re familiar with jQuery and want to learn more about DOM manipulation using Vanilla JS, you can read more about it here.

With all that said let’s dive into creating a JavaScript Library from scratch.

We’ll building an Image Lightbox. A lightbox is library that is used to view images on a webpage by filling the page with the image and de-emphasizing the background. Here’s what we’ll build.

If you want to jump ahead and see the code you can find the GitHub repository here.

When building a JavaScript Library there are a few things that you want to keep in mind.
Your code shouldn’t interfere with the existing code written by the user. To do this we’ll encapsulate all our code in a single Function object.

function FsLightbox() {
   // all the code goes here
 }

All the code that we write in this tutorial will be written in that function body. This will take care of the encapsulation.

We now have to create a list of all the methods that the user will be given access to and that will be useful the user. In this library, we’ll have four such methods

.render() .next().prev(), and .hideLightbox()

The FsLightbox object will thus have these four public members. By public members we mean all those methods and properties that are preceded with the this keyword.

First and foremost we’ll store a reference to the object context as using this keyword can get messy in JavaScript.

let _this = this;

This will eliminate all the confusion and let us use the object context without any worries.

With that out of the way, let’s go over the strategy that we’ll employ to get the lightbox working. 

  • The user will specify the images that will use the lightbox by adding a specific class to those img tags. So, every time DOM loads, we’ll be able to capture those images and save their references in an array.
  • After that, we’ll add a click listener to each image to make sure that every time the user clicks that image, it will be shown in the lightbox. We’ll leverage both JavaScript as well as CSS to achieve this.
  • We’ll also need navigation controls for Previous Image, Next Image and a button to Close the lightbox. We’ll wire these controls with their respective click listeners.
  • We’ll also display some meta information like Image Counter and Caption (we’ll get this from Alt text)

As you may have already realized the JavaScript library that we’ll create will use a lot of CSS and hence, we’ll be including a CSS file also.

Enough talk, let’s write some code. First, we’ll need to keep track of some data and hence, we’ll be declaring some variables that will be available throughout our little Function. These will be the following:

    let imagesArray = []; // Array containing the reference of all the images that have the specified class (fs-lightbox)
    let currentImage; // Image being displayed currently in the lightbox
    let isLightbox = false; // Boolean to tell you whether the image is being displayed in the lightbox or not 

After this we’ll write our first public method, .render(). In here we’ll initialize are “global” variables and get a reference of all the images stored in the array. This method will need to be run to actually initialize our Lightbox. Here’s the code:

        this.render= () => {
            imagesArray= [];
            currentImage=null;
            isLightbox=false;
            document.querySelectorAll('img.fs-lightbox').forEach((img_el, index) => {
                imagesArray.push(img_el);
                img_el.setAttribute("data-lightbox-index", index);
                img_el.addEventListener('click', () => {
                    _this.lightbox(img_el);
                });
             });
             addKeyListeners();
        }

As seen we’re using a function addKeyListeners() here. This method is used to hook up all the keyboard actions for Next, Prev and Close Lightbox. We’ll get to it later. The event listener that we are adding here is _this.lightbox(). This method will be responsible for actually displaying the image in the lightbox. Here’s it’s implementation.

    this.lightbox = _el => {
       this.hideLightbox();
       currentImage = _el;
       isLightbox = true;
       var overlay = document.createElement('div');
       overlay.classList.add('lightbox-overlay');
       var imageContainer = document.createElement('div');
       imageContainer.classList.add('lightbox-image');
       var image = document.createElement('img');
       image.src = _el.src;
       imageContainer.appendChild(image);
       document.querySelector('body').appendChild(overlay);
       document.querySelector('body').appendChild(imageContainer);
       prepareControls(_el);
   }

As evident in the render() method, this method expects the image element _el as a method parameter. This will be a reference to the img element that needs to be displayed in the lightbox. If we analyse this method further, we see that we first hideLightbox() if it’s already displaying some other image. Then we save the reference of the image in our global variable currentImage and also change the isLightbox variable to true. Then the real DOM manipulation begins. 

We create the overlay div overlay using createElement method and add the class lightbox-image to it. The CSS file that we create will contain all the necessary CSS for the overlay such as position and background-color. I won’t dive into that code as that is not the point of this post. That being said, here’s a snippet detailing the same:

.lightbox-overlay {
   position: fixed;
   top: 0;
   left: 0;
   width: 100vw;
   height: 100vh;
   background-color: rgba(0,0,0,0.7);
   z-index: 10000;
}

Once we create the overlay, we need to create a container for the actual image. The imageContainer variable will contain the same. The variable will be a reference to a div that we create and then we will create an img element image and append it to the same div. With the supplied _el function parameter we’ll have the src of the clicked image and will set the src of image element to the same value. Then, we append these newly created elements, overlay and imageContainer to the DOM. That’s it! Now every time the user clicks an image that has the fs-lightbox class added to it, it will trigger the lightbox() method and the image will be displayed in the lightbox.

That’s most of the hard-work done. Now comes the polish. That’s just us making our library a little more user friendly. This is very important when creating a library. Always think from a user’s perspective. The more convenient it’s for the user, better it will be received.

So, let’s get to it then. Remember, earlier in the post I mentioned all the four public methods that will be available to the users. We have just implemented one of those four methods – render(). We need to implement the rest – next(), prev() and hideLightbox().

This will be done in the prepareControls() method. You can see in the lightbox()  method we’re calling this method. This method will take care of both rendering the UI Elements, i.e., buttons for the above actions as well as attaching click listeners to them. Here’s the implementation.

    function prepareControls(imgElement) {
       let controls = document.createElement('div');
       controls.innerHTML += controlsHtml;
     document.querySelector('body').appendChild(controls.querySelector('.lightbox-controls'));
       let imgIndex = getCurrentImageIndex();
       if (imgIndex > 0) {
           document.querySelector(".lb-prev").addEventListener('click', () => {
               _this.prev();
           })
       }
       else {
           document.querySelector(".lb-prev").classList.add(['lb-disabled'])
       }
       if (imgIndex < _this.imagesArray.length - 1)
             document.querySelector(".lb-next").addEventListener('click', () => {
               _this.next();
           })
       }
       else {
           document.querySelector(".lb-next").classList.add(['lb-disabled'])
       }
       document.querySelector('.lb-close').addEventListener('click', () => {
           _this.hideLightbox();
       })
       showCounter();
   }

As I said, creating controls and hooking them up with click listeners is all that’s being done here. The handlers this.next() and this.prev() are implemented as:

    this.next = () => {
       let imgIndex = getCurrentImageIndex();
       if (imgIndex === _this.imagesArray.length - 1)
           return;
       _this.lightbox(_this.imagesArray[getCurrentImageIndex() + 1]);
   }
   this.prev = () => {
       let imgIndex = getCurrentImageIndex();
       if (imgIndex === 0)
           return;
       _this.lightbox(_this.imagesArray[getCurrentImageIndex() - 1]);
   }
    this.hideLightbox = () => {
       let overlay = document.querySelector('.lightbox-overlay');
       let image = document.querySelector('.lightbox-image');
       let controls = document.querySelector('.lightbox-controls');
       if (overlay)
           document.querySelector('body').removeChild(overlay);
       if (image)
           document.querySelector('body').removeChild(image);
       if (controls)
           document.querySelector('body').removeChild(controls);
       this.isLightbox = false;
   };

.next() gets the next image from the array imagesArray and passes it to .lightbox() method and similarly for .prev() method. hideLightbox() removes the displayed image from the DOM.

We’re left with two more things – Showing meta information and Binding keyboard shortcuts.

We’ll write two methods for this, addKeyListeners() and showCounter() (poor name choice, should be showMetaInformation), Here’s the implementation. It’s fairly simple to understand once you go through it.

   function showCounter() {
       let imgIndex = getCurrentImageIndex();
       let counter = document.createElement("span");
       let counter_Html = `<br/>${imgIndex + 1} of ${_this.imagesArray.length}`;
       if(_this.currentImage.alt){
           counter_Html += ` - ${_this.currentImage.alt}`;
       }
       counter.innerHTML = counter_Html;
       document.querySelector('.lightbox-image').appendChild(counter);
   }
    function addKeyListeners() {
       document.removeEventListener('keydown', bindKeys);
       document.addEventListener('keydown', bindKeys);
   }
   function bindKeys(e) {
       // left arrow key
       if (e.keyCode === 37 && _this.isLightbox) {
           _this.prev();
           return;
       }
       // right arrow key
       else if (e.keyCode === 39 && _this.isLightbox) {
           _this.next();
           return;
       }
       // escape key
       else if (e.keyCode === 27 && _this.isLightbox) {
           _this.hideLightbox();
           return;
       }
   }

That’s all the code that we’re going to write in our function body. We now need to initialize it. This will be done by calling the .render() method that we created. Remember, it’s the method responsible for actually initializing everything. We can do this outside the Function body like this: 

var fsLightbox = new FsLightbox;
fsLightbox.render();

Now the user will have to reference the .js as well as the .css files in their html and they will be up and running with using the library.

That’s about it. There are some smaller functions that I didn’t mention here like getCurrentImageIndex(), etc as those aren’t really important to understanding the workflow. You can still check those in the Github Repo.

Since we’re done with building our library, we need to understand how to publish it so that other users can use it. We have many options for that. We can use GitHubnpm, etc to host our library. Always remember to include a small readme file that details the library’s usage. You can find the example in the same Github Repo.

TL;DR; To create a JavaScript library, you need to keep in mind that you’re encapsulating the code so that it doesn’t interfere with user code, making it simple and easy to use, and trying to make sure it doesn’t have any dependencies. You follow these and you shall be fine.

JavaScript vs jQuery for DOM Manipulation

jQuery has been the savior for so many new and coming Web Developers, including myself. If you wanted to learn Web Development back in the day, learning jQuery was an absolute given. This was mainly because jQuery took much of the cross-browser compatibility issues out and enabled developers to write code without having to worry about whether the features that they are implementing will work on all browsers.

But with improvements in browser standards and most of jQuery’s API’s integrated into JavaScript, jQuery has become a little redundant. Moreover, with native browser API’s it is so much easier to debug code, and being native, most of these API’s offer better performance than jQuery’s API’s. Besides, you will have one less library to add to your script imports. If you’re still not sold on parting with jQuery maybe this answer will help.

So, if you’re considering a move away from jQuery, I have compiled a list of common jQuery methods and API’s that people use, with their Vanilla JS alternatives (Vanilla JS is a fancy name for plain JavaScript code without the use of any libraries). Let’s dive in!

Querying the DOM

The bread and butter of jQuery is it’s amazing ability to query DOM elements. This is demonstrated below:

jQuery('div.home')

However, you can achieve the same thing with JavaScript using it document.querySelector() and document.querySelectorAll() methods. Below is their implementation.

document.querySelector('div.home')

This method always returns the first element that matches the selector. If you’re expecting to query multiple elements, you will have to use the querySelectorAll() method.

document.querySelectorAll('img').forEach((_el, index) => {
      //do stuff with _el here
})

querySelectoryAll() will return an array of matched elements, hence the use of forEach to iterate over each element.

.()html

.html() method of jQuery is used to get or set the HTML content of  a node/element.

To do the same in JavaScript you can use the .innerHtml property of any HTML element.

Let’s say you need to get the HTML content of a div element with class main-div. You could do this in JavaScript like this.

var htmlContent = document.querySelector('.main-div').innerHtml;

Similarly to set the HTML content of the same div, you could do something like this:

document.querySelector('.main-div').innerHtml = '<p>Hello World</p>';

Remember, if you want to change the Html of multiple elements with the same selector, you can use the querySelectorAll() method like this:

document.querySelectorAll('.main-div').forEach((el, index) => {
    _el.innerHtml = '<p>Hello World</p>';
});

Notice that I pass in the index to the anonymous function here. You don’t really need it in this case, but it’s a good practice to keep the index of each element of the array handy. You never know when you may need it.


.val()

.val() method of jQuery is used to get or set the value of an input element.

To get or set the value of an input element in JavaScript, you can use the value property of that element. 

To set the value of an input element with class input-box to ‘new’

document.querySelector('.input-box').value = 'new';

To get the value of the same element

var inputValue = document.querySelector('.input-box').value;

.text()

jQuery offers .text() method to get all the content of an element without the markup. e.g., using jQuery to get the text of <p>Hi</p> element will return the string ‘Hi’. This can be very useful when you’re trying to get the content actually visible in the web-page.

That being said, every HTML element has a property “innerText” that essentially holds the same value. 

let textValue = document.querySelector('.main-div').innerText;

Styling the DOM

One of the most exciting features of jQuery is the ability to modify the styling of our DOM. This is highly useful when you’re trying to have a visual feedback to a user interaction.

Modifying styles can be done by one of two ways:

  1. Adding/Removing Classes:
    To add or remove a class you will most definitely use .addClass() or .removeClass() methods that ship with jQuery.
    To do the same thing in JavaScript, you can use the classList property of an element. This can be done as shown below.
    To add the class testClass to an element with Id testElement

    document.querySelector('#testElement').classList.add('testClass');

    To remove the class testClass from the element with Id testElement

    document.querySelector('#testElement').classList.remove('testClass');

     
  2. Adding/Modifying CSS:
    To directly change the CSS of an element we use the .css() method provided by jQuery. Once again, this is fairly simple to do in JavaScript.
    To set a style for an element, we simply set the value of that style as exposed by the style property of that element.

    document.querySelector('#testElement').style.color = '#ffffff';

    This will set the color of the element with Id testElement to white.
    Note: Since the styles of element are exposed as JavaScript object properties, you won’t be able to use properties such as background-color directly as there is a “-” in the property name. In these situations, you should simply use the camelCased version of these properties. e.g.,

    document.querySelector('#testElement').style.backgroundColor = '#000000';

    This will set the background-color of element with Id testElement to black.

.attr()

Setting and retrieving values of the attributes of HTML elements is once again very useful when developing a web application. You may need to get the value of a data- attribute that is storing important data while working with your application. jQuery provides this ability by exposing .attr() method. You can use it with one parameter to get the value of an attribute or use an overload with two parameters to set the value of an HTML attribute. 

In JavaScript every attribute of an HTML element is exposed as a property of that element. This means that to set the value of the src attribute of an img element, you can simply do the following:

document.querySelector('img').src = '/images/image.png';

It’s that simple. For attributes such as data- you can use another approach,

document.querySelector('img').setAttribute('data-title', 'test');

To get the value of the same element,

document.querySelector('img').getAttribute('data-title');

The getAttribute() and setAttribute() bear a striking resemblance to jQuery’s attr() method. So, getting used to these shouldn’t be that difficult.

.show() / .hide()

Once again, two of the most widely used jQuery methods are the .show() and .hide() methods. These methods do exactly what they say they do, hide or show HTML elements. In their essence, these methods are simply modifying the display style property of elements. So, we can use simple style modification to achieve the same thing.

To hide an element:

document.querySelector('.test-element').style.display = 'none';

To show a hidden element

document.querySelector('.test-element').style.display = 'block';

AJAX

One of my favorite and most used jQuery functionality is sending HTTP requests using AJAX. However, we have to understand that jQuery.ajax() is a wrapper around existing JavaScript functionality. There is no doubt that jQuery makes using AJAX a breeze, but it’s not why we’re here, is it?

To send a POST request to the URL ‘home/sendmessage’ with the data {id: 23, name: ‘faisal’}, we’d do the following:

let request = new XMLHttpRequest();

// Set a event handler when the status of the request changes
request.onreadystatechange = function(response) {
    if (request.readystate == 4 && request.status == 200) {
        // do stuff with the response object
    }
}

// Open a Post request to the url home/sendmessage asynchronously.
request.open('POST', 'home/sendmessage', true);

// Set the request header/s
request..setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

//Send parameterized data as part of the Post request
request.send('id=23&name=faisal');

That’s it. You post request will be sent to the server.

If you’re a jQuery developer, you will know that these aren’t all the methods and features that jQuery ships with. There are definitely more, but I’ve simply tried to address the most common ones just to give you an idea that life without jQuery in the browser is possible.

I hope that it’s enough to push you towards using more and more JavaScript.

How to Write an Unbeatable Tic-Tac-Toe in JavaScript

Writing small puzzle games is a nice way of improving your programming skills as those games tend to present you with challenge after challenge and force you to really put tremendous thought into writing your code. 

One such game is the classic Tic-Tac-Toe.

In this little walk-through (read long and descriptive) about writing an unbeatable tic-tac-toe, I will be using Vanilla JavaScript for logic and HTML/CSS for UI. I won’t be concentrating much on UI part of it, because, duh! 

Another little disclaimer – we will be concentrating on logic primarily and code will come second to that. Again, if you want to skip the “walk-through” and jump right to the code, I have a public repository on GitHub at https://github.com/FaisalST32/tic-tac-toe. I also have a live version of the game available at https://games.faisalrashid.online/tictactoe/

So, let’s dive in.

First of all you need to create the UI. Just use simple HTML to create 9 input boxes, or divs even to hold your X’s and O’s. The id’s of these input boxes are important as they will be our reference to the X’s and O’s. To keep it consistent with the positioning of our boxes, I have given the input boxes the id’s “0-0” to “2-2”. To visualize: 

0-00-10-2
1-01-11-2
2-02-12-2


This will give us an easy to use 3×3 matrix. You can design your UI whatever way you want. It should simply employ this form of queryable HTML elements. 

Here’s a screenshot of my UI. Simple boxes with a little gloss of CSS.

Now, the logic! First of all, whenever a user, or a participant in this case, clicks or taps an empty box, the value of that box should change to either an X or an O, depending upon whose turn it is.


Implementing that is simple. I use a global variable to hold the ‘currentPlayer‘. This variable has a value of either X or O. Then I simply write a method to toggle current player, whenever a box is tapped. I call the method changePlayer.

All this is started by tapping a box. So, we need a function to get us started. We write a function that marks the box with X or O and changes player. We call the function onCheckBox. So far, the function checks a box and and then changes player.

Now, while the boxes are being tapped, we need to keep track of the checked boxes. Using an array should help us get this job done. Simply holding the id’s of all the boxes is useful, but it’s not enough. We will definitely need to hold the id’s (co-ordinates in this case) as well as the player who checked the box. So, our checkedBoxes array will be an array of objects of this form:

[{box: ‘0-1’, player: ‘X’}, {box: ‘2-1’, player: ‘O’}, …]


We now have two global variables – one to hold currentPlayer and other to hold checkedBoxes.

Another thing that I like to keep track of is the Turn Count. I can get that from the chekedBoxes array, but this way it’s simpler and very useful if I’m manipulating the checkedBoxes array.

Since we will be writing a Player vs CPU mode only (github repo contains both 1p and 2p modes), these are all the global variables that we are going to need – checkedBoxescurrentPlayer and turnCount.

NOTE: It’s always good practice to keep your global variables at a minimum to avoid dependencies and unexpected behavior within functions.

I believe our onCheckBox method is now ready. This is what it looks like

function onCheckBox(element) {
   checkedBoxes.push({ box: element.id, player: currentPlayer });
   checkElement(element);
   turnCount++;
   var   gameStatus =checkWinner();   //Will come to this method in a bit
    switchPlayer();
}function checkElement(element){
     element.value = currentPlayer;     
     element.disabled ="disabled";
}function switchPlayer() {
    currentPlayer = currentPlayer =="X"?"O":"X";
    document.querySelector('.current-player').textContent = currentPlayer;
}

We are passing the clicked Html element as one of the parameters to the method to access it’s id for coordinates. Everytime we check a box we need to check whether there is a winner or whether the game is drawn.

This brings us to our the checkWinner method. This method simply checks whether the X’s or O’s are in a single line (rules of tic-tac-toe) to determine whether there is a winner. It’s not very difficult to build that logic. We query the checkedBoxes array and find all the coordinates with player X. If the coordinates follow the pattern (a-0, a-1, a-2) or (0-a, 1-a, 2-a) where a = 0 or 1 or 2, then we have a horizontal or vertical line checked. Well, diagonal lines are a little tricky. For right to left diagonal, we use (a-a, b-b, c-c) and for left to right diagonal we use (0-2, 1-1, 2-0). If we find any of these patterns in the checkedBox matrix then the ‘player’ having the pattern is the winner. If none of these patterns match, then we simply check whether one of the two players has checked 5 boxes to determine that it’s a draw. 

function checkWinner(isCheckOnly=false) {
if (currentPlayer =="X") {
//Get all the boxes marked by Player X
   var  xs = checkedBoxes.filter(item=> item.player =="X").map(value=> {
     return { x: Number(value.box.split("-")[0]), y: Number(value.box.split("-")[1]) }
});
returncalculateScore(xs);
}
else if (currentPlayer =="O") {
//Get all the boxes marked by Player O
   var  os = checkedBoxes.filter(item=> item.player =="O").map(value=> {
     return { x: Number(value.box.split("-")[0]), y: Number(value.box.split("-")[1]) }
});
returncalculateScore(os);
}
}function calculateScore(positions) {
//Check right diagonalif (positions.filter(i=> { return i.x == i.y }).length ==3) {
              return'game over';
}//Check Left diagonal
if (positions.filter(i=> { return (i.x ==0&& i.y ==2) 
                                    || (i.x ==1&& i.y ==1)
                                    || (i.x ==2&& i.y ==0) }).length ==3) {
                return'game over';
}
//check horizontal match
for (var i =0; i <3; i++) {
  var  consecutiveHorizontal = positions.filter(p=> p.x == i);
  if (consecutiveHorizontal.length ==3) {
       return'game over';
}
//check vertical match
  var  consecutiveVertical = positions.filter(p=> p.y == i);
  if (consecutiveVertical.length ==3) {
        return'game over';
}
}
//check draw
if (positions.length ==5) {
  return'game drawn';
}
//if none of the conditions match 'game on'
return'game on';
}

Here I’m delegating the responsibility of finding result to calculateScore method. These two methods are essentially translating what we said above to JavaScript code. Nothing fancy here.

Let’s try something now. Shall we? I believe our game is now ready to handle all the tic-tac-toe logic. What remains is making it unbeatable. To make the game unbeatable, I want to set some ground rules to avoid making this post excruciatingly long (you’re kidding, right). The CPU will always play second and will be primarily focused on not letting the opponent win. So, our onCheckBoxmethod will see the addition of the method computerPlays for CPU’s turn. 

function onCheckBox(element) {
checkedBoxes.push({ box: element.id, player:
    currentPlayer });
  checkElement(element);
turnCount++;
  var
     gameStatus = checkWinner();
  switchPlayer();
  if (turnCount %2 == 1&&
    gameStatus == 'game on'){
    computerPlays();
}
}

As you can see the computerPlays method will only be called if the turnCount is odd, ie, it’s 2nd, 4th, 6th or 8th turn, and if gameStatus returned by the checkWinner method is ‘game on’ as there is no point in continuing the game, if the game is already over. To understand how to counter a human move, I came up with 5 cases. These five cases in the order of their priority are: 

  1. First Move
  2. Finishing Move that allows CPU to win the game.
  3. Saving Move that allows CPU to save the game.
  4. Predicting a move that will trap the CPU and avoiding it.
  5. And if none of these fit, a Random move.

Here’s a sneak preview of our computerPlays method.

function computerPlays() {
  var  nextBoxCoords;
  if(turnCount ==1){
nextBoxCoords =computeFirstMove();
}
  if (!nextBoxCoords){
nextBoxCoords =computeFinishingMove();
}  if (!nextBoxCoords) {
nextBoxCoords =computeSavingMove();
}
  if (!nextBoxCoords)
nextBoxCoords =predictTrappingMove();
  if (!nextBoxCoords) {
nextBoxCoords =computeRandomMove();
}
  var  nextBox = document.querySelector(`[id='${nextBoxCoords}']`);
  onCheckBox(nextBox);
}

1. First Move

Computing first move is out of experience, although, I could have written an algorithm for it. But to avoid complexity, I just went with past experience. If it’s CPU’s first move, we need to find where the opponent played. If it’s center box, we return a corner box. Similarly, if he played a corner box, we return an edge box adjacent to it and finally if he played an edge box, we return the center box. By the way, 1-1 is the center box, 0-1, 1-0, 1-2, 2-1 are the edge boxes and, you guessed it, 0-0, 0-2, 2-0, 2-2 are the corner boxes.

Here it is,

function computeFirstMove(){
  var  playedMove = checkedBoxes.map(b=> b.box)[0];
  var  edgeMoves = ['0-1', '1-0', '1-2', '2-1'];
  var  cornerMoves = ['0-0', '0-2', '2-0', '2-2'];
  var  centerMove = ['1-1'];
  if(edgeMoves.find(m=> m == playedMove))
    returnedgeMoveResponse(playedMove);
  else if(cornerMoves.find(m=> m == playedMove))
    return'1-1';
  else if(centerMove.find(m=> m == playedMove))
    return cornerMoves[Math.floor(Math.random()*cornerMoves.length)];
}function edgeMoveResponse(playedMove){
  if(playedMove =='1-2') 
    return'0-2';
  else if (playedMove =="0-1") 
    return"0-0";
  else if (playedMove =="1-0") 
   return"2-0";
  else if(playedMove =='2-1') 
    return'2-0';
}

To avoid cramming up the computeFirstMove method, I moved out the edgeMoveResponse method. Now, that we got the first move sorted, let’s move to the next check – computeFinishingMove

2. Finishing Move that allows CPU to win the game.

If the opponent is there for the taking we would prioritize taking him. Let’s say we have the following situation: 

Since, it’s CPU’s turn (O) we would definitely want to check the lower-right box. That’s where our computeFinishingMove method comes in. It simply iterates over all the remaining boxes and checks them one by one. It then calls the checkWinner method on each iteration and if there’s a winner, just returns the box of that iteration as the ‘nextBoxCoords‘ to the computerPlays method. Here’s the code: 

function computeFinishingMove() {
  var  remainingMoves =getRemainingMoves();
  var  finishingMoveCoords;
  for (var move of remainingMoves) {
    checkedBoxes.push({ box: move, player: currentPlayer });
    var  nextBox = document.querySelector(`[id='${move}']`)
    if (checkWinner() =='game over') {
finishingMoveCoords = move;
      onUncheckBox(nextBox, true);
      break;
}
    onUncheckBox(nextBox, true);
}
  if(finishingMoveCoords){
    console.log('Playing Finishing Move')
    return finishingMoveCoords;
}
  else{
    return'';
}
}function getRemainingMoves() {
  var  allMoves = ['0-0', '0-1', '0-2',
                  '1-0', '1-1', '1-2',
                  '2-0', '2-1', '2-2',]
  var  playedMoves = checkedBoxes.map(b=> b.box);
  return allMoves.filter(m=>!playedMoves.find(move=> move == m));
}
function onUncheckBox(elementisImplicit=false) {
checkedBoxes = checkedBoxes.filter(b=> b.box != element.id);
  if (!isImplicit) {
element.value ='';
element.removeAttribute("disabled");
turnCount--;
     switchPlayer();
}
}

While checking a box to check for winner, we have to make sure that we uncheck that box after the iteration completes to avoid bad data in checkedBoxes array. For that matter, we employ the onUncheckBox method above. If there is no ‘finishing move’ available, we check for the next condition – computeSavingMove. 

3. Saving Move that allows CPU to save the game.

Consider the following scenario: 

Here it’s CPU’s (O) turn to play. Clearly playing anywhere other than top middle box will cause the game to end in the next turn. So, we have to definitely check that box. To do this, we write a method similar to previous method where we this time iterate over all the remaining boxes and during each iteration fill them with opponent’s mark and then call the checkWinner method. If it returns ‘game over’ in any of the iterations then that is our desired box. Here’s what this translates to in JavaScript. 

function computeSavingMove() {  var  remainingMoves =getRemainingMoves();  switchPlayer();  var  savingMoveCoords;  for (var  move of remainingMoves) {          checkedBoxes.push({ box: move, player: currentPlayer });         var nextBox = document.querySelector(`[id='${move}']`)         if (checkWinner() =='game over') {                         savingMoveCoords = move;                         onUncheckBox(nextBox, true);                         break;}    onUncheckBox(nextBox, true);}    switchPlayer();   if(savingMoveCoords){      console.log('Playing Saving Move')      return savingMoveCoords;}}

You might argue that this is a clear violation of the DRY (Don’t Repeat Yourself) principle. But, sometimes, to keep the code readable and avoid complexities, you may want to take a rain check on the DRY. To make sure that I’m iterating with opponent’s move and note CPU’s, I’m calling the switchPlayer method before iterating over remainingMoves. If none of the remaining moves is a saving move we then turn our attention to the trickiest condition – predictTrappingMove

4. Predicting a move that will trap the CPU and avoiding it.

A ‘Trapping Move’, as I like to call it is when the opponent checks a box that leaves you with two Finishing boxes to fill, ultimately leading you to lose the game. Here’s a screenshot to show exactly that: 

In the image, it’s Player O’s turn and he is in a lose-lose situation. If he check tile 0-0, Player’s X will check 2-2 to win the game and vice versa. So, the only way out of this situation is to avoid it. That’s where our next method, predictTrappingMove comes in.

This method isn’t complicated in terms of execution but the planning needs to be spot on. So, what we do is, every time the opponent plays a move, we check for the possibility of a trapping move, the next time he plays. To achieve this, we will have to one by one check every box with our move. On each turn, we will then have to check each remaining box with the opponents move. Then, we check whether more than one winning move was created as a result of that. I know, I know, what the hell, right? Okay let’s break it down. Suppose we are in this situation: 


The opponent is playing for O’s. If I, for instance, play 1-0, the opponent can easily play 0-2 and trap me like this. 


Since, there was no finishing or saving move available, I wasn’t able to detect this. To detect this, I will have to check every single box till I find one that’s safe. I can accomplish that by first checking box. Then I will play opponent’s turn on each of the remaining boxes.

This way, I can determine whether any of his moves will result in a trapping move. Any of my turns that I don’t find a trapping move anywhere, will be my next move. For some turns, it won’t be possible for the opponent to play all the available boxes, eg, a turn where he has to play a saving move. In those cases, I won’t be iterating over all his possible moves, but just the saving ones. I’ll be making the assumption that the opponent will try to save himself if I play a move that forces him to do so. Okay, this is how it all translates to JavaScript. 

function predictTrappingMove() {
  var  checkedBoxesBackup = checkedBoxes.slice();
  var  remainingMoves =getRemainingMoves();
  var  nextMove;
  var  moveFound;
  for(var move of remainingMoves){
checkedBoxes.push({box: move, player: currentPlayer})
    switchPlayer();
//Check if the opponent needs to play a saving move
    var savingMove =  computeSavingMove();
    if(savingMove){
checkedBoxes.push({box: savingMove, player: currentPlayer});
      if(checkTrap() =='no trap'){
checkedBoxes.pop();
        switchPlayer();
nextMove = move;
        break;
}
checkedBoxes.pop();
      switchPlayer();
     continue;
}
//If no saving move is required, check each position  
 else{
     switchPlayer();
     for(var opponentMove ofgetRemainingMoves()){
       switchPlayer();
       moveFound =true;
       checkedBoxes.push({box: opponentMove, player: currentPlayer});
       if(checkTrap() =='trapped'){
         moveFound =false;
         checkedBoxes.pop();
         switchPlayer();
         break;
       }
         checkedBoxes.pop();
         switchPlayer();
}
}
checkedBoxes.pop();
    if(moveFound){
nextMove = move;
      break;
}
}checkedBoxes = checkedBoxesBackup;
  return nextMove;
}
function checkTrap(){
  var  boxes =getRemainingMoves();
  var  winningMoveCount =0;
  for(var  freeMove of boxes){
checkedBoxes.push({box: freeMove, player: currentPlayer});
    var result = checkWinner(true);
    if(result =='game over')
winningMoveCount++
checkedBoxes.pop();
}
  if(winningMoveCount >1){
    return 'trapped';
}
  else{
    return 'no trap';
}
}

Here, predictTrappingMove is first checking a box on my behalf. Then, it’s checking whether the opponent needs to play a saving move to counter that. If yes, it’s calling the checkTrap method to see whether more than one saving conditions were created on that opponent move.

If not, that is the move that will be returned and played. If that’s not the case, however, and the opponent’s saving move created a trap for us, we need to move forward and check the remaining boxes for ourselves, and then in each turn check the remaining boxes on the opponent’s behalf and call the checkTrap method each time. If any of the boxes creates a ‘trapping move’, we avoid that move and keep iterating over remaining moves, till we find a safe move to play.

It may be a little confusing still, but if you go over the code a few times, it should all be clear. 

5. Random move

I wrote the computeRandomMove in the first versions of the game to calculate a move if all checks don’t return a move. However, it’s not relevant now as checkTrappingMove will always return a move, irrespective of everything. Here’s the code still if you need it. 

function computeRandomMove() {  var  remainingMoves =getRemainingMoves();  return remainingMoves[Math.floor(Math.random()*remainingMoves.length)]}

It’s simply leveraging the Math.random() method of JavaScript and returning a box based on that from the remainingMoves. So, that’s it. You’re ready to create your unbeatable tic-tac-toe. You can go ahead and write your own with the same principles or even improve over these.

You can even try to write a method that forcesTrappingMove on the opponent making your player a little more aggressive. If you got this far, thanks for your patience. Till next time!

TL;DR Writing code to create an unbeatable tic-tac-toe is simple, but coming up with the logic is what matters.