In this post, we will see how to make the Tic Tac Toe game using HTML CSS, and Pure JavaScript.
Create the Tic Tac Toe Game using HTML, CSS, & JavaScript
As we know that there are 9 boxes in the game so in the following HTML file we have added 9 <section>
elements that represent the boxes.
Each <section>
element will be identified through its value of the data-box-num
attribute.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tic Tac Toe - Play with your friend</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;900&family=Monoton&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>Tic Tac Toe</h1>
<div class="state">
<button id="restart">↻ Restart</button>
<p id="result"></p>
</div>
<div class="game-container">
<div class="wrap">
<section class="box" data-box-num="0"></section>
<section class="box" data-box-num="1"></section>
<section class="box" data-box-num="2"></section>
<section class="box" data-box-num="3"></section>
<section class="box" data-box-num="4"></section>
<section class="box" data-box-num="5"></section>
<section class="box" data-box-num="6"></section>
<section class="box" data-box-num="7"></section>
<section class="box" data-box-num="8"></section>
</div>
</div>
</div>
<script src="two-user.js"></script>
</body>
</html>
In the following style.css
, you can see two classes .cross::before
and .zero::before
, these classes will be used to add cross or zero to a box.
*,
*::after,
*::before {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
font-family: "Cairo", sans-serif;
background-color: #f2f2f2;
color: #222222;
}
/*
* Text Shadow https://codepen.io/fielding/pen/wYPRjj
*/
h1 {
text-align: center;
font-size: 3.5rem;
color: #ffffff;
margin: 0;
word-spacing: 7px;
text-shadow: 0px -6px 0 #212121, 0px -6px 0 #212121, 0px 6px 0 #212121,
0px 6px 0 #212121, -6px 0px 0 #212121, 6px 0px 0 #212121,
-6px 0px 0 #212121, 6px 0px 0 #212121, -6px -6px 0 #212121,
6px -6px 0 #212121, -6px 6px 0 #212121, 6px 6px 0 #212121,
-6px 18px 0 #212121, 0px 18px 0 #212121, 6px 18px 0 #212121,
0 19px 1px rgba(0, 0, 0, 0.1), 0 0 6px rgba(0, 0, 0, 0.1),
0 6px 3px rgba(0, 0, 0, 0.3), 0 12px 6px rgba(0, 0, 0, 0.2),
0 18px 18px rgba(0, 0, 0, 0.25), 0 24px 24px rgba(0, 0, 0, 0.2),
0 36px 36px rgba(0, 0, 0, 0.15);
}
h1 a {
color: inherit;
text-decoration: none;
}
ul {
margin: 0;
padding: 20px;
list-style: none;
max-width: 400px;
margin: 0 auto;
}
ul li {
padding: 5px;
}
ul a {
display: block;
padding: 10px 20px;
font-size: 18px;
border-radius: 3px;
text-decoration: none;
color: #000;
border: 1px solid rgba(0, 0, 0, 0.2);
background-color: white;
transition: 0.3s all;
}
ul a:hover {
background-color: #222222;
color: white;
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1),
0 8px 10px -6px rgb(0 0 0 / 0.1);
}
.game-container {
padding: 20px;
}
.game-container .wrap {
margin: 0 auto;
max-width: 400px;
display: grid;
grid-template-columns: repeat(3, 90px);
grid-template-rows: repeat(3, 90px);
justify-content: center;
border-radius: 3px;
}
.box {
border: 3px solid #333333;
display: flex;
justify-content: center;
align-items: center;
font-size: 50px;
font-weight: 800;
font-family: "Monoton", cursive;
}
.box:nth-child(3n) {
border-right: none;
}
.box:nth-child(-n + 3) {
border-top: none;
}
.box:nth-child(3n - 2) {
border-left: none;
}
.box:nth-child(n + 7) {
border-bottom: none;
}
.cross::before {
content: "X";
color: #222222;
}
.zero::before {
content: "O";
color: #3393ff;
}
.box:hover {
background-color: #f9f9f9;
cursor: pointer;
}
.state {
text-align: center;
margin-top: 20px;
}
.blink::before {
animation: blink 1s linear infinite;
}
@keyframes blink {
0% {
opacity: 0;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
.state button {
all: unset;
background: #f7f7f7;
border: 1px solid rgb(0 0 0 / 0.3);
color: black;
padding: 5px 15px;
font-size: 18px;
border-radius: 3px;
cursor: pointer;
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}
.state button:hover {
background: white;
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1),
0 8px 10px -6px rgb(0 0 0 / 0.1);
}
.state button:active {
box-shadow: none;
}
#result {
font-size: 22px;
font-weight: bold;
}
#result::first-letter {
color: yellow;
font-family: "Monoton", cursive;
background-color: #333333;
padding: 3px 10px;
display: inline-block;
border-radius: 2px;
}
The checkList
array will contain both users’ actions.
currentPlayer
– It defines who is the current player (current move – CROSS or ZERO).
If any user has won the match then the winStatus
will be true.
When a box is clicked the handleboxClick()
function will be called.
The checkWin()
function will be called after a box click is completed to check whether the current player has won the match after making his move.
The isWin()
function contains the winning logic. I picked this logic from here.
If you have understood the winning logic, you can easily find out that what is the job of the checkEquality()
and areEqual()
functions.
If any user won the match, the blinkTheBox()
function will add the blink class to the matching boxes for better user experience.
const allBox = document.querySelectorAll(".box");
const resultContainer = document.getElementById("result");
const restartBtn = document.getElementById("restart");
const checkList = [];
let currentPlayer = "CROSS";
let winStatus = false;
function areEqual(one, two) {
if (one === two) return one;
return false;
}
function checkEquality(currentPlayer, array) {
for (const item of array) {
const a = checkList[item[0]];
const b = checkList[item[1]];
if (areEqual(a, b) == currentPlayer) {
return [item[0], item[1]];
}
}
return false;
}
function blinkTheBox(val){
if (val){
for(const i of val){
const box = document.querySelector(`[data-box-num="${i}"]`);
box.classList.add('blink');
}
return true;
}
return false;
}
function isWin() {
let val = false;
if (checkList[0] == currentPlayer) {
val = checkEquality(currentPlayer, [
[1, 2],
[3, 6],
[4, 8],
]);
if (val && blinkTheBox([0,...val])) return true;
}
if (checkList[8] == currentPlayer) {
val = checkEquality(currentPlayer, [
[2, 5],
[6, 7],
]);
if (val && blinkTheBox([8,...val])) return true;
}
if (checkList[4] == currentPlayer) {
val = checkEquality(currentPlayer, [
[1, 7],
[3, 5],
[2, 6],
]);
if (val && blinkTheBox([4,...val])) return true;
}
return val;
}
function checkWin(len) {
if (len >= 3 && isWin()) {
winStatus = true;
if (currentPlayer == "CROSS") {
resultContainer.innerText = "X Won the Match.";
} else {
resultContainer.innerText = "O Won the Match.";
}
} else if (len == 8) {
winStatus = true;
resultContainer.innerText = "= Match Draw.";
}
}
function boxClick(e, player, boxNum){
checkList[boxNum] = player;
e.target.classList.add(player.toLowerCase());
currentPlayer = (player === 'CROSS') ? 'CROSS' : 'ZERO';
}
function handleBoxClick(e) {
const len = checkList.filter(Boolean).length;
const boxNum = parseInt(e.target.getAttribute("data-box-num"));
if (!winStatus && !checkList[boxNum]) {
if (len > 0 && currentPlayer == "CROSS") {
boxClick(e,'ZERO',boxNum);
} else {
boxClick(e,'CROSS',boxNum);
}
checkWin(len);
}
}
restartBtn.addEventListener('click',function(){
allBox.forEach(item => {
item.classList.remove('cross','zero','blink');
});
checkList.length = 0;
currentPlayer = 'CROSS';
resultContainer.innerText = '';
winStatus = false;
});
allBox.forEach(item => {
item.addEventListener('click',(e) => handleBoxClick(e));
});
Create a Bot for the Tic Tac Toe game using JavaScript
Now I will show you the three types of bots written in JS for the above Tic Tac Toe game.
Demo of Bots
- Normal Bot – This bot randomly picks up an empty box.
- Smart Bot – This bot is almost similar to the genius bot, the difference is in the first move of the bot.
- Genius Bot – Difficult to beat.
const allBox = document.querySelectorAll(".box");
const resultContainer = document.getElementById("result");
const restartBtn = document.getElementById("restart");
const checkList = [];
let currentPlayer = "CROSS";
let winStatus = false;
function areEqual(one, two) {
if (one === two) return one;
return false;
}
function checkEquality(currentPlayer, array) {
for (const item of array) {
const a = checkList[item[0]];
const b = checkList[item[1]];
if (areEqual(a, b) == currentPlayer) {
return [item[0], item[1]];
}
}
return false;
}
function blinkTheBox(val) {
if (val) {
for (const i of val) {
const box = document.querySelector(`[data-box-num="${i}"]`);
box.classList.add("blink");
}
return true;
}
return false;
}
function isWin() {
let val = false;
if (checkList[0] == currentPlayer) {
val = checkEquality(currentPlayer, [
[1, 2],
[3, 6],
[4, 8],
]);
if (val && blinkTheBox([0, ...val])) return true;
}
if (checkList[8] == currentPlayer) {
val = checkEquality(currentPlayer, [
[2, 5],
[6, 7],
]);
if (val && blinkTheBox([8, ...val])) return true;
}
if (checkList[4] == currentPlayer) {
val = checkEquality(currentPlayer, [
[1, 7],
[3, 5],
[2, 6],
]);
if (val && blinkTheBox([4, ...val])) return true;
}
return val;
}
function checkWin(len) {
if (len >= 3 && isWin()) {
winStatus = true;
if (currentPlayer == "CROSS") {
resultContainer.innerText = "X Won the Match.";
} else {
resultContainer.innerText = "O Won the Match.";
}
} else if (len == 8) {
winStatus = true;
resultContainer.innerText = "= Match Draw.";
}
return winStatus;
}
function boxClick(targetBox, player, boxNum) {
checkList[boxNum] = player;
targetBox.classList.add(player.toLowerCase());
}
function handleBoxClick(e) {
let len = checkList.filter(Boolean).length;
const boxNum = parseInt(e.target.getAttribute("data-box-num"));
let boxNumForBot;
if (!winStatus && !checkList[boxNum]) {
currentPlayer = "CROSS";
boxClick(e.target, "CROSS", boxNum);
if (checkWin(len) === false) {
len = checkList.filter(Boolean).length;
currentPlayer = "ZERO";
while (len < 9) {
boxNumForBot = Math.floor(Math.random() * 9);
if (!checkList[boxNumForBot]) {
boxClick(allBox[boxNumForBot], "ZERO", boxNumForBot);
checkWin(len);
break;
}
}
}
}
}
restartBtn.addEventListener("click", function () {
allBox.forEach((item) => {
item.classList.remove("cross", "zero", "blink");
});
checkList.length = 0;
currentPlayer = "CROSS";
resultContainer.innerText = "";
winStatus = false;
});
allBox.forEach((item) => {
item.addEventListener("click", (e) => handleBoxClick(e));
});
const allBox = document.querySelectorAll(".box");
const resultContainer = document.getElementById("result");
const restartBtn = document.getElementById("restart");
const checkList = [];
const BOX_NUMBERS = [0,1,2,3,4,5,6,7,8];
const BOX_MID_NUMBERS = [1, 3, 5, 7];
let userWinList = [];
let botWinList = [];
let currentPlayer = "CROSS";
let winStatus = false;
function areEqual(one, two) {
if (one === two) return one;
return false;
}
function checkEquality(currentPlayer, array, list) {
for (const item of array) {
const a = list[item[0]];
const b = list[item[1]];
if (areEqual(a, b) == currentPlayer) {
return [item[0], item[1]];
}
}
return false;
}
function blinkTheBox(val, pass=false) {
if(pass) return true;
if (val) {
for (const i of val) {
const box = document.querySelector(`[data-box-num="${i}"]`);
box.classList.add("blink");
}
return true;
}
return false;
}
function isWin(player = currentPlayer, list = checkList, pass = false) {
let val = false;
if (list[0] == player) {
val = checkEquality(player, [
[1, 2],
[3, 6],
[4, 8],
],list);
if (val && blinkTheBox([0, ...val], pass)) return true;
}
if (list[8] == player) {
val = checkEquality(player, [
[2, 5],
[6, 7],
],list);
if (val && blinkTheBox([8, ...val], pass)) return true;
}
if (list[4] == player) {
val = checkEquality(player, [
[1, 7],
[3, 5],
[2, 6],
],list);
if (val && blinkTheBox([4, ...val], pass)) return true;
}
return val;
}
function checkWin(len) {
if (len >= 3 && isWin()) {
winStatus = true;
if (currentPlayer == "CROSS") {
resultContainer.innerText = "X Won the Match.";
} else {
resultContainer.innerText = "O Won the Match.";
}
} else if (len == 8) {
winStatus = true;
resultContainer.innerText = "= Match Draw.";
}
return winStatus;
}
function captureCenterOrCorner(){
if(!checkList[4]){
return 4;
}
else if(!checkList[2]){
return 2;
}
else if(!checkList[6]){
return 6;
}
else if(!checkList[8]){
return 8;
}
return false;
}
function getBoxNumForBot(mainListLen) {
let xWin = false;
let dummyList = [];
let dummyListLen;
let dummyBoxNumbers = [];
if (mainListLen === 1) {
while (mainListLen < 9) {
const boxNumForBot = Math.floor(Math.random() * 9);
if (!checkList[boxNumForBot]) {
return boxNumForBot;
}
} // while
} else if (mainListLen === 3) {
if (
(checkList[0] && checkList[8] && checkList[0] == checkList[8]) ||
(checkList[2] && checkList[6] && checkList[2] == checkList[6])
) {
return BOX_MID_NUMBERS[Math.floor(Math.random() * 3)];
}
}
dummyList = [...checkList];
dummyListLen = dummyList.filter(Boolean).length;
dummyBoxNumbers = [...BOX_NUMBERS]
while(dummyListLen < 9 && dummyBoxNumbers.length){
botWinList = []
botWinList = [...checkList]
let randNum = Math.floor(Math.random() * dummyBoxNumbers.length);
let botNum = dummyBoxNumbers[randNum];
if(!botWinList[botNum]){
dummyList[botNum] = 'ZERO';
botWinList[botNum] = 'ZERO';
if(isWin('ZERO',botWinList,true)){
return botNum;
}
}
dummyBoxNumbers.splice(randNum,1);
}// while
dummyList = [];
dummyList = [...checkList];
dummyListLen = dummyList.filter(Boolean).length;
dummyBoxNumbers = []
dummyBoxNumbers = [...BOX_NUMBERS];
while(dummyListLen < 9 && dummyBoxNumbers.length){
userWinList = []
userWinList = [...checkList]
let randNum = Math.floor(Math.random() * dummyBoxNumbers.length);
let botNum = dummyBoxNumbers[randNum];
if(!userWinList[botNum]){
dummyList[botNum] = 'CROSS';
userWinList[botNum] = 'CROSS';
if(isWin('CROSS',userWinList,true)){
xWin = botNum;
break;
}
}
dummyBoxNumbers.splice(randNum, 1);
}
if(xWin === false && mainListLen === 3 && checkList[4] == 'ZERO'){
if(!checkList[3] && !checkList[5]){
return 3;
}
else if(!checkList[1]){
return 1;
}
else if(!checkList[7]){
return 7;
}
}
return xWin;
} // getBoxNumForBot
function boxClick(targetBox, player, boxNum) {
checkList[boxNum] = player;
targetBox.classList.add(player.toLowerCase());
}
function handleBoxClick(e) {
let len = checkList.filter(Boolean).length;
const boxNum = parseInt(e.target.getAttribute("data-box-num"));
let boxNumForBot;
if (!winStatus && !checkList[boxNum]) {
currentPlayer = "CROSS";
boxClick(e.target, "CROSS", boxNum);
if (checkWin(len) === false) {
len = checkList.filter(Boolean).length;
currentPlayer = "ZERO";
boxNumForBot = getBoxNumForBot(len);
if(boxNumForBot !== false){
boxClick(allBox[boxNumForBot],'ZERO',boxNumForBot);
checkWin(len);
/// go on....
}
else{
boxNumForBot = captureCenterOrCorner();
if(boxNumForBot){
boxClick(allBox[boxNumForBot],'ZERO',boxNumForBot);
checkWin(len);
}
else{
while (len < 9) {
boxNumForBot = Math.floor(Math.random() * 9);
if (!checkList[boxNumForBot]) {
boxClick(allBox[boxNumForBot], "ZERO", boxNumForBot);
checkWin(len);
break;
}
} // while
}
}
} // checkWin
}
}
restartBtn.addEventListener("click", function () {
allBox.forEach((item) => {
item.classList.remove("cross", "zero", "blink");
});
checkList.length = 0;
currentPlayer = "CROSS";
resultContainer.innerText = "";
winStatus = false;
});
allBox.forEach((item) => {
item.addEventListener("click", (e) => handleBoxClick(e));
});
const allBox = document.querySelectorAll(".box");
const resultContainer = document.getElementById("result");
const restartBtn = document.getElementById("restart");
const checkList = [];
const BOX_NUMBERS = [0,1,2,3,4,5,6,7,8];
const BOX_MID_NUMBERS = [1, 3, 5, 7];
let userWinList = [];
let botWinList = [];
let currentPlayer = "CROSS";
let winStatus = false;
function areEqual(one, two) {
if (one === two) return one;
return false;
}
function checkEquality(currentPlayer, array, list) {
for (const item of array) {
const a = list[item[0]];
const b = list[item[1]];
if (areEqual(a, b) == currentPlayer) {
return [item[0], item[1]];
}
}
return false;
}
function blinkTheBox(val, dontBlink=false) {
if(dontBlink) return true;
if (val) {
for (const i of val) {
const box = document.querySelector(`[data-box-num="${i}"]`);
box.classList.add("blink");
}
return true;
}
return false;
}
function isWin(player = currentPlayer, list = checkList, dontBlink = false) {
let val = false;
if (list[0] == player) {
val = checkEquality(player, [
[1, 2],
[3, 6],
[4, 8],
],list);
if (val && blinkTheBox([0, ...val], dontBlink)) return true;
}
if (list[8] == player) {
val = checkEquality(player, [
[2, 5],
[6, 7],
],list);
if (val && blinkTheBox([8, ...val], dontBlink)) return true;
}
if (list[4] == player) {
val = checkEquality(player, [
[1, 7],
[3, 5],
[2, 6],
],list);
if (val && blinkTheBox([4, ...val], dontBlink)) return true;
}
return val;
}
function checkWin(len) {
if (len >= 3 && isWin()) {
winStatus = true;
if (currentPlayer == "CROSS") {
resultContainer.innerText = "X Won the Match.";
} else {
resultContainer.innerText = "O Won the Match.";
}
} else if (len == 8) {
winStatus = true;
resultContainer.innerText = "= Match Draw.";
}
return winStatus;
}
function captureCenterOrCorner(){
if(!checkList[4]){
return 4;
}
else if(!checkList[2]){
return 2;
}
else if(!checkList[6]){
return 6;
}
else if(!checkList[8]){
return 8;
}
return false;
}
function getBoxNumForBot(mainListLen) {
let xWin = false;
let dummyList = [];
let dummyListLen;
let dummyBoxNumbers = [];
if (mainListLen === 1) {
if (!checkList[4]) {
return 4;
} else if (!checkList[2]) {
return 2;
} else {
return 8;
}
} else if (mainListLen === 3) {
if (
(checkList[0] && checkList[8] && checkList[0] == checkList[8]) ||
(checkList[2] && checkList[6] && checkList[2] == checkList[6])
) {
return BOX_MID_NUMBERS[Math.floor(Math.random() * 3)];
}
}
dummyList = [...checkList];
dummyListLen = dummyList.filter(Boolean).length;
dummyBoxNumbers = [...BOX_NUMBERS]
while(dummyListLen < 9 && dummyBoxNumbers.length){
botWinList = []
botWinList = [...checkList]
let randNum = Math.floor(Math.random() * dummyBoxNumbers.length);
let botNum = dummyBoxNumbers[randNum];
if(!botWinList[botNum]){
dummyList[botNum] = 'ZERO';
botWinList[botNum] = 'ZERO';
if(isWin('ZERO',botWinList,true)){
return botNum;
}
}
dummyBoxNumbers.splice(randNum,1);
}// while
dummyList = [];
dummyList = [...checkList];
dummyListLen = dummyList.filter(Boolean).length;
dummyBoxNumbers = []
dummyBoxNumbers = [...BOX_NUMBERS];
while(dummyListLen < 9 && dummyBoxNumbers.length){
userWinList = []
userWinList = [...checkList]
let randNum = Math.floor(Math.random() * dummyBoxNumbers.length);
let botNum = dummyBoxNumbers[randNum];
if(!userWinList[botNum]){
dummyList[botNum] = 'CROSS';
userWinList[botNum] = 'CROSS';
if(isWin('CROSS',userWinList,true)){
xWin = botNum;
break;
}
}
dummyBoxNumbers.splice(randNum, 1);
}
if(xWin === false && mainListLen === 3 && checkList[4] == 'ZERO'){
if(!checkList[3] && !checkList[5]){
return 3;
}
else if(!checkList[1]){
return 1;
}
else if(!checkList[7]){
return 7;
}
}
return xWin;
} // getBoxNumForBot
function boxClick(targetBox, player, boxNum) {
checkList[boxNum] = player;
targetBox.classList.add(player.toLowerCase());
}
function handleBoxClick(e) {
let len = checkList.filter(Boolean).length;
const boxNum = parseInt(e.target.getAttribute("data-box-num"));
let boxNumForBot;
if (!winStatus && !checkList[boxNum]) {
currentPlayer = "CROSS";
boxClick(e.target, "CROSS", boxNum);
if (checkWin(len) === false) {
len = checkList.filter(Boolean).length;
currentPlayer = "ZERO";
boxNumForBot = getBoxNumForBot(len);
if(boxNumForBot !== false){
boxClick(allBox[boxNumForBot],'ZERO',boxNumForBot);
checkWin(len);
}
else{
boxNumForBot = captureCenterOrCorner();
if(boxNumForBot){
boxClick(allBox[boxNumForBot],'ZERO',boxNumForBot);
checkWin(len);
}
else{
while (len < 9) {
boxNumForBot = Math.floor(Math.random() * 9);
if (!checkList[boxNumForBot]) {
boxClick(allBox[boxNumForBot], "ZERO", boxNumForBot);
checkWin(len);
break;
}
} // while
}
}
} // checkWin
}
}
restartBtn.addEventListener("click", function () {
allBox.forEach((item) => {
item.classList.remove("cross", "zero", "blink");
});
checkList.length = 0;
currentPlayer = "CROSS";
resultContainer.innerText = "";
winStatus = false;
});
allBox.forEach((item) => {
item.addEventListener("click", (e) => handleBoxClick(e));
});