-
Feb 22nd, 2021, 02:53 PM
#1
[ES6] Tic-Tac-Toe
I posted this in the game demos forum, but I feel like it belongs in the JavaScript codebank too.
Here is an example of a simple player versus computer Tic-Tac-Toe game: https://jsfiddle.net/9bj2rvn0/
Source:
HTML Code:
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<meta content="utf-8" http-equiv="encoding" />
<meta content="Dday9" name="author" />
<meta content="Play Tic-Tac-Toe online with the power of HTML5" name="description" />
<meta content="tic-tac-toe, html5, free, online, game" name="keywords" />
<title>Tic Tac Toe</title>
<style>
body {
font-family: Helvetica, sans-serif;
}
table {
margin: auto;
}
table tbody tr td {
font-size: 4rem;
height: 5rem;
text-align: center;
width: 5rem;
}
/* border utilities */
.u-border {
border: 1px solid black;
}
.u-border-bottom {
border-bottom: 1px solid black;
}
.u-border-left {
border-left: 1px solid black;
}
.u-border-right {
border-right: 1px solid black;
}
.u-border-top {
border-top: 1px solid black;
}
/* color utilities */
.u-color-blue {
color: #0d6efd;
}
.u-color-red {
color: #dc3545;
}
</style>
</head>
<body>
<table>
<caption>
Wins:
<span id="wins">0</span>
- Losses:
<span id="losses">0</span>
</caption>
<tbody>
<tr>
<td class="u-border-bottom u-border-right"></td>
<td class="u-border-bottom u-border-left u-border-right"></td>
<td class="u-border-bottom u-border-left"></td>
</tr>
<tr>
<td class="u-border-bottom u-border-right u-border-top"></td>
<td class="u-border"></td>
<td class="u-border-bottom u-border-left u-border-top"></td>
</tr>
<tr>
<td class="u-border-right u-border-top"></td>
<td class="u-border-left u-border-right u-border-top"></td>
<td class="u-border-left u-border-top"></td>
</tr>
</tbody>
</table>
<script>
let availableCells = [];
let losses = 0;
let unavailableCells = [];
let wins = 0;
window.onload = function () {
const table = document.getElementsByTagName("table")[0];
const tableBody = table.getElementsByTagName("tbody")[0];
const tableRows = tableBody.getElementsByTagName("tr");
Array.from(tableRows).forEach((tableRow) => {
const cells = tableRow.getElementsByTagName("td");
Array.from(cells).forEach((cell) => (cell.onclick = cellClicked));
});
resetGame();
};
/**
* <td> cell event handler
*/
function cellClicked() {
const coordinate = determineCoordinate(this);
if (!coordinate) {
return;
}
const cell = availableCells.find((availableCell) => availableCell.x === coordinate.x && availableCell.y === coordinate.y);
if (!cell) {
return;
}
// print the cross
printCross(this);
// update the available/unavailable cells
availableCells = availableCells.filter((availableCell) => availableCell !== cell);
cell.owner = "x";
unavailableCells.push(cell);
if (determineWin("x")) {
// user won
alert("You won!");
++wins;
resetGame();
} else if (!availableCells.length) {
// cat (draw)
alert("Cat");
resetGame();
} else {
// computer's turn
playComputer();
}
}
/**
* gets the X/Y coordinate of a <td> respective to the overall <table>
*/
function determineCoordinate(cell) {
const table = document.getElementsByTagName("table")[0];
const tableBody = table.getElementsByTagName("tbody")[0];
const tableRows = tableBody.getElementsByTagName("tr");
for (let y = 0; y < tableRows.length; y++) {
const cells = tableRows[y].getElementsByTagName("td");
for (let x = 0; x < cells.length; x++) {
if (cells[x] === cell) {
return { x, y };
}
}
}
return null;
}
function determineWin(owner) {
const ownerCells = unavailableCells.filter((unavailableCell) => unavailableCell.owner === owner);
if (ownerCells.length < 3) {
// no point in checking
return false;
}
if (ownerCells.filter((ownerCell) => ownerCell.x === 0).length === 3) {
// 1st column match
return true;
}
if (ownerCells.filter((ownerCell) => ownerCell.x === 1).length === 3) {
// 2nd column match
return true;
}
if (ownerCells.filter((ownerCell) => ownerCell.x === 2).length === 3) {
// 3nd column match
return true;
}
if (ownerCells.filter((ownerCell) => ownerCell.y === 0).length === 3) {
// 1st column match
return true;
}
if (ownerCells.filter((ownerCell) => ownerCell.y === 1).length === 3) {
// 2nd column match
return true;
}
if (ownerCells.filter((ownerCell) => ownerCell.y === 2).length === 3) {
// 3nd column match
return true;
}
if (ownerCells.filter((ownerCell) => (ownerCell.x === 0 && ownerCell.y === 0) || (ownerCell.x === 1 && ownerCell.y === 1) || (ownerCell.x === 2 && ownerCell.y === 2)).length === 3) {
// diagnoal top left to bottom right match
return true;
}
if (ownerCells.filter((ownerCell) => (ownerCell.x === 2 && ownerCell.y === 0) || (ownerCell.x === 1 && ownerCell.y === 1) || (ownerCell.x === 0 && ownerCell.y === 2)).length === 3) {
// diagnoal top right to bottom left match
return true;
}
return false;
}
function playComputer() {
// get a random coordinate
const randomCell = availableCells[Math.floor(Math.random() * availableCells.length)];
const table = document.getElementsByTagName("table")[0];
const tableBody = table.getElementsByTagName("tbody")[0];
const tableRow = tableBody.getElementsByTagName("tr")[randomCell.y];
const cell = tableRow.getElementsByTagName("td")[randomCell.x];
// print the cross
printCircle(cell);
// update the available/unavailable cells
availableCells = availableCells.filter((availableCell) => availableCell !== randomCell);
randomCell.owner = "o";
unavailableCells.push(randomCell);
if (determineWin("o")) {
// player won
alert("CPU won :(");
++losses;
resetGame();
} else if (!availableCells.length) {
// cat (draw)
alert("Cat");
resetGame();
}
}
/**
* sets a <td>'s text to "O" and adds the red color class
*/
function printCircle(cell) {
cell.innerText = "O";
cell.classList.add("u-color-red");
}
/**
* sets a <td>'s text to "X" and adds the blue color class
*/
function printCross(cell) {
cell.innerText = "X";
cell.classList.add("u-color-blue");
}
/**
* updates the global variables as well as the DOM
*/
function resetGame() {
// update the win/loss ratio
document.getElementById("losses").innerText = losses;
document.getElementById("wins").innerText = wins;
// reset the availableCells/unavailableCells
availableCells = [];
unavailableCells = [];
const table = document.getElementsByTagName("table")[0];
const tableBody = table.getElementsByTagName("tbody")[0];
const tableRows = tableBody.getElementsByTagName("tr");
for (let y = 0; y < tableRows.length; y++) {
const cells = tableRows[y].getElementsByTagName("td");
for (let x = 0; x < cells.length; x++) {
// clear the text of the <td> and add the cell to the availableCells
cells[x].innerText = "";
availableCells.push({ x, y });
}
}
}
</script>
</body>
</html>
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|