Simple Vanilla Javascript Tic Tac Toe (Free Download)

Welcome to a tutorial on how to create a simple Tic Tac Toe game in pure Javascript, HTML, and CSS. Tic Tac Toe is another classic that is perfect for beginners to get their hands on creating games – There is not a lot of sound and graphics to worry about, but beware, this one does require a tad bit of artificial intelligence.

As there are already quite a number of other tutorials out there, I shall keep this one as simple as possible, to the bare necessities with no third-party frameworks required. Read on!

ⓘ I have included a zip file with all the example source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

TABLE OF CONTENTS

Download & Demo How It Works Useful Bits & Links
The End

 

DOWNLOAD & DEMO

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

EXAMPLE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

JAVASCRIPT TIC-TAC-TOE DEMO

 

 

TIC-TAC-TOE MECHANICS

All right, let us now get into some details on how the Javascript Tic-Tac-Toe works.

 

PART 1) HTML INTERFACE

ttt.html
<div id="ttt-game"></div>

That’s right, there is only a single <div id="ttt-game">. The rest of the squares will be generated by Javascript.

 

PART 2) BASIC JAVASCRIPT MECHANICS

ttt.js
var ttt = {
  board : [], // array to hold the current game
};
  • For a start, var ttt holds all the necessary game mechanics.
  • ttt.board is an array with 9 “slots” to represent the squares on the Tic Tac Toe grid. I.E. ttt.board[0] is the top left square, ttt.board[1] is the top center, ttt.board[2] is the top right, ttt.board[3] is the center-left, etc…
  • Initially, all 9 slots of ttt.board will have a value of null.
  • If the player selects a square, the corresponding value in ttt.board will be changed to 0. For example ttt.board[4] = 0 if the player chooses the center square.
  • If the computer selects a square, the corresponding value in ttt.board will be changed to 1.

 

PART 3) GAME INITIALIZE & RESET

ttt.js
// (A) RESET THE GAME
reset : () => {
  let container = document.getElementById("ttt-game");
  container.innerHTML = "";
  ttt.board = [];
  for (let id=0; id<9; id++) {
    let square = document.createElement("div");
    square.innerHTML = "&nbsp;";
    square.id = "ttt-" + id;
    square.onclick = () => { ttt.play(id); };
    container.appendChild(square);
    ttt.board.push(null);
  }
},
window.addEventListener("load", ttt.reset);

Moving forward, ttt.reset() is used to reset and initialize the game. This is very straightforward and does only 2 things.

  • It resets ttt.board into an array with 9 null slots.
  • Redraws the HTML squares, each square is simply a <div id="ttt-N" onclick="ttt.play(N)">.

 

 

PART 4) PLAY!

ttt.js
// (B) HELPER FUNCTION - SET "O" OR "X" ON A SQUARE
// PLAYER IS "O", COMPUTER IS "X"
// id : target square
// by : 0 for player, 1 for computer
set : (id, by) => {
  let square = document.getElementById("ttt-"+id);
  square.innerHTML = by==0 ? "O" : "X" ;
  square.classList.add((by==0 ? "player" : "computer"));
  square.onclick = "";
  ttt.board[id] = by;
},
 
// (C) PLAY - WHEN THE PLAYER SELECTS A SQUARE
// id : id of chosen square
play : (id) => {
  // (C1) PLAYER'S MOVE - MARK SQUARE WITH "O"
  ttt.set(id, 0);
 
  // (C2) NO MORE MOVES AVAILABLE - NO WINNER
  if (ttt.board.indexOf(null) == -1) {
    alert("No winner");
    ttt.reset();
  }

  // (C3) COMPUTER'S MOVE - MARK SQUARE WITH "X"
  else { ttt.set(ttt.dumbAI(), 1); }
  // else { ttt.set(ttt.notBadAI(), 1); } // USE NOT BAD AI IF YOU WANT
 
  // (C4) WHO WON?
  win = null;
 
  // (C4-1) HORIZONTAL ROW CHECKS
  for (let i=0; i<9; i+=3) {
    if (ttt.board[i]!=null && ttt.board[i+1]!=null && ttt.board[i+2]!=null) {
      if ((ttt.board[i] == ttt.board[i+1]) && (ttt.board[i+1] == ttt.board[i+2])) { win = ttt.board[i]; }
    }
    if (win !== null) { break; }
  }
 
  // (C4-2) VERTICAL ROW CHECKS
  if (win === null) {
    for (let i=0; i<3; i++) {
      if (ttt.board[i]!=null && ttt.board[i+3]!=null && ttt.board[i+6]!=null) {
        if ((ttt.board[i] == ttt.board[i+3]) && (ttt.board[i+3] == ttt.board[i+6])) { win = ttt.board[i]; }
        if (win !== null) { break; }
      }
    }
  }
 
  // (C4-3) DIAGONAL ROW CHECKS
  if (win === null) {
    if (ttt.board[0]!=null && ttt.board[4]!=null && ttt.board[8]!=null) {
      if ((ttt.board[0] == ttt.board[4]) && (ttt.board[4] == ttt.board[8])) { win = ttt.board[4]; }
    }
  }
  if (win === null) {
    if (ttt.board[2]!=null && ttt.board[4]!=null && ttt.board[6]!=null) {
      if ((ttt.board[2] == ttt.board[4]) && (ttt.board[4] == ttt.board[6])) { win = ttt.board[4]; }
    }
  }
 
  // (C4-4) WE HAVE A WINNER
  if (win !== null) {
    alert("WINNER - " + (win==0 ? "Player" : "Computer"));
    ttt.reset();
  }
},

