This exercise will walk you through building a simple Tic Tac Toe game. In addition to providing the code necessary to build the game, you will also be provided and explanation for what each block of code does.
- Go to codepen.io and click on Start Coding.
That's it! That's all the setup you'll need. Code Pen is a social development environment for developers and designers. It allows us to write code and see the results as we build it. Neat huh!
Ok, Let's get started.
1). Let start by adding some HTML
<body>
<main class="background">
<section class="title">
<h1>Tic Tac Toe</h1>
</section>
</main>
</body>
This block of code includes the <body>
element. Inside the body element is where all of our other elements will live. This block of code also includes a <section>
element and an <h1>
with the heading Tic Tac Toe.
2).
After the closing </section>
tag element, add the following block of code.
<section class="display">
Player <span class="display-player playerX">X</span>'s turn
</section>
<section class="container">
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</section>
<section class="display announcer hide"></section>
<section class="controls">
<button id="reset">Reset</button>
</section>
This block of code contains <section>
elements, <div>
elements, and a <button>
element. You can learn more about these elements at W3 Schools. You can think of a <div>
as an empty container which has no style but style can be applied using CSS. The <div>
element will make up the structure for our Tic-Tac-Toe board.
Now let's add some styling to our project by adding the following code to the CSS section/
* {
padding: 0;
margin: 0;
font-family: 'Itim', cursive;
}
.background {
background-color: #12181B;
height: 100vh;
padding-top: 1px;
}
.title {
color: white;
text-align: center;
font-size: 40px;
margin-top: 10%;
}
.display {
color: white;
font-size: 25px;
text-align: center;
margin-top: 1em;
margin-bottom: 1em;
}
As you may notice, some of our HTML elements contain class=""
or id=""
. These allow us to target the element and do things like apply styling. For example, the first <section>
element has a class
that is equal to "title"
. In our CSS section, we can see that the .title
is what gives the heading its size, color, allignment, etc.
Similarly, the <main>
element has a class
of "background"
and in the CSS section we can see that the .background
gives us the background color, view height, etc.
Add to remaining CSS.
.hide {
display: none;
}
.container {
margin: 0 auto;
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 33% 33% 33%;
max-width: 300px;
}
.tile {
border: 1px solid white;
min-width: 100px;
min-height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 50px;
cursor: pointer;
}
.playerX {
color: #09C372;
}
.playerO {
color: #498AFB;
}
.controls {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-top: 1em;
}
.controls button {
color: white;
padding: 8px;
border-radius: 8px;
border: none;
font-size: 20px;
margin-left: 1em;
cursor: pointer;
}
.restart {
background-color: #498AFB;
}
#reset {
background-color: #FF3860;
}
Now Let's add some JavaScript. Add the following to the JS section.
The following code add 3 variables for the board
, currentPlayer
, and isGameActive
which determines whether there is a winner or if the game is still in progress.
window.addEventListener('DOMContentLoaded', () => {
const tiles = Array.from(document.querySelectorAll('.tile'));
const playerDisplay = document.querySelector('.display-player');
const resetButton = document.querySelector('#reset');
const announcer = document.querySelector('.announcer');
let board = ['', '', '', '', '', '', '', '', ''];
let currentPlayer = 'X';
let isGameActive = true;
const PLAYERX_WON = 'PLAYERX_WON';
const PLAYERO_WON = 'PLAYERO_WON';
const TIE = 'TIE';
/*
Indexes within the board
[0] [1] [2]
[3] [4] [5]
[6] [7] [8]
*/
const winningConditions = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
function handleResultValidation() {
let roundWon = false;
for (let i = 0; i <= 7; i++) {
const winCondition = winningConditions[i];
const a = board[winCondition[0]];
const b = board[winCondition[1]];
const c = board[winCondition[2]];
if (a === '' || b === '' || c === '') {
continue;
}
if (a === b && b === c) {
roundWon = true;
break;
}
}
if (roundWon) {
announce(currentPlayer === 'X' ? PLAYERX_WON : PLAYERO_WON);
isGameActive = false;
return;
}
if (!board.includes(''))
announce(TIE);
}
const announce = (type) => {
switch(type){
case PLAYERO_WON:
announcer.innerHTML = 'Player <span class="playerO">O</span> Won';
break;
case PLAYERX_WON:
announcer.innerHTML = 'Player <span class="playerX">X</span> Won';
break;
case TIE:
announcer.innerText = 'Tie';
}
announcer.classList.remove('hide');
};
const isValidAction = (tile) => {
if (tile.innerText === 'X' || tile.innerText === 'O'){
return false;
}
return true;
};
const updateBoard = (index) => {
board[index] = currentPlayer;
}
const changePlayer = () => {
playerDisplay.classList.remove(`player${currentPlayer}`);
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
playerDisplay.innerText = currentPlayer;
playerDisplay.classList.add(`player${currentPlayer}`);
}
const userAction = (tile, index) => {
if(isValidAction(tile) && isGameActive) {
tile.innerText = currentPlayer;
tile.classList.add(`player${currentPlayer}`);
updateBoard(index);
handleResultValidation();
changePlayer();
}
}
const resetBoard = () => {
board = ['', '', '', '', '', '', '', '', ''];
isGameActive = true;
announcer.classList.add('hide');
if (currentPlayer === 'O') {
changePlayer();
}
tiles.forEach(tile => {
tile.innerText = '';
tile.classList.remove('playerX');
tile.classList.remove('playerO');
});
}
tiles.forEach( (tile, index) => {
tile.addEventListener('click', () => userAction(tile, index));
});
resetButton.addEventListener('click', resetBoard);
});
Good job. Now give it a try.