JavaScript/Exercises/TicTacToe
< JavaScript | Exercises
TicTacToe
TicTacToe is a game for two players. They choose fields out of a 3 x 3 chessboard.
First, we need an HTML file plus CSS to realize the user interface. It shall contain:
- A caption
- A container with nine buttons arranged in a 3 x 3 chessboard
- Two button
Start X
and a buttonStart O
to decide which user starts - A
Reset
button - A textfield to give the users feedbacks about the status of the game
The HTML might look like this:
Click to see solution
<!DOCTYPE html>
<html>
<head>
<title>TicTacToe</title>
<script>
// ...
</script>
<style>
.container {
display: grid;
grid-template-columns: 32% 32% 32%;
grid-template-rows: 6em 6em 6em;
gap: 1%;
background-color: aliceblue;
margin: 2em;
padding: 2em;
}
.cell {
display: flex;
align-items: center;
justify-content: center;
}
.button {
height:2.4em;
width: 2.4em;
font-size: 2em;
background-color: aqua;
}
.containerCmd {
display: flex;
justify-content: flex-end;
}
.buttonCmd {
padding: 0.6em 2em 0.6em 2em;
font-size: 1em;
margin-right: 2em;
}
.feedback {
padding: 0.8em 1em 0.8em 1em;
margin: 1em;
font-size: 1.4em;
background-color: green;
}
</style>
</head>
<body>
<h1 style="text-align: center;">TicTacToe</h1>
<!-- The container with the nine clickable buttons -->
<div class="container">
<div class="cell"><button id="b1" class="button" disabled /></div>
<div class="cell"><button id="b2" class="button" disabled /></div>
<div class="cell"><button id="b3" class="button" disabled /></div>
<div class="cell"><button id="b4" class="button" disabled /></div>
<div class="cell"><button id="b5" class="button" disabled /></div>
<div class="cell"><button id="b6" class="button" disabled /></div>
<div class="cell"><button id="b7" class="button" disabled /></div>
<div class="cell"><button id="b8" class="button" disabled /></div>
<div class="cell"><button id="b9" class="button" disabled /></div>
</div>
<!-- buttons for start and reset the game -->
<div class="containerCmd">
<button class="buttonCmd" id="startX">Start: X</button>
<button class="buttonCmd" id="startO">Start: O</button>
<p style="padding-right:3em"></p> <!-- a small spacer -->
<button class="buttonCmd">Reset</button>
</div>
<!-- feedback from the script to the players -->
<p id="feedback" class="feedback">Click to one of the 'Start' buttons</p>
</body>
</html>
Next, you develop the app's logic by adding events to buttons and functions within the script element.
- When the game starts or will be reset, the nine X/O buttons and the feedback area must be cleared.
- It is helpful when the nine X/O buttons call the same event handler.
event.target.id
delivers the ID of the button and viadocument.getElementById(event.target.id)
this button is reachable. - Whenever one of the nine X/O buttons is clicked, this button must be protected against further clicks
elem.disabled = true
. - We need a function that decides whether a line, a row, or a diagonal (8 possibilities) contains 3 'X's respectively 3 'O's.
- Consider the case that no one wins.
All in all, the app might look like this:
Click to see solution
<!DOCTYPE html>
<html>
<head>
<title>TicTacToe</title>
<script>
"use strict";
// global variable for X/O
let user = "";
// -------- start: decide which user has the first click ----------------
function startXO(userXO) {
if (userXO === "X") {
user = "X";
} else {
user = "O";
}
// disable / enable certain buttons
document.getElementById("startX").disabled = true;
document.getElementById("startO").disabled = true;
["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9"]
.forEach((button) => {
document.getElementById(button).disabled = false;
});
document.getElementById("feedback").innerHTML = "";
}
// -------- regular action ----------------------------------------------
function buttonClicked(event) {
const elem = document.getElementById(event.target.id);
elem.innerHTML = user;
elem.disabled = true;
// check for end of game
switch (isFinished()) {
case "tie":
document.getElementById("feedback").innerHTML = "No winner. Tie.";
["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9"]
.forEach((button) => {
document.getElementById(button).disabled = true;
});
break;
case true:
document.getElementById("feedback").innerHTML = "The winner is: " + user;
["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9"]
.forEach((button) => {
document.getElementById(button).disabled = true;
});
break;
default:
// toggle user and go on
if (user === "X") {
user = "O";
} else {
user = "X";
}
}
}
// -------- check for end of game ----------------------
function isFinished() {
const xo_b1 = document.getElementById("b1").innerHTML;
const xo_b2 = document.getElementById("b2").innerHTML;
const xo_b3 = document.getElementById("b3").innerHTML;
const xo_b4 = document.getElementById("b4").innerHTML;
const xo_b5 = document.getElementById("b5").innerHTML;
const xo_b6 = document.getElementById("b6").innerHTML;
const xo_b7 = document.getElementById("b7").innerHTML;
const xo_b8 = document.getElementById("b8").innerHTML;
const xo_b9 = document.getElementById("b9").innerHTML;
// check for 'tie' in a loop over all buttons
let tmp = 0;
[xo_b1, xo_b2, xo_b3, xo_b4, xo_b5, xo_b6, xo_b7, xo_b8, xo_b9]
.forEach((elem) => {if (elem !== "") tmp++});
if (tmp === 9) {
return "tie";
}
// check for winner
if ( // horizontal
(xo_b1 === user && xo_b2 === user && xo_b3 === user) ||
(xo_b4 === user && xo_b5 === user && xo_b6 === user) ||
(xo_b7 === user && xo_b8 === user && xo_b9 === user) ||
// vertical
(xo_b1 === user && xo_b4 === user && xo_b7 === user) ||
(xo_b2 === user && xo_b5 === user && xo_b8 === user) ||
(xo_b3 === user && xo_b6 === user && xo_b9 === user) ||
// diagonal
(xo_b1 === user && xo_b5 === user && xo_b9 === user) ||
(xo_b3 === user && xo_b5 === user && xo_b7 === user)
)
{
return true;
} else {
return false;
}
}
// -------- reset game -------------------------------------
function reset() {
// disable / enable certain buttons
["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9"]
.forEach((button) => {
document.getElementById(button).innerHTML = "";
document.getElementById(button).disabled = true;
});
document.getElementById("feedback").innerHTML = "Click to 'Start'";
document.getElementById("startX").disabled = false;
document.getElementById("startO").disabled = false;
}
</script>
<style>
.container {
display: grid;
grid-template-columns: 32% 32% 32%;
grid-template-rows: 6em 6em 6em;
gap: 1%;
background-color: aliceblue;
margin: 2em;
padding: 2em;
}
.cell {
display: flex;
align-items: center;
justify-content: center;
}
.button {
height:2.4em;
width: 2.4em;
font-size: 2em;
background-color: aqua;
}
.containerCmd {
display: flex;
justify-content: flex-end;
}
.buttonCmd {
padding: 0.6em 2em 0.6em 2em;
font-size: 1em;
margin-right: 2em;
}
.feedback {
padding: 0.8em 1em 0.8em 1em;
margin: 1em;
font-size: 1.4em;
background-color: green;
}
</style>
</head>
<body>
<h1 style="text-align: center;">TicTacToe</h1>
<!-- The container with the nine clickable buttons -->
<div class="container">
<div class="cell"><button id="b1" class="button" onclick="buttonClicked(event)" disabled /></div>
<div class="cell"><button id="b2" class="button" onclick="buttonClicked(event)" disabled /></div>
<div class="cell"><button id="b3" class="button" onclick="buttonClicked(event)" disabled /></div>
<div class="cell"><button id="b4" class="button" onclick="buttonClicked(event)" disabled /></div>
<div class="cell"><button id="b5" class="button" onclick="buttonClicked(event)" disabled /></div>
<div class="cell"><button id="b6" class="button" onclick="buttonClicked(event)" disabled /></div>
<div class="cell"><button id="b7" class="button" onclick="buttonClicked(event)" disabled /></div>
<div class="cell"><button id="b8" class="button" onclick="buttonClicked(event)" disabled /></div>
<div class="cell"><button id="b9" class="button" onclick="buttonClicked(event)" disabled /></div>
</div>
<!-- buttons for start and reset the game -->
<div class="containerCmd">
<button class="buttonCmd" id="startX" onClick="startXO('X')">Start: X</button>
<button class="buttonCmd" id="startO" onClick="startXO('O')">Start: O</button>
<p style="padding-right:3em"></p> <!-- a small spacer -->
<button class="buttonCmd" onClick="reset()">Reset</button>
</div>
<!-- feedback from the script to the players -->
<p id="feedback" class="feedback">Click to one of the 'Start' buttons</p>
</body>
</html>