Learn how to create your own Tic Tac Toe game using HTML, CSS, and JavaScript with this step-by-step tutorial. By the end, you'll have a fully functional game to share with your friends and family.
Table of Contents
Tic Tac Toe is a classic two-player game that is easy to learn and lots of fun to play. In this tutorial, we will walk you through the process of creating a Tic Tac Toe game from scratch using these three technologies.
First, we will cover the basics of HTML and CSS, including how to structure and style a web page. Then, we will delve into the world of JavaScript and learn how to use it to add interactivity to our game. By the end of this tutorial, you will have a fully functional Tic Tac Toe game that you can play in your web browser.
So let's start making an amazing Tic Tac Toe game Using HTML, CSS and JavaScript step by step.
Source Code
Step 1 (HTML Code):
To get started, we will first need to create a basic HTML file. The HTML code provided below sets up the structure and layout of a Tic Tac Toe game. It includes a <table> element with a 3x3 grid of cells, which represent the spaces on the game board where players can place their markers (X's or O's). Each cell is contained within a <div> element, which is itself contained within a <td> element (table cell).
The <table> element also includes a row for the score table, which tracks the number of wins for each player and the number of draws. The score table uses <th> (table header) and <td> elements to create the cells for the table.
The code also includes several <div> elements with the class "modal", which are used to create modal dialogs. Modal dialogs are windows that appear on top of the current page and are used to display messages or ask the user for input. In this case, the modal dialogs are used to announce the winner of the game, get feedback from the user, and present options to the user.
Finally, the code includes links to a CSS stylesheet and a Google font, which are used to style the page and add some visual flair to the game. The <link> element with the rel attribute set to "stylesheet" is used to link to a stylesheet, and the <link> element with the href attribute set to the URL of a Google font is used to link to a font file.
After creating the files just paste the following codes into your file. Remember that you must save a file with the .html extension.
Step 2 (CSS Code):
Next, we will create our CSS file. In this file, we will use some basic CSS rules to create our tic tac toe. The CSS code provided below styles the Tic Tac Toe game. It sets the background color and font of the page, style the game grid and score table, and defines the styles for the modal dialogs.
The CSS code uses several selectors, such as body, h1, table, and .td_game, to specify which elements the styles should be applied to. It also uses several properties, such as color, font-family, and border, to specify the values for these styles.
For example, the body selector is used to set the background color and font of the page. The h1 selector is used to style the text of the page title, and the table selector is used to style the game grid and score table. The .td_game class selector is used to style the cells of the game grid, and the .fixed class selector is used to style the individual cells within the grid.
The CSS code also includes several class selectors for the modal dialogs, such as .modal, .modal-content, and .close, which are used to style the appearance of the dialogs and the elements within them.
This will give our tic tac toe game an upgraded presentation. Create a CSS file with the name of styles.css and paste the given codes into your CSS file. Remember that you must create a file with the .css extension.
body {
background: rgb(2,0,36);
background: linear-gradient(60deg, rgba(2,0,36,1) 50%, rgba(9,9,121,1) 75%, rgba(0,212,255,1) 100%);
color: rgb(230, 230, 230);
text-align: center;
font-family: 'Indie Flower', 'Comic Sans', cursive;
font-size: 0.7em;
}
h1 {
line-height: 1em;
margin-bottom: 0;
padding-bottom: 5px;
font-size: 2.8em;
font-weight: bold;
}
h2 {
font-size: 1.3em;
font-weight: bold;
padding: 0;
margin: 0;
}
h3 {
font-size: 1.1em;
text-decoration: underline;
text-decoration-style: dashed;
padding: 0;
margin: 10px 0 2px 0;
}
table {
margin: 2% auto;
border-collapse: collapse;
}
#table_game {
position: relative;
font-size: 120px;
margin: 1% auto;
border-collapse: collapse;
}
.td_game {
border: 4px solid rgb(230, 230, 230);
width: 90px;
height: 90px;
padding: 0;
vertical-align: middle;
text-align: center;
}
.fixed {
width: 90px;
height: 90px;
line-height: 90px;
display: block;
overflow: hidden;
cursor: pointer;
}
.td_list {
text-align: center;
font-size: 1.3em;
font-weight: bold;
}
.th_list {
font-size: 1.3em;
font-weight: bold;
text-align: center;
text-decoration: underline;
}
#restart {
font-size: 3em;
width: 1em;
height: 0.9em;
cursor: pointer;
margin: 0 auto;
overflow: hidden;
}
.x {
color: rgb(245, 65, 4);
position: relative;
top: -8px;
font-size: 1.2em;
cursor: default;
}
.o {
color: rgb(4, 247, 166);
position: relative;
top: -7px;
font-size: 1.0em;
cursor: default;
}
/* modal background */
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto; /* enable scroll if needed */
background: rgb(2,0,36);
background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 48%, rgba(0,212,255,1) 100%);
}
/* modal content */
.modal-content {
background-color: rgb(240, 240, 240);
color: rgb(32, 32, 32);
font-size: 2em;
font-weight: bold;
/* 16 % from the top and centered */
margin: 16% auto;
padding: 20px;
border: 2px solid black;
border-radius: 10px;
width: 380px;
max-width: 80%;
}
.modal-content p {
margin: 0;
padding: 0;
}
/* close button for modal dialog */
.close {
color: rgb(170, 170, 170);
float: right;
position: relative;
top: -25px;
right: -10px;
font-size: 34px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.win-color {
background-color: rgb(240, 240, 240);
}
Step 3 (JavaScript Code):
Finally, we need to create a function in JavaScript. This JavaScript code is defining several functions and variables for a Tic Tac Toe game. The Grid object is used to represent the state of the game grid, with its cells property being an array of 9 elements representing each cell of the grid. The Grid object has several methods, such as getFreeCellIndices, getRowValues, getRowIndices, getColumnValues, and getColumnIndices, which are used to get various pieces of information about the state of the grid. There are also functions such as sumArray, isInArray, shuffleArray, and intRandom that are used for various utility purposes, such as summing up the values in an array, checking if an element is in an array, shuffling the elements in an array randomly, and generating a random integer within a given range. Additionally, there are several variables defined for keeping track of the game state, such as the number of moves made, the winner of the game, the player and computer's symbol (either "X" or "O"), whose turn it is, and the score for each player and for draws.
Create a JavaScript file with the name of script.js and paste the given codes into your JavaScript file. Remember, you’ve to create a file with .js extension.
"use strict";
// Bind Esc key to closing the modal dialog
document.onkeypress = function (evt) {
evt = evt || window.event;
var modal = document.getElementsByClassName("modal")[0];
if (evt.keyCode === 27) {
modal.style.display = "none";
}
};
// When the user clicks anywhere outside of the modal dialog, close it
window.onclick = function (evt) {
var modal = document.getElementsByClassName("modal")[0];
if (evt.target === modal) {
modal.style.display = "none";
}
};
//==================================
// HELPER FUNCTIONS
//==================================
function sumArray(array) {
var sum = 0,
i = 0;
for (i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
function isInArray(element, array) {
if (array.indexOf(element) > -1) {
return true;
}
return false;
}
function shuffleArray(array) {
var counter = array.length,
temp,
index;
while (counter > 0) {
index = Math.floor(Math.random() * counter);
counter--;
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
}
function intRandom(min, max) {
var rand = min + Math.random() * (max + 1 - min);
return Math.floor(rand);
}
// GLOBAL VARIABLES
var moves = 0,
winner = 0,
x = 1,
o = 3,
player = x,
computer = o,
whoseTurn = x,
gameOver = false,
score = {
ties: 0,
player: 0,
computer: 0
},
xText = "×",
oText = "o",
playerText = xText,
computerText = oText,
difficulty = 1,
myGrid = null;
//==================================
// GRID OBJECT
//==================================
// Grid constructor
//=================
function Grid() {
this.cells = new Array(9);
}
// Grid methods
//=============
// Get free cells in an array.
// Returns an array of indices in the original Grid.cells array, not the values
// of the array elements.
// Their values can be accessed as Grid.cells[index].
Grid.prototype.getFreeCellIndices = function () {
var i = 0,
resultArray = [];
for (i = 0; i < this.cells.length; i++) {
if (this.cells[i] === 0) {
resultArray.push(i);
}
}
// console.log("resultArray: " + resultArray.toString());
// debugger;
return resultArray;
};
// Get a row (accepts 0, 1, or 2 as argument).
// Returns the values of the elements.
Grid.prototype.getRowValues = function (index) {
if (index !== 0 && index !== 1 && index !== 2) {
console.error("Wrong arg for getRowValues!");
return undefined;
}
var i = index * 3;
return this.cells.slice(i, i + 3);
};
// Get a row (accepts 0, 1, or 2 as argument).
// Returns an array with the indices, not their values.
Grid.prototype.getRowIndices = function (index) {
if (index !== 0 && index !== 1 && index !== 2) {
console.error("Wrong arg for getRowIndices!");
return undefined;
}
var row = [];
index = index * 3;
row.push(index);
row.push(index + 1);
row.push(index + 2);
return row;
};
// get a column (values)
Grid.prototype.getColumnValues = function (index) {
if (index !== 0 && index !== 1 && index !== 2) {
console.error("Wrong arg for getColumnValues!");
return undefined;
}
var i, column = [];
for (i = index; i < this.cells.length; i += 3) {
column.push(this.cells[i]);
}
return column;
};
// get a column (indices)
Grid.prototype.getColumnIndices = function (index) {
if (index !== 0 && index !== 1 && index !== 2) {
console.error("Wrong arg for getColumnIndices!");
return undefined;
}
var i, column = [];
for (i = index; i < this.cells.length; i += 3) {
column.push(i);
}
return column;
};
// get diagonal cells
// arg 0: from top-left
// arg 1: from top-right
Grid.prototype.getDiagValues = function (arg) {
var cells = [];
if (arg !== 1 && arg !== 0) {
console.error("Wrong arg for getDiagValues!");
return undefined;
} else if (arg === 0) {
cells.push(this.cells[0]);
cells.push(this.cells[4]);
cells.push(this.cells[8]);
} else {
cells.push(this.cells[2]);
cells.push(this.cells[4]);
cells.push(this.cells[6]);
}
return cells;
};
// get diagonal cells
// arg 0: from top-left
// arg 1: from top-right
Grid.prototype.getDiagIndices = function (arg) {
if (arg !== 1 && arg !== 0) {
console.error("Wrong arg for getDiagIndices!");
return undefined;
} else if (arg === 0) {
return [0, 4, 8];
} else {
return [2, 4, 6];
}
};
// Get first index with two in a row (accepts computer or player as argument)
Grid.prototype.getFirstWithTwoInARow = function (agent) {
if (agent !== computer && agent !== player) {
console.error("Function getFirstWithTwoInARow accepts only player or computer as argument.");
return undefined;
}
var sum = agent * 2,
freeCells = shuffleArray(this.getFreeCellIndices());
for (var i = 0; i < freeCells.length; i++) {
for (var j = 0; j < 3; j++) {
var rowV = this.getRowValues(j);
var rowI = this.getRowIndices(j);
var colV = this.getColumnValues(j);
var colI = this.getColumnIndices(j);
if (sumArray(rowV) == sum && isInArray(freeCells[i], rowI)) {
return freeCells[i];
} else if (sumArray(colV) == sum && isInArray(freeCells[i], colI)) {
return freeCells[i];
}
}
for (j = 0; j < 2; j++) {
var diagV = this.getDiagValues(j);
var diagI = this.getDiagIndices(j);
if (sumArray(diagV) == sum && isInArray(freeCells[i], diagI)) {
return freeCells[i];
}
}
}
return false;
};
Grid.prototype.reset = function () {
for (var i = 0; i < this.cells.length; i++) {
this.cells[i] = 0;
}
return true;
};
//==================================
// MAIN FUNCTIONS
//==================================
// executed when the page loads
function initialize() {
myGrid = new Grid();
moves = 0;
winner = 0;
gameOver = false;
whoseTurn = player; // default, this may change
for (var i = 0; i <= myGrid.cells.length - 1; i++) {
myGrid.cells[i] = 0;
}
// setTimeout(assignRoles, 500);
setTimeout(showOptions, 500);
// debugger;
}
// Ask player if they want to play as X or O. X goes first.
function assignRoles() {
askUser("Do you want to go first?");
document.getElementById("yesBtn").addEventListener("click", makePlayerX);
document.getElementById("noBtn").addEventListener("click", makePlayerO);
}
function makePlayerX() {
player = x;
computer = o;
whoseTurn = player;
playerText = xText;
computerText = oText;
document.getElementById("userFeedback").style.display = "none";
document.getElementById("yesBtn").removeEventListener("click", makePlayerX);
document.getElementById("noBtn").removeEventListener("click", makePlayerO);
}
function makePlayerO() {
player = o;
computer = x;
whoseTurn = computer;
playerText = oText;
computerText = xText;
setTimeout(makeComputerMove, 400);
document.getElementById("userFeedback").style.display = "none";
document.getElementById("yesBtn").removeEventListener("click", makePlayerX);
document.getElementById("noBtn").removeEventListener("click", makePlayerO);
}
// executed when player clicks one of the table cells
function cellClicked(id) {
// The last character of the id corresponds to the numeric index in Grid.cells:
var idName = id.toString();
var cell = parseInt(idName[idName.length - 1]);
if (myGrid.cells[cell] > 0 || whoseTurn !== player || gameOver) {
// cell is already occupied or something else is wrong
return false;
}
moves += 1;
document.getElementById(id).innerHTML = playerText;
// randomize orientation (for looks only)
var rand = Math.random();
if (rand < 0.3) {
document.getElementById(id).style.transform = "rotate(180deg)";
} else if (rand > 0.6) {
document.getElementById(id).style.transform = "rotate(90deg)";
}
document.getElementById(id).style.cursor = "default";
myGrid.cells[cell] = player;
// Test if we have a winner:
if (moves >= 5) {
winner = checkWin();
}
if (winner === 0) {
whoseTurn = computer;
makeComputerMove();
}
return true;
}
// Executed when player hits restart button.
// ask should be true if we should ask users if they want to play as X or O
function restartGame(ask) {
if (moves > 0) {
var response = confirm("Are you sure you want to start over?");
if (response === false) {
return;
}
}
gameOver = false;
moves = 0;
winner = 0;
whoseTurn = x;
myGrid.reset();
for (var i = 0; i <= 8; i++) {
var id = "cell" + i.toString();
document.getElementById(id).innerHTML = "";
document.getElementById(id).style.cursor = "pointer";
document.getElementById(id).classList.remove("win-color");
}
if (ask === true) {
// setTimeout(assignRoles, 200);
setTimeout(showOptions, 200);
} else if (whoseTurn == computer) {
setTimeout(makeComputerMove, 800);
}
}
// The core logic of the game AI:
function makeComputerMove() {
// debugger;
if (gameOver) {
return false;
}
var cell = -1,
myArr = [],
corners = [0,2,6,8];
if (moves >= 3) {
cell = myGrid.getFirstWithTwoInARow(computer);
if (cell === false) {
cell = myGrid.getFirstWithTwoInARow(player);
}
if (cell === false) {
if (myGrid.cells[4] === 0 && difficulty == 1) {
cell = 4;
} else {
myArr = myGrid.getFreeCellIndices();
cell = myArr[intRandom(0, myArr.length - 1)];
}
}
// Avoid a catch-22 situation:
if (moves == 3 && myGrid.cells[4] == computer && player == x && difficulty == 1) {
if (myGrid.cells[7] == player && (myGrid.cells[0] == player || myGrid.cells[2] == player)) {
myArr = [6,8];
cell = myArr[intRandom(0,1)];
}
else if (myGrid.cells[5] == player && (myGrid.cells[0] == player || myGrid.cells[6] == player)) {
myArr = [2,8];
cell = myArr[intRandom(0,1)];
}
else if (myGrid.cells[3] == player && (myGrid.cells[2] == player || myGrid.cells[8] == player)) {
myArr = [0,6];
cell = myArr[intRandom(0,1)];
}
else if (myGrid.cells[1] == player && (myGrid.cells[6] == player || myGrid.cells[8] == player)) {
myArr = [0,2];
cell = myArr[intRandom(0,1)];
}
}
else if (moves == 3 && myGrid.cells[4] == player && player == x && difficulty == 1) {
if (myGrid.cells[2] == player && myGrid.cells[6] == computer) {
cell = 8;
}
else if (myGrid.cells[0] == player && myGrid.cells[8] == computer) {
cell = 6;
}
else if (myGrid.cells[8] == player && myGrid.cells[0] == computer) {
cell = 2;
}
else if (myGrid.cells[6] == player && myGrid.cells[2] == computer) {
cell = 0;
}
}
} else if (moves === 1 && myGrid.cells[4] == player && difficulty == 1) {
// if player is X and played center, play one of the corners
cell = corners[intRandom(0,3)];
} else if (moves === 2 && myGrid.cells[4] == player && computer == x && difficulty == 1) {
// if player is O and played center, take two opposite corners
if (myGrid.cells[0] == computer) {
cell = 8;
}
else if (myGrid.cells[2] == computer) {
cell = 6;
}
else if (myGrid.cells[6] == computer) {
cell = 2;
}
else if (myGrid.cells[8] == computer) {
cell = 0;
}
} else if (moves === 0 && intRandom(1,10) < 8) {
// if computer is X, start with one of the corners sometimes
cell = corners[intRandom(0,3)];
} else {
// choose the center of the board if possible
if (myGrid.cells[4] === 0 && difficulty == 1) {
cell = 4;
} else {
myArr = myGrid.getFreeCellIndices();
cell = myArr[intRandom(0, myArr.length - 1)];
}
}
var id = "cell" + cell.toString();
// console.log("computer chooses " + id);
document.getElementById(id).innerHTML = computerText;
document.getElementById(id).style.cursor = "default";
// randomize rotation of marks on the board to make them look
// as if they were handwritten
var rand = Math.random();
if (rand < 0.3) {
document.getElementById(id).style.transform = "rotate(180deg)";
} else if (rand > 0.6) {
document.getElementById(id).style.transform = "rotate(90deg)";
}
myGrid.cells[cell] = computer;
moves += 1;
if (moves >= 5) {
winner = checkWin();
}
if (winner === 0 && !gameOver) {
whoseTurn = player;
}
}
// Check if the game is over and determine winner
function checkWin() {
winner = 0;
// rows
for (var i = 0; i <= 2; i++) {
var row = myGrid.getRowValues(i);
if (row[0] > 0 && row[0] == row[1] && row[0] == row[2]) {
if (row[0] == computer) {
score.computer++;
winner = computer;
// console.log("computer wins");
} else {
score.player++;
winner = player;
// console.log("player wins");
}
// Give the winning row/column/diagonal a different bg-color
var tmpAr = myGrid.getRowIndices(i);
for (var j = 0; j < tmpAr.length; j++) {
var str = "cell" + tmpAr[j];
document.getElementById(str).classList.add("win-color");
}
setTimeout(endGame, 1000, winner);
return winner;
}
}
// columns
for (i = 0; i <= 2; i++) {
var col = myGrid.getColumnValues(i);
if (col[0] > 0 && col[0] == col[1] && col[0] == col[2]) {
if (col[0] == computer) {
score.computer++;
winner = computer;
// console.log("computer wins");
} else {
score.player++;
winner = player;
// console.log("player wins");
}
// Give the winning row/column/diagonal a different bg-color
var tmpAr = myGrid.getColumnIndices(i);
for (var j = 0; j < tmpAr.length; j++) {
var str = "cell" + tmpAr[j];
document.getElementById(str).classList.add("win-color");
}
setTimeout(endGame, 1000, winner);
return winner;
}
}
// diagonals
for (i = 0; i <= 1; i++) {
var diagonal = myGrid.getDiagValues(i);
if (diagonal[0] > 0 && diagonal[0] == diagonal[1] && diagonal[0] == diagonal[2]) {
if (diagonal[0] == computer) {
score.computer++;
winner = computer;
// console.log("computer wins");
} else {
score.player++;
winner = player;
// console.log("player wins");
}
// Give the winning row/column/diagonal a different bg-color
var tmpAr = myGrid.getDiagIndices(i);
for (var j = 0; j < tmpAr.length; j++) {
var str = "cell" + tmpAr[j];
document.getElementById(str).classList.add("win-color");
}
setTimeout(endGame, 1000, winner);
return winner;
}
}
// If we haven't returned a winner by now, if the board is full, it's a tie
var myArr = myGrid.getFreeCellIndices();
if (myArr.length === 0) {
winner = 10;
score.ties++;
endGame(winner);
return winner;
}
return winner;
}
function announceWinner(text) {
document.getElementById("winText").innerHTML = text;
document.getElementById("winAnnounce").style.display = "block";
setTimeout(closeModal, 1400, "winAnnounce");
}
function askUser(text) {
document.getElementById("questionText").innerHTML = text;
document.getElementById("userFeedback").style.display = "block";
}
function showOptions() {
if (player == o) {
document.getElementById("rx").checked = false;
document.getElementById("ro").checked = true;
}
else if (player == x) {
document.getElementById("rx").checked = true;
document.getElementById("ro").checked = false;
}
if (difficulty === 0) {
document.getElementById("r0").checked = true;
document.getElementById("r1").checked = false;
}
else {
document.getElementById("r0").checked = false;
document.getElementById("r1").checked = true;
}
document.getElementById("optionsDlg").style.display = "block";
}
function getOptions() {
var diffs = document.getElementsByName('difficulty');
for (var i = 0; i < diffs.length; i++) {
if (diffs[i].checked) {
difficulty = parseInt(diffs[i].value);
break;
// debugger;
}
}
if (document.getElementById('rx').checked === true) {
player = x;
computer = o;
whoseTurn = player;
playerText = xText;
computerText = oText;
}
else {
player = o;
computer = x;
whoseTurn = computer;
playerText = oText;
computerText = xText;
setTimeout(makeComputerMove, 400);
}
document.getElementById("optionsDlg").style.display = "none";
}
function closeModal(id) {
document.getElementById(id).style.display = "none";
}
function endGame(who) {
if (who == player) {
announceWinner("Congratulations, you won!");
} else if (who == computer) {
announceWinner("Computer wins!");
} else {
announceWinner("It's a tie!");
}
gameOver = true;
whoseTurn = 0;
moves = 0;
winner = 0;
document.getElementById("computer_score").innerHTML = score.computer;
document.getElementById("tie_score").innerHTML = score.ties;
document.getElementById("player_score").innerHTML = score.player;
for (var i = 0; i <= 8; i++) {
var id = "cell" + i.toString();
document.getElementById(id).style.cursor = "default";
}
setTimeout(restartGame, 800);
}
Final Output:
Conclusion:
In conclusion, building a Tic Tac Toe game with HTML, CSS, and JavaScript is a fun and rewarding project for web development beginners. By following the steps in this tutorial, you can create a fully functional game that you can share with your friends and family. Remember to test and debug your code, and feel free to experiment with different designs and features to make the game your own. We hope this tutorial has helped you improve your web development skills and inspired you to create even more fun projects in the future. Happy coding!
That’s a wrap!
I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.
Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee
And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!
Thanks!
Faraz 😊