This is probably the most confusing part of the entire script.

  • (B) ttt.set() is a helper function to set “O” or “X” on a selected square, also update ttt.board.
  • (C) play() is fired when the player selects an open square, long story short:
    • (C1) Set the player’s selected square to “O”.
    • (C2) If there are no more empty squares and no winners, it’s a draw.
    • (C3) Computer makes a move after the player. See “dumb AI” and “not bad AI” below.
    • (C4) Check if there are any winners.

 

 

PART 5) DUMB AI

ttt.js
// (D) DUMB COMPUTER AI, RANDOMLY CHOOSES AN OPEN SLOT
dumbAI : () => {
  let open = [];
  for (let i=0; i<9; i++) { if (ttt.board[i] === null) { open.push(i); }}
  return open[(Math.floor(Math.random() * (open.length-1)))];
},

So, just how do we create an AI computer move for Tic Tac Toe? This is one of the simplest… and dumbest ways.

  • Extract all the slots that are still empty in ttt.board.
  • Then randomly choose an empty slot.

Yep, it works.

 

PART 6) A SLIGHTLY BETTER COMPUTER AI

ttt.js
// (E) AI WITH A LITTLE MORE INTELLIGENCE
notBadAI : () => {
  // (E1) HELPER FUNCTION, CHECK POSSIBLE WINNING ROW
  // first : first square number
  // direction : "R"ow, "C"ol, "D"iagonal
  // pc : 0 for player, 1 for computer
  let check = (first, direction, pc) => {
    let second = 0, third = 0;
    if (direction=="R") {
      second = first + 1;
      third = first + 2;
    } else if (direction=="C") {
      second = first + 3;
      third = first + 6;
    } else {
      second = 4;
      third = first==0 ? 8 : 6;
    }

    if (ttt.board[first]==null && ttt.board[second]==pc && ttt.board[third]==pc) {
      return first;
    } else if (ttt.board[first]==pc && ttt.board[second]==null && ttt.board[third]==pc) {
      return second;
    } else if (ttt.board[first]==pc && ttt.board[second]==pc && ttt.board[third]==null) {
      return third;
    }
    return null;
  };

  // (E2) PRIORITY #1 - GO FOR THE WIN
  let move = null;
 
  // (E2-1) CHECK HORIZONTAL ROWS
  for (let i=0; i<9; i+=3) {
    move = check(i, "R", 1);
    if (move!==null) { return move; }
  }
 
  // (E2-2) CHECK VERTICAL COLUMNS
  for (let i=0; i<3; i++) {
    move = check(i, "C", 1);
    if (move!==null) { return move; }
  }
 
  // (E2-3) CHECK DIAGONAL
  move = check(0, "D", 1); if (move!==null) { return move; }
  move = check(2, "D", 1); if (move!==null) { return move; }}

  // (E3) PRIORITY #2 - BLOCK PLAYER FROM WINNING
  // (E3-1) CHECK HORIZONTAL ROWS
  for (let i=0; i<9; i+=3) {
    move = check(i, "R", 0);
    if (move!==null) { return move; }
  }
 
  // (E3-2) CHECK VERTICAL COLUMNS
  for (let i=0; i<3; i++) {
    move = check(i, "C", 0);
    if (move!==null) { return move; }
  }
 
  // (E3-3) CHECK DIAGONAL
  move = check(0, "D", 0); if (move!==null) { return move; }
  move = check(2, "D", 0); if (move!==null) { return move; }
 
  // (E4) RANDOM MOVE IF NOTHING
  return ttt.dumbAI();
}

At this stage, the game is complete. But the AI is still pretty dumb, and let us give it some more intelligence.

  • This version may look difficult, but all it does is essentially check for 2 consecutive squares and attempt to plug in the third one (to win the game).
  • If there are no squares to “plug”, it reverts to using dumbAI() to randomly select an open square.

 

 

USEFUL BITS & LINKS

That’s all for this project, and here is a small section on some extras and links that may be useful to you.

 

HOW TO PLAY TIC-TAC-TOE

Not going to judge the people who don’t know… Or forgot how this works.

  • Tic Tac Toe is a simple 2 player game that is played on a grid of 9 squares.
  • Each player will take turns to choose a square. One player will mark the selected square with “X” and the other with “O”.
  • The first player to have 3 selected squares in a row will win the game – Either horizontally, vertically, or diagonally.

 

STILL PRETTY DUMB

Congratulations, you have now created a pretty good fully functioning simple game with AI. But of course, it is not the most clever. So for some homework, see if you can further improve on notBadAI() – Make it check for 2 empty squares and try more predictions.

 

2 PLAYERS?

Want to change this to a 2 players game instead? It should be quite easy.

  • Add another flag to var ttt to indicate which player’s turn.
  • Update the play() function – Remove the computer move part, and check for the player turn instead.

Yep, that should pretty much turn it into a 2 player game.

 

 

COMPATIBILITY CHECKS

This example will work on all modern “Grade A” browsers.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you in your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

8 thoughts on “Simple Vanilla Javascript Tic Tac Toe (Free Download)”

  1. Hey! Question, what if I wanted to add the option to chose to be player x or o in the game? How could i do that? Great tutorial by the way super easy to follow 🙂

    1. Quick and dirty way:

      1) Create a new ttt.symbol flag.
      2) At the start of the game, let the player choose “X” or “O”. Save into ttt.symbol.
      3) Modify play() to use ttt.symbol instead of fixed “O”.

      1. Okay thank you so much! I’m new to learning the language and how to write things out so I’m confused on how I would write ttt.symbol flag, any further help would be really appreciated.

  2. Great article for learning functional Javascript and simple game logic. Question: how do I start the game? When I fire ttt.reset(), the game board appears on the DOM. I click on a square but it goes right to ‘There’s no winner’.

      1. The board doesn’t load by default. Do I need to add `document.addEventListener(‘DOMContentLoaded’) ?

Leave a Comment

Your email address will not be published.