I am making Tic Tac Toe Game , I am giving two options for the player to play the game that is VS player and VS computer
Both functions logic is ready but when I am integrating both the code so the function that I run first , only that logic is only implemented
ex if I run first VSplayer then after clicking on VScomp then also VSplayer code is running, and vice versa
I know this code can be optimized but I was facing problems that’s why I tried to break both in different functions and try it
// Computer Logic
function VScomp() {
var Boxes = document.querySelectorAll('.box')
var info = document.getElementById('info')
let reset = document.getElementById('reset')
var a = "X"
let gameover = false
info.innerText = `Player X Turn`
Boxes.forEach(box => {
box.addEventListener('click', () => {
if (box.innerHTML == "" && !gameover) {
box.innerHTML = a
checkWins()
compchoice()
if (gameover) {
Boxes.forEach((box) => {
box.style.cursor = "not-allowed"
})
}
}
})
})
// Check Wins
const checkWins = () => {
let wins = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]
]
wins.forEach(i => {
if (Boxes[i[0]].innerHTML == Boxes[i[1]].innerHTML && Boxes[i[1]].innerHTML == Boxes[i[2]].innerHTML && Boxes[i[0]].innerHTML != "") {
info.innerText = `${Boxes[i[0]].innerHTML} Won`
gameover = true
i.forEach(y => {
Boxes[y].classList.add("winner")
})
}
})
}
reset.addEventListener('click', () => {
Boxes.forEach(box => {
box.innerHTML = ""
gameover = false
a = "X"
info.innerText = `Player X Turn`
Boxes.forEach((box) => {
box.classList.remove("winner")
box.style.cursor = "pointer"
})
})
})
function compchoice() {
if (!gameover) {
let flag = false;
while (!flag) {
let nums = Math.floor(Math.random() * 8);
let box = Boxes[nums];
if (box.innerHTML === "") {
box.innerHTML = "O";
flag = true;
checkWins();
}
}
}
}
}
// // VS player Logic
function VSplayer() {
let Boxes = document.querySelectorAll('.box')
let info = document.getElementById('info')
let reset = document.getElementById('reset')
var a = "O"
let gameover = false
info.innerText = `X Turn`
Boxes.forEach(box => {
box.addEventListener('click', () => {
if (box.innerHTML == "" && !gameover) {
if (a === "O") {
info.innerText = `${a} Turn`
a = "X"
}
else if (a === "X") {
info.innerText = `${a} Turn`
a = "O"
}
box.innerHTML = a
checkWins()
if (gameover) {
Boxes.forEach((box) => {
box.style.cursor = "not-allowed"
})
}
}
})
})
// Check Wins
const checkWins = () => {
let wins = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]
]
wins.forEach(i => {
if (Boxes[i[0]].innerHTML == Boxes[i[1]].innerHTML && Boxes[i[1]].innerHTML == Boxes[i[2]].innerHTML && Boxes[i[0]].innerHTML != "") {
info.innerText = `${Boxes[i[0]].innerHTML} Won`
gameover = true
i.forEach(y => {
Boxes[y].classList.add("winner")
})
}
})
}
reset.addEventListener('click', () => {
Boxes.forEach(box => {
box.innerHTML = ""
gameover = false
a = "O"
info.innerText = `X Turn`
Boxes.forEach((box) => {
box.classList.remove("winner")
box.style.cursor = "pointer"
})
})
})
}
VScomp()
* {
padding: 0;
margin: 0;
}
body {
overflow: hidden;
}
.game {
grid-template-columns: repeat(3, 10vw);
grid-template-rows: repeat(3, 15vh);
}
.customfont
{
font-size: 56px;
color: black;
}
.carousel
{
top:-16px;
}
.carousel-inner
{
top: 4px;
}
.carousel-control
{
top: -16px;
}
@media screen and (max-width: 730px) {
.game {
grid-template-columns: repeat(3, 16vw);
}
}
.box {
border: 1px solid black;
display: flex;
flex-wrap: wrap;
align-content: center;
justify-content: center;
}
.box:hover {
cursor: pointer;
background-color: #d0d8d9;
}
.maindiv {
background-color: #2e2922;
}
.subdiv {
background-color: silver;
border-radius: 22px;
min-width: 300px;
}
#reset:hover
{
color: red !important;
}
#reset:focus
{
box-shadow: none !important;
}
#tic {
animation: tic 2s linear 1;
}
@keyframes tic {
from {
margin-left: -60vw;
}
to {
margin-left: 0;
}
}
#tac {
animation: tac 2s linear 1;
}
@keyframes tac {
0% {
margin-top: -60vh;
}
85% {
margin-top: 0;
}
}
#toe {
animation: toe 2s linear 1;
}
@keyframes toe {
from {
margin-right: -60vw;
}
to {
margin-right: 0vw;
}
}
.winner
{
animation: blink 1s linear infinite ;
}
@keyframes blink
{
from {
color: transparent;
}
to {
color: black;
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="style.css">
<title>Tic Tac Toe</title>
</head>
<body>
</div>
<div class="maindiv min-vh-100 d-flex justify-content-center flex-column align-content-center flex-wrap">
<div class="text-white d-flex justify-content-center">
<div>
<h1 class="ps-3" id="tic">TIC</h1>
</div>
<div>
<h1 class="ps-3" id="tac">TAC</h1>
</div>
<div>
<h1 class="ps-3 text-end" id="toe">TOE</h1>
</div>
</div>
<div class="subdiv d-flex h-100 p-4 flex-column">
<div class="gameInfo">
<div>
<button class="VScomp" onclick="VSplayer()">VS Player</button>
<button class="VSplayer" onclick="VScomp()">VS Comp</button>
</div>
<h2>
<div class=" d-flex justify-content-center" id="info"></div>
</h2>
</div>
<div class="game mt-4 d-grid justify-content-center ">
<div class="box fw-bolder fs-3 border-top-0 border-start-0 "></div>
<div class="box fw-bolder fs-3 border-top-0 "></div>
<div class="box fw-bolder fs-3 border-top-0 border-end-0 "></div>
<div class="box fw-bolder fs-3 border-start-0 "></div>
<div class="box fw-bolder fs-3 "></div>
<div class="box fw-bolder fs-3 border-end-0 "></div>
<div class="box fw-bolder fs-3 border-start-0 border-bottom-0 "></div>
<div class="box fw-bolder fs-3 border-bottom-0 "></div>
<div class="box fw-bolder fs-3 border-bottom-0 border-end-0 "></div>
</div>
<div class="d-flex justify-content-center mt-3"><button type="button" class="btn btn-lg"
id="reset">Reset</button></div>
</div>
</div>
<script src="JQUERY-Version.js"></script>
<script src="temp.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"
integrity="sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13"
crossorigin="anonymous"></script>
</body>
</html>
2
Answers
VScomp
andVSplayer
both add event handlers to your boxes. If you call both functions, both sets of event handlers will be added so both handlers will be called when a box is clicked.To fix this, either remove the event handlers before adding the new ones, or use a single event handler that looks at the current playing mode to decide what to do.
You need to call both
VScomp
andVSplayer
to support both use-cases, but, besides of that, you will need to switch off the events when the other use-case is chosen. This is a working example that you can start your thought-process from:As you can see, I used an
isComp
flag to know whether the secondary moves are to be made by the comp. If so, then the comp will make the moves. Otherwise a human move will be expected.A better solution would be to create a single cell event listener for each cell and implement two validators. Depending on what mode you are in, the right move validator would be chosen.
Validator in comp mode: first player is on move and the move to be made is valid.
Validator in human mode: any player is on move and the move to be made is valid.
In comp mode call a comp move function after the player has moved as long as the game did not finish.