So you'll find an if-statement marked in the snippet to inspect. If you run the snippet you'll notice the score adds when the character meets the coin. When the character is not at the coin, the score then returns to 0.
How can we save the score until 'isTouching()' is true again? Adding to it again and again, each time the coin is met. To confirm, the coin is supposed to move.
Thank you!
function isTouching(a, b) {
const aRect = a.getBoundingClientRect();
const bRect = b.getBoundingClientRect();
return !(
aRect.top + aRect.height < bRect.top ||
aRect.top > bRect.top + bRect.height ||
aRect.left + aRect.width < bRect.left ||
aRect.left > bRect.left + bRect.width
);
}
const avatar = document.querySelector('#player');
const coin = document.querySelector('#coin');
window.addEventListener('keyup', function(event){
if(event.key==='ArrowDown' || event.key==='Down'){
const currTop = extractPos(avatar.style.top);
avatar.style.top = `${currTop + 50}px`;
}
else if (event.key === 'ArrowUp' || event.key === 'Up'){
const currTop = extractPos(avatar.style.top);
avatar.style.top = `${currTop - 50}px`;
}
else if (event.key === 'ArrowRight' || event.key === 'Right'){
const currLeft = extractPos(avatar.style.left);
avatar.style.left = `${currLeft + 50}px`;
avatar.style.transform = 'scale(1,1)';
}
else if (event.key === 'ArrowLeft' || event.key === 'Left'){
const currLeft = extractPos(avatar.style.left);
avatar.style.left = `${currLeft - 50}px`;
avatar.style.transform = 'scale(-1,1)';
};
////////////////////////////////////////////////////////////////////////
const scoreLabel = document.querySelector('#score');
let scoreCount = 0;
if (isTouching(avatar, coin)) {
scoreCount++;
};
scoreLabel.textContent = scoreCount;
if(isTouching(avatar, coin)) moveCoin();
});
/////////////////////////////////////////////////////////////////////////
const extractPos = (position) => {
if (!position) return 50;
return parseInt(position.slice(0,-2))
};
const moveCoin = () => {
const y = Math.floor(Math.random()*window.innerWidth)
const x = Math.floor(Math.random()*window.innerHeight)
coin.style.top = `${y}px`;
coin.style.left = `${x}px`;
};
moveCoin();
#player {
width: 100px;
position: absolute;
top: 50px;
left: 50px;
}
#coin {
width: 100px;
position: absolute;
top: 150px;
left: 50px;
}
#scoreBoard {
width: 20px;
position: absolute;
top: 50px;
left: 20px;
border: 8px solid #FFD700;
}
label {
text-align: center;
margin-left: 6px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Coin Game</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<img id="player" src="https://media.tenor.com/images/0791eb3858075aca85eed5ecfe08c778/tenor.gif" alt="">
<img id="coin" src="https://i.ya-webdesign.com/images/coin-gif-png-7.gif" alt="">
<div id="scoreBoard">
<label id="score">0</label>
</div>
<script src="app.js"></script>
</body>
</html>
The score is getting initialized to 0 every time the listener runs, instead, you can use a global variable or you could save the score as textContent of the div with id "score".
Another small improvement would be to do the moveCoin and increment score in the same if (isTouching) block instead of two if blocks
function isTouching(a, b) {
const aRect = a.getBoundingClientRect();
const bRect = b.getBoundingClientRect();
return !(
aRect.top + aRect.height < bRect.top ||
aRect.top > bRect.top + bRect.height ||
aRect.left + aRect.width < bRect.left ||
aRect.left > bRect.left + bRect.width
);
}
const avatar = document.querySelector('#player');
const coin = document.querySelector('#coin');
window.addEventListener('keyup', function (event) {
if (event.key === 'ArrowDown' || event.key === 'Down') {
const currTop = extractPos(avatar.style.top);
avatar.style.top = `${currTop + 50}px`;
} else if (event.key === 'ArrowUp' || event.key === 'Up') {
const currTop = extractPos(avatar.style.top);
avatar.style.top = `${currTop - 50}px`;
} else if (event.key === 'ArrowRight' || event.key === 'Right') {
const currLeft = extractPos(avatar.style.left);
avatar.style.left = `${currLeft + 50}px`;
avatar.style.transform = 'scale(1,1)';
} else if (event.key === 'ArrowLeft' || event.key === 'Left') {
const currLeft = extractPos(avatar.style.left);
avatar.style.left = `${currLeft - 50}px`;
avatar.style.transform = 'scale(-1,1)';
}
////////////////////////////////////////////////////////////////////////
if (isTouching(avatar, coin)) {
incrementScore();
moveCoin();
}
});
/////////////////////////////////////////////////////////////////////////
const incrementScore = () => {
const score = Number(document.getElementById('score').textContent);
document.getElementById('score').textContent = score + 1;
};
const extractPos = (position) => {
if (!position) return 50;
return parseInt(position.slice(0, -2));
};
const moveCoin = () => {
const y = Math.floor(Math.random() * window.innerWidth);
const x = Math.floor(Math.random() * window.innerHeight);
coin.style.top = `${y}px`;
coin.style.left = `${x}px`;
};
moveCoin();
#player {
width: 100px;
position: absolute;
top: 50px;
left: 50px;
}
#coin {
width: 100px;
position: absolute;
top: 150px;
left: 50px;
}
#scoreBoard {
width: 20px;
position: absolute;
top: 50px;
left: 20px;
border: 8px solid #FFD700;
}
label {
text-align: center;
margin-left: 6px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Coin Game</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<img id="player" src="https://media.tenor.com/images/0791eb3858075aca85eed5ecfe08c778/tenor.gif" alt="">
<img id="coin" src="https://i.ya-webdesign.com/images/coin-gif-png-7.gif" alt="">
<div id="scoreBoard">
<label id="score">0</label>
</div>
<script src="app.js"></script>
</body>
</html>
The variable let scoreCount = 0; is defined inside the event listener and once the event finishes and the variable is destructed with the value.
Simplest solution is to move let scoreCount = 0; to after const coin = document.querySelector('#coin'); directly as below
function isTouching(a, b) {
const aRect = a.getBoundingClientRect();
const bRect = b.getBoundingClientRect();
return !(
aRect.top + aRect.height < bRect.top ||
aRect.top > bRect.top + bRect.height ||
aRect.left + aRect.width < bRect.left ||
aRect.left > bRect.left + bRect.width
);
}
const avatar = document.querySelector('#player');
const coin = document.querySelector('#coin');
let scoreCount = 0;
window.addEventListener('keyup', function(event){
if(event.key==='ArrowDown' || event.key==='Down'){
const currTop = extractPos(avatar.style.top);
avatar.style.top = `${currTop + 50}px`;
}
else if (event.key === 'ArrowUp' || event.key === 'Up'){
const currTop = extractPos(avatar.style.top);
avatar.style.top = `${currTop - 50}px`;
}
else if (event.key === 'ArrowRight' || event.key === 'Right'){
const currLeft = extractPos(avatar.style.left);
avatar.style.left = `${currLeft + 50}px`;
avatar.style.transform = 'scale(1,1)';
}
else if (event.key === 'ArrowLeft' || event.key === 'Left'){
const currLeft = extractPos(avatar.style.left);
avatar.style.left = `${currLeft - 50}px`;
avatar.style.transform = 'scale(-1,1)';
};
////////////////////////////////////////////////////////////////////////
const scoreLabel = document.querySelector('#score');
if (isTouching(avatar, coin)) {
scoreCount++;
};
scoreLabel.textContent = scoreCount;
if(isTouching(avatar, coin)) moveCoin();
});
/////////////////////////////////////////////////////////////////////////
const extractPos = (position) => {
if (!position) return 50;
return parseInt(position.slice(0,-2))
};
const moveCoin = () => {
const y = Math.floor(Math.random()*window.innerWidth)
const x = Math.floor(Math.random()*window.innerHeight)
coin.style.top = `${y}px`;
coin.style.left = `${x}px`;
};
moveCoin();
#player {
width: 100px;
position: absolute;
top: 50px;
left: 50px;
}
#coin {
width: 100px;
position: absolute;
top: 150px;
left: 50px;
}
#scoreBoard {
width: 20px;
position: absolute;
top: 50px;
left: 20px;
border: 8px solid #FFD700;
}
label {
text-align: center;
margin-left: 6px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Coin Game</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<img id="player" src="https://media.tenor.com/images/0791eb3858075aca85eed5ecfe08c778/tenor.gif" alt="">
<img id="coin" src="https://i.ya-webdesign.com/images/coin-gif-png-7.gif" alt="">
<div id="scoreBoard">
<label id="score">0</label>
</div>
<script src="app.js"></script>
</body>
</html>
Related
Say a user has a HTML file, and they are using a touchscreen device, say a phone.
I have this set of code right here:
W.onmousedown = function () {
gameOver();
}
Basically what it does is detect if the div named W was clicked and if so, end the game.
I want to do this same thing but instead, it's detecting if the user touched the DIV on their screen. How can I do this?
Edit:
Using click for me doesn't work, it doesn't end the game. This is my code:
var gameIsPlaying = true;
function game() {
gameIsPlaying = true;
const RandomLetterGUI = document.getElementById("RandomLetters")
const TimerGUI = document.getElementById("Timer")
const LivesGUI = document.getElementById("Lives")
const ScoreGUI = document.getElementById("Score")
const W = document.getElementById("W")
const A = document.getElementById("A")
const S = document.getElementById("S")
const D = document.getElementById("D")
W.style.backgroundColor = "white";
W.innerHTML = 'W'
A.style.backgroundColor = "white";
A.innerHTML = 'A'
S.style.backgroundColor = "white";
S.innerHTML = 'S'
D.style.backgroundColor = "white";
D.innerHTML = 'D'
const letters = [
"W",
"A",
"S",
"D"
]
var seconds = 60;
var lives = 3;
var score = 0;
var timerId = setInterval(countdown, 1000);
function countdown() {
if (seconds == -1) {
gameOver()
} else {
if (seconds > 9) {
TimerGUI.innerHTML = ':' + seconds;
} else {
TimerGUI.innerHTML = ':0' + seconds;
}
seconds--;
}
}
countdown()
const updateLives = setInterval(displayLives, 0)
const ScoreUpdate = setInterval(updateScore, 0)
function gameOver() {
gameIsPlaying = false
clearTimeout(timerId)
clearTimeout(updateLives)
clearTimeout(ScoreUpdate)
W.style.backgroundColor = "red";
W.innerHTML = ''
A.style.backgroundColor = "red";
A.innerHTML = ''
S.style.backgroundColor = "red";
S.innerHTML = ''
D.style.backgroundColor = "red";
D.innerHTML = ''
RandomLetterGUI.innerHTML = ''
TimerGUI.innerHTML = ''
LivesGUI.innerHTML = ''
ScoreGUI.innerHTML = ''
}
function updateScore() {
ScoreGUI.innerHTML = "Score: " + score
}
function displayLives() {
LivesGUI.innerHTML = "Remaining Lives: " + lives
if (lives == 0) {
gameOver()
}
}
function letter() {
var item = letters[Math.floor(Math.random() * letters.length)];
RandomLetterGUI.innerHTML = "Next Letter: " + item
var pickedLetterTime = Math.floor(Date.now() / 1000)
document.onkeypress = function(e) {
if (gameIsPlaying) {
var key = e.key
if (key.toUpperCase() != item) {
lives -= 1;
if (score >= 0) {
score -= 50;
}
} else {
var pressedKeyTime = Math.floor(Date.now() / 1000)
var seconds = pressedKeyTime - pickedLetterTime
if (seconds > 0 && seconds < 1.5) {
score += 500
}
if (seconds >= 1.5 && seconds < 3) {
score += 350
}
if (seconds >= 3 && seconds < 5) {
score += 150
}
}
}
};
document.onkeydown = function(e) {
var key = e.key
if (key == "w") {
W.style.backgroundColor = "lime";
W.innerHTML = ''
}
if (key == "a") {
A.style.backgroundColor = "lime";
A.innerHTML = ''
}
if (key == "s") {
S.style.backgroundColor = "lime";
S.innerHTML = ''
}
if (key == "d") {
D.style.backgroundColor = "lime";
D.innerHTML = ''
}
}
document.onkeyup = function(e) {
if (e.key == "w") {
W.style.backgroundColor = "white";
W.innerHTML = 'W'
}
if (e.key == "a") {
A.style.backgroundColor = "white";
A.innerHTML = 'A'
}
if (e.key == "s") {
S.style.backgroundColor = "white";
S.innerHTML = 'S'
}
if (e.key == "d") {
D.style.backgroundColor = "white";
D.innerHTML = 'D'
}
}
// touchscreen compatibility
W.onclick = function() {
gameOver();
}
}
letter()
}
game()
function resetGame() {
if (gameIsPlaying == false) {
document.onkeypress = function(e) {
var key = e.key
if (key == "Enter") {
game()
}
}
}
}
setInterval(resetGame, 0)
body {
background-color: black;
}
p {
font-family: Verdana;
color: white;
font-size: 20px;
}
.RandomLetters {
float: left;
}
.Timer {
float: right;
}
.Lives {
position: absolute;
left: auto;
}
.Score {
position: absolute;
right: 0;
}
.center {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
bottom: 100px;
left: 0;
right: 0;
margin: auto;
}
.center2 {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.W,
.A,
.S,
.D {
height: 50px;
width: 50px;
font-family: Verdana;
text-align: center;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Play WASD online</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<noscript>turn on javascript to play this game or noob :P</noscript>
<p id="RandomLetters" class="RandomLetters">
</p>
<p id="Timer" class="Timer">
</p>
<br>
<p id="Lives" class="Lives">
</p>
<p id="Score" class="Score">
</p>
<div class="center">
<div id="W" class="W">
</div>
</div>
<div class="center2">
<div id="A" class="A">
</div>
<div id="S" class="S">
</div>
<div id="D" class="D">
</div>
</div>
<script src="script.js"></script>
</body>
</html>
It doesn't work, because you obscured your W container with another one and it's not clickable
I changed the order of containers so the W one is now at the front layer and it works
var gameIsPlaying = true;
function game() {
gameIsPlaying = true;
const RandomLetterGUI = document.getElementById("RandomLetters")
const TimerGUI = document.getElementById("Timer")
const LivesGUI = document.getElementById("Lives")
const ScoreGUI = document.getElementById("Score")
const W = document.getElementById("W")
const A = document.getElementById("A")
const S = document.getElementById("S")
const D = document.getElementById("D")
W.style.backgroundColor = "white";
W.innerHTML = 'W'
A.style.backgroundColor = "white";
A.innerHTML = 'A'
S.style.backgroundColor = "white";
S.innerHTML = 'S'
D.style.backgroundColor = "white";
D.innerHTML = 'D'
const letters = [
"W",
"A",
"S",
"D"
]
var seconds = 60;
var lives = 3;
var score = 0;
var timerId = setInterval(countdown, 1000);
function countdown() {
if (seconds == -1) {
gameOver()
} else {
if (seconds > 9) {
TimerGUI.innerHTML = ':' + seconds;
} else {
TimerGUI.innerHTML = ':0' + seconds;
}
seconds--;
}
}
countdown()
const updateLives = setInterval(displayLives, 0)
const ScoreUpdate = setInterval(updateScore, 0)
function gameOver() {
gameIsPlaying = false
clearTimeout(timerId)
clearTimeout(updateLives)
clearTimeout(ScoreUpdate)
W.style.backgroundColor = "red";
W.innerHTML = ''
A.style.backgroundColor = "red";
A.innerHTML = ''
S.style.backgroundColor = "red";
S.innerHTML = ''
D.style.backgroundColor = "red";
D.innerHTML = ''
RandomLetterGUI.innerHTML = ''
TimerGUI.innerHTML = ''
LivesGUI.innerHTML = ''
ScoreGUI.innerHTML = ''
}
function updateScore() {
ScoreGUI.innerHTML = "Score: " + score
}
function displayLives() {
LivesGUI.innerHTML = "Remaining Lives: " + lives
if (lives == 0) {
gameOver()
}
}
function letter() {
var item = letters[Math.floor(Math.random() * letters.length)];
RandomLetterGUI.innerHTML = "Next Letter: " + item
var pickedLetterTime = Math.floor(Date.now() / 1000)
document.onkeypress = function(e) {
if (gameIsPlaying) {
var key = e.key
if (key.toUpperCase() != item) {
lives -= 1;
if (score >= 0) {
score -= 50;
}
} else {
var pressedKeyTime = Math.floor(Date.now() / 1000)
var seconds = pressedKeyTime - pickedLetterTime
if (seconds > 0 && seconds < 1.5) {
score += 500
}
if (seconds >= 1.5 && seconds < 3) {
score += 350
}
if (seconds >= 3 && seconds < 5) {
score += 150
}
}
}
};
document.onkeydown = function(e) {
var key = e.key
if (key == "w") {
W.style.backgroundColor = "lime";
W.innerHTML = ''
}
if (key == "a") {
A.style.backgroundColor = "lime";
A.innerHTML = ''
}
if (key == "s") {
S.style.backgroundColor = "lime";
S.innerHTML = ''
}
if (key == "d") {
D.style.backgroundColor = "lime";
D.innerHTML = ''
}
}
document.onkeyup = function(e) {
if (e.key == "w") {
W.style.backgroundColor = "white";
W.innerHTML = 'W'
}
if (e.key == "a") {
A.style.backgroundColor = "white";
A.innerHTML = 'A'
}
if (e.key == "s") {
S.style.backgroundColor = "white";
S.innerHTML = 'S'
}
if (e.key == "d") {
D.style.backgroundColor = "white";
D.innerHTML = 'D'
}
}
// touchscreen compatibility
W.onclick = function() {
gameOver();
}
}
letter()
}
game()
function resetGame() {
if (gameIsPlaying == false) {
document.onkeypress = function(e) {
var key = e.key
if (key == "Enter") {
game()
}
}
}
}
setInterval(resetGame, 0)
body {
background-color: black;
}
p {
font-family: Verdana;
color: white;
font-size: 20px;
}
.RandomLetters {
float: left;
}
.Timer {
float: right;
}
.Lives {
position: absolute;
left: auto;
}
.Score {
position: absolute;
right: 0;
}
.center {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
bottom: 100px;
left: 0;
right: 0;
margin: auto;
}
.center2 {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.W,
.A,
.S,
.D {
height: 50px;
width: 50px;
font-family: Verdana;
text-align: center;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Play WASD online</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<noscript>turn on javascript to play this game or noob :P</noscript>
<p id="RandomLetters" class="RandomLetters">
</p>
<p id="Timer" class="Timer">
</p>
<br>
<p id="Lives" class="Lives">
</p>
<p id="Score" class="Score">
</p>
<div class="center2">
<div id="A" class="A">
</div>
<div id="S" class="S">
</div>
<div id="D" class="D">
</div>
</div>
<div class="center">
<div id="W" class="W">
</div>
</div>
<script src="script.js"></script>
</body>
</html>
var element= document.getElementById("IDName");
element.onclick = function () {
gameOver();
}
you can use click or Pointer Events
You can use the touchstart event for this, see this mdn article
W.ontouchstart = function () {
gameOver();
}
I tried to create a tic-tac-toe game using minimax algorithm. But before I could write the whole algorithm, I encountered an absurd type of error. This code is for reference in case if you have to look into the full code.
//Using minimax to the same code of tictactoe2.js
$ = document.querySelectorAll(".box");
// adding click event to all boxes
$.forEach((element)=>{
element.addEventListener("click", mainFunction);
})
document.querySelector(".reset-button-container").addEventListener("click", reset);
function mainFunction(e){
if (checkForGameOver() === false){ //once game is over, so should be all clicks
if (e.target.innerText !== ""){
alert("Click Another Square!!!");
}
else{
e.target.innerText = "X";
if (checkForGameOver() === false){ //computer gets its turn only if the game isn't over after players turn
setTimeout(computerTurn, 500);
setTimeout(checkForGameOver, 501); //501 is provided to run checkForGameOver only after computerTurn
}
}
}
}
//a function that randomly plays computers turn
function computerTurn(){
let board = [], bestScore = Infinity, bestMove = {}, k = 0;
//array of innerTexts of each box is the board in one dimension
$.forEach((element, index) => {
board[index] = element.innerText;
});
let board2D = convertToTwoDimension(board, 3, 3); //convert 1d board to 2d of 3 rows and 3 columns
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
if (board2D[i][j] === ""){
board2D[i][j] = "O";
let score = minimax(board2D, false);
board2D[i][j] = "";
if(score < bestScore){
bestScore = score;
bestMove = {i, j};
}
}
}
}
board2D[bestMove.i][bestMove.j] = "O"; //assigning bestMove to baord2D
//to dispaly the played bestMove by the computer on screen
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
$[k].innerText = board2D[i][j];
k += 1;
}
}
}
//minimax implementation
function minimax(position, maximizing){
console.log(position);
//if the game is over at that position, return the score based on who is wining
if (checkForGameOver() === true){
if(document.querySelector(".win-message").innerText[0] === "X"){
return 1; //X (maximizing player) wins
}
else if(document.querySelector(".win-message").innerText[0] === "O"){
return -1; //O (minimizing player) wins
}
else{
return 0; // the game is a draw
}
}
}
//function to check if game is over. But I think it can be written more easily.
function checkForGameOver(){
var tempText = [];
for(i = 1; i <= 9; i++){
tempText[i] = $[i-1].innerText;
}
if (tempText[1] === tempText[2] && tempText[2] === tempText[3] && tempText[1] !== ""){
document.querySelector(".win-message").innerText = tempText[1] + " Wins!!";
return true;
}
else if (tempText[4] === tempText[5] && tempText[5] === tempText[6] && tempText[4] !== ""){
document.querySelector(".win-message").innerText = tempText[4] + " Wins!!";
return true;
}
else if (tempText[7] === tempText[8] && tempText[8] === tempText[9] && tempText[7] !== ""){
document.querySelector(".win-message").innerText = tempText[7] + " Wins!!";
return true;
}
else if (tempText[1] === tempText[4] && tempText[4] === tempText[7] && tempText[1] !== ""){
document.querySelector(".win-message").innerText = tempText[1] + " Wins!!";
return true;
}
else if (tempText[2] === tempText[5] && tempText[5] === tempText[8] && tempText[2] !== ""){
document.querySelector(".win-message").innerText = tempText[2] + " Wins!!";
return true;
}
else if (tempText[3] === tempText[6] && tempText[6] === tempText[9] && tempText[3] !== ""){
document.querySelector(".win-message").innerText = tempText[3] + " Wins!!";
return true;
}
else if (tempText[1] === tempText[5] && tempText[5] === tempText[9] && tempText[1] !== ""){
document.querySelector(".win-message").innerText = tempText[1] + " Wins!!";
return true;
}
else if (tempText[3] === tempText[5] && tempText[5] === tempText[7] && tempText[3] !== ""){
document.querySelector(".win-message").innerText = tempText[3] + " Wins!!";
return true;
}
//if none of above, which is for win only, is true, check for draw which can be done by filtering the remaining blank squares. If no squares are lest, then game is a draw else the game isn't over yet.
else{
arrayOfBlankCharacters = tempText.filter(element => {
return (element === "");
});
if (arrayOfBlankCharacters.length === 0){
document.querySelector(".win-message").innerText = "Game Drawn!!";
return true;
}
else{
return false;
}
}
}
//a function that resets the board and win message.
function reset(){
for(i = 1; i <= 9; i++){
$[i-1].innerText = "";
}
document.querySelector(".win-message").innerText = "";
}
//function to convert 1d array to 2d array => useful for computerTurn() function to access board in 2d view
function convertToTwoDimension(initialArray, rows, columns){
var k = 0, finalArray = [];
for(i = 0; i < initialArray.length/columns; i++){
finalArray[i] = []; //state that each element of finalArray is also an array
for(j = 0; j < initialArray.length/rows; j++){
finalArray[i][j] = initialArray[k];
k += 1;
}
}
return finalArray;
}
#import url('https://fonts.googleapis.com/css2?family=Roboto:wght#900&display=swap');
:root{
font-size: 16px;
font-family: 'Roboto', sans-serif;
}
*, *::after, *::before{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.description-header{
background-color: lightgrey;
line-height: 3rem;
padding-left: 2rem;
margin-bottom: 2rem;
}
.player-information{
text-align: center;
margin-bottom: 1rem;
}
.player-information > h3{
font-size: 2.5rem;
}
.player-information > h3 > span{
color:brown;
}
.game-box-container{
display: flex;
flex-direction: column;
min-height: 100vh;
align-items: center;
}
.tictactoe-box{
height: 250px;
width: 250px;
display: grid;
grid-template: repeat(3, 1fr)/repeat(3, 1fr);
grid-gap: 0.5rem;
background-color: black;
}
.reset-button-container{
margin-top: 1rem;
background: lightgrey;
border-radius: 0.5rem;
}
.tictactoe-box > .box{
display: flex;
justify-content: center;
align-items: center;
font-size: 4rem;
background-color: white;
cursor: context-menu
}
.reset-button-container > span{
display: block;
font-size: 2rem;
padding: 0.5rem 1.5rem;
}
.reset-button-container:hover{
background-color: grey;
cursor: context-menu;
}
.win-message{
margin-top: 1rem;
width: 25%;
font-size: 2.5rem;
background-color: lightgrey;
text-align: center;
border-radius: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>TicTacToe</title>
<link rel="stylesheet" type="text/css" href="tictactoe.css">
<link rel="icon" type="image/x-icon" href="https://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Tic_tac_toe.svg/1024px-Tic_tac_toe.svg.png">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-sclae=1.0">
<meta name="description" content="">
<meta name="keywords" content="">
</head>
<body>
<header class="description-header">
<h3>My TicTacToe Game</h3>
</header>
<div class="player-information">
<h3>Player: "<span>X</span>"; Computer: "<span>O</span>"</h3>
<h3>Player First</h3>
</div>
<div class="game-box-container">
<div class="tictactoe-box">
<div class="box box1"></div>
<div class="box box2"></div>
<div class="box box3"></div>
<div class="box box4"></div>
<div class="box box5"></div>
<div class="box box6"></div>
<div class="box box7"></div>
<div class="box box8"></div>
<div class="box box9"></div>
</div>
<div class="reset-button-container">
<span>Reset</span>
</div>
<div class="win-message">
</div>
</div>
<script type="text/javascript" src="usingMinimax.js"></script>
</body>
</html>
My problem arises in the minimax(position, maximizing) function of the code as below. console.log(position) runs just fine, but because of the checkForGameOver() in the below function, it throws an error that board2D[i] is undefined which is unexpected because there is no any arguments passed in to the checkForGameOver() function. As soon as I remove the (checkForGameOver() === false) condition and run it, the computer makes a move for the first available spot which is as expected.
//minimax implementation
function minimax(position, maximizing){
console.log(position);
//if the game is over at that position, return the score based on who is wining
if (checkForGameOver() === true){
if(document.querySelector(".win-message").innerText[0] === "X"){
return 1; //X (maximizing player) wins
}
else if(document.querySelector(".win-message").innerText[0] === "O"){
return -1; //O (minimizing player) wins
}
else{
return 0; // the game is a draw
}
}
}
A function that doesn't take any variable as arguments just returns a boolean value, is somehow the cause of an error that the variable is undefined.
I'm a self taught programmer and I got my hands on a TicTacToe project, but I got stucked at the following part.
I did a relatively working TicTacToe(inspired from youtube) then I wanted to do a responsive TicTacToe matrix that changes it's numbers from(0-9) in X's and O's in the console depending of the inputs of the first game. But now I'm stuck at trying to find a way to check the winners of the matrix oh and I'm trying to start the "checking" from the last X or O
Hopefully seeing the code is much more easier to understand.
I'd really really appreciate some help!
const statusDiv = document.querySelector('.status');
const resetDiv = document.querySelector('.reset');
const cellDivs = document.querySelectorAll('.game-cell');
// game constants
const xSymbol = '×';
const oSymbol = '○';
// game variables
let gameIsLive = true;
let xIsNext = true;
var currentPlayer = 0;
//Random generated
function getInputValue() {
document.getElementById("formInput").style.display = "none";
document.getElementById("container").style.display = "inline";
let player1Input = document.getElementById("player1Input").value;
let player2Input = document.getElementById("player2Input").value;
currentPlayer = 1;
var random = Math.random() * 100;
if (random <= 60) {
currentPlayer = 1;
statusDiv.innerHTML = player1Input + " turn";
statusDiv.style.color = "blue";
}
else {
currentPlayer = 2;
statusDiv.innerHTML = player2Input + " turn";
statusDiv.style.color = "red";
}
}
// statusDiv.style.filter= "green";
//functions
const letterToSymbol = (letter) => letter === 'x' ? xSymbol : oSymbol;
const handleWin = (letter) => {
gameIsLive = false;
if (currentPlayer === 1) {
statusDiv.innerHTML = player1Input.value + " has won!";
statusDiv.style.color = "blue";
} else {
statusDiv.innerHTML = player2Input.value + " has won!";
statusDiv.style.color = "red";
}
};
const checkGameStatus = () => {
const topLeft = cellDivs[0].classList[1];
const topMiddle = cellDivs[1].classList[1];
const topRight = cellDivs[2].classList[1];
const middleLeft = cellDivs[3].classList[1];
const middleMiddle = cellDivs[4].classList[1];
const middleRight = cellDivs[5].classList[1];
const bottomLeft = cellDivs[6].classList[1];
const bottomMiddle = cellDivs[7].classList[1];
const bottomRight = cellDivs[8].classList[1];
// check winner
if (topLeft && topLeft === topMiddle && topLeft === topRight) {
handleWin(topLeft);
cellDivs[0].classList.add('won');
cellDivs[1].classList.add('won');
cellDivs[2].classList.add('won');
} else if (middleLeft && middleLeft === middleMiddle && middleLeft === middleRight) {
handleWin(middleLeft);
cellDivs[3].classList.add('won');
cellDivs[4].classList.add('won');
cellDivs[5].classList.add('won');
} else if (bottomLeft && bottomLeft === bottomMiddle && bottomLeft === bottomRight) {
handleWin(bottomLeft);
cellDivs[6].classList.add('won');
cellDivs[7].classList.add('won');
cellDivs[8].classList.add('won');
} else if (topLeft && topLeft === middleLeft && topLeft === bottomLeft) {
handleWin(topLeft);
cellDivs[0].classList.add('won');
cellDivs[3].classList.add('won');
cellDivs[6].classList.add('won');
} else if (topMiddle && topMiddle === middleMiddle && topMiddle === bottomMiddle) {
handleWin(topMiddle);
cellDivs[1].classList.add('won');
cellDivs[4].classList.add('won');
cellDivs[7].classList.add('won');
} else if (topRight && topRight === middleRight && topRight === bottomRight) {
handleWin(topRight);
cellDivs[2].classList.add('won');
cellDivs[5].classList.add('won');
cellDivs[8].classList.add('won');
} else if (topLeft && topLeft === middleMiddle && topLeft === bottomRight) {
handleWin(topLeft);
cellDivs[0].classList.add('won');
cellDivs[4].classList.add('won');
cellDivs[8].classList.add('won');
} else if (topRight && topRight === middleMiddle && topRight === bottomLeft) {
handleWin(topRight);
cellDivs[2].classList.add('won');
cellDivs[4].classList.add('won');
cellDivs[6].classList.add('won');
} else if (topLeft && topMiddle && topRight && middleLeft && middleMiddle && middleRight && bottomLeft && bottomMiddle && bottomRight) {
gameIsLive = false;
statusDiv.innerHTML = 'Game is tied!';
} else {
xIsNext = !xIsNext;
if (currentPlayer === 2) {
statusDiv.innerHTML = player1Input.value + ` turn`;
statusDiv.style.color = "blue";
currentPlayer = 1;
} else {
currentPlayer = 2;
statusDiv.style.color = "red";
statusDiv.innerHTML = player2Input.value + ` turn`;
}
}
};
// event Handlers
const handleReset = () => {
document.getElementById("formInput").style.display = "inline";
document.getElementById("container").style.display = "none";
xIsNext = true;
statusDiv.innerHTML = `Press Start Game`;
for (const cellDiv of cellDivs) {
cellDiv.classList.remove('x');
cellDiv.classList.remove('o');
cellDiv.classList.remove('won');
document.location.reload()
}
gameIsLive = true;
};
const handleCellClick = (e) => {
const classList = e.target.classList;
if (!gameIsLive || classList[1] === 'x' || classList[1] === 'o') {
return;
}
if (currentPlayer === 1) {
classList.add('playerOneColour');
} else {
classList.add('playerTwoColour');
}
if (xIsNext) {
classList.add("x");
} else {
classList.add('o');
}
checkGameStatus();
};
// event listeners
resetDiv.addEventListener('click', handleReset);
for (const cellDiv of cellDivs) {
cellDiv.addEventListener('click', handleCellClick);
cellDiv.addEventListener('click', handleCellSelection);
}
//Matrix Builder
var counter = 0;
var matrix = [];
for (var i = 0; i < 3; i++) {
matrix[i] = [];
for (var j = 0; j < 3; j++) {
matrix[i][j] = [];
matrix[i][j] = counter;
++counter;
}
}
// 0,1,2,3,4,5,6,7,8
// 0,1,2,0,1,2,0,1,2
var playerCounter = 1;
var currentPlayer = 1;
function handleCellSelection(e) {
playerCounter++;
let htmlElement = e.target;
let cellType = htmlElement.getAttribute("data-cell");
let reuseNTI = numberToIndexes(cellType);
let matrixIndex = indexInMatrix(reuseNTI[0], reuseNTI[1]);
let isWinnerFunction = isPlayerWinner(matrix);
function indexInMatrix(index1, index2) {
if (currentPlayer === 2) {
return matrix[index1].splice([index2], 1, "X")
currentPlayer = 1;
} else {
return matrix[index1].splice([index2], 1, "O");
currentPlayer = 2;
}
}
function isPlayerWinner(matrix, currentPlayer) {
if (playerCounter < 6) {
return;
} else {
var xSign = "X";
var oSign = "O";
let rowX = 0;
let rowO = 0;
//TO DO CHECK COLUMN
for (var i = 0; i < matrix[0].length; i++) {
if (xSign === matrix[i][reuseNTI[0]]) {
rowX++
} else if (oSign === matrix[i][reuseNTI[0]]) {
rowO++
console.log(rowO)
}
// console.log(matrix[i, 0].lastIndexOf("X")) ; //000↓,111↓,222↓
// console.log(matrix[i, 0].lastIndexOf("O"));
}
//TO DO CHECK ROW
for (var i = 0; i < matrix[0].length; i++) {
if (xSign === matrix[reuseNTI[0][i]]) {
} else if (oSign === matrix[reuseNTI[0]][i]) {
}
// console.log(matrix[0, i].lastIndexOf("X")) ; //row is 000->,111->,222->
// console.log(matrix[0, i].lastIndexOf("O"));
}
//TO DO CHECK PRIMARY DIAGONAL
for (var i = 0; i < matrix[0].length; i++) {
if (xSign === matrix[i][i]) {
} else if (oSign === matrix[i][i]) {
}
// matrix[i][i];
// console.log(matrix[i, i].lastIndexOf("X")); //012->,012->,012->
// console.log(matrix[i, i].lastIndexOf("O"));
}
//TO DO CHECK SECONDARY DIAGONAL
for (var i = 0; i < matrix[0].length; i++) {
if (xSign === matrix[i][matrix.length - 1 - i]) {
} else if (oSign === matrix[i][matrix.length - 1 - i]) {
}
// matrix[i][matrix.length - 1 - i];
// console.log(matrix[i, matrix.length - 1 - i].lastIndexOf("X"));
// console.log(matrix[i, [matrix.length - 1 - i]].lastIndexOf("O"));
}
}
}
isPlayerWinner(matrix);
console.log(matrix);
}
function numberToIndexes(number) {
let row = Math.floor(number / 3);
let column = number % 3;
return [row, column];
}
var cellSize = 100;
var cellSpace = 10;
var matrixRows = matrix.length;
var matrixColumns = matrix[0].length;
for (var i = 0; i < matrixRows; i++) {
for (var j = 0; j < matrixColumns; j++) {
var cell = document.createElement("div");
cell.setAttribute("class", "cell");
matrixStage.appendChild(cell);
cell.style.top = i * (cellSize + cellSpace) + "px";
cell.style.left = j * (cellSize + cellSpace) + "px";
}
}
function newMatrixGrid() {
var grid3x3 = document.getElementById("matrixMaker").value;
if (grid3x3 == "3") {
var matrixStage = document.querySelector("#matrixStage").style.display = "block";
} else {
matrixStage = document.querySelector("#matrixStage").style.display = "none";
}
}
#player1{
color: blue;
}
#player2{
color: red;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
color: #545454;
display: grid;
font-family: sans-serif;
justify-content: center;
}
.container {
background: #ffffff;
margin: 50px;
padding: 50px;
border-radius: 25px;
}
.title {
text-align: center;
}
.title span {
color: #F2EBD3;
}
.status-action {
color:blue;
display: flex;
margin-top: 25px;
font-size: 25px;
justify-content: space-around;
height: 30px;
}
.status span {
color: red;
}
.reset {
color: black;
cursor: pointer;
}
.reset:hover {
color: #10976c;
}
.game-grid {
background: #000000;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
grid-gap: 15px;
margin-top: 50px;
}
.game-cell {
background:white;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
height: 200px;
width: 200px;
}
.playerOneColour{
filter: invert(9%) sepia(100%) saturate(7261%) hue-rotate(247deg) brightness(91%) contrast(146%);
}
.playerTwoColour{
filter: invert(14%) sepia(95%) saturate(6042%) hue-rotate(358deg) brightness(109%) contrast(117%);
}
.x {
color: transparent;
cursor: default;
}
.o {
color: transparent;
cursor: default;
}
.x::after {
background-image: url(../Resources/X.png);
background-size:contain;
background-repeat: no-repeat;
margin-top: 80px;
content: '×';
}
.o::after {
background-image: url(../Resources/O.png);
background-size:contain;
background-repeat: no-repeat;
margin-top: 100px;
content: '○';
}
.won::after {
filter: invert(100%) sepia() saturate(10000%) hue-rotate(0deg);
}
#media only screen and (min-width: 1024px) {
.game-grid {
margin-top: 25px;
grid-gap: 10px;
}
.game-cell {
height: 150px;
width: 150px;
}
.x::after {
font-size: 150px;
}
.o::after {
font-size: 175px;
}
}
#media only screen and (max-width: 540px) {
.container {
margin: 25px;
padding: 25px;
}
.game-cell {
height: 100px;
width: 100px;
}
.x::after {
font-size: 100px;
}
.o::after {
font-size: 125px;
}
}
#matrixStage{
position:relative;
}
.cell
{
position: absolute;
width: 100px;
height: 100px;
border: 1px solid black;
background-color: white;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Play XO</title>
<link rel="StyleSheet" href="Styles/index.css">
</head>
<body>
<form id="formInput">
<h1>Play X O </h1><br>
<label for="player1" id="player1">Player 1:</label><br>
<input type="text" id="player1Input"><br>
<label for="player1" id="player2">Player 2:</label><br>
<input type="text" id="player2Input"><br><br>
<button type="button" onclick="getInputValue();"> Start Game</button>
</form>
<br>
</form>
<div class="container" style="display:none" id="container">
<h1 class="title">Play X O </h1>
<div class="status-action">
<div class="status">Player 1 turn</div>
<div class="reset">Reset</div>
</div>
<div class="game-grid">
<div class="game-cell" data-cell="0"></div>
<div class="game-cell" data-cell="1"></div>
<div class="game-cell" data-cell="2"></div>
<div class="game-cell" data-cell="3"></div>
<div class="game-cell" data-cell="4"></div>
<div class="game-cell" data-cell="5"></div>
<div class="game-cell" data-cell="6"></div>
<div class="game-cell" data-cell="7"></div>
<div class="game-cell" data-cell="8"></div>
</div>
</div>
<select name="matrix-size" id="matrixMaker" onchange="newMatrixGrid()">
<option value="3">3X3</option>
<option value="4">4X4</option>
<option value="5">5X5</option>
</select>
</br></br></br>
<div id="matrixStage"></div>
<script src="Scripts/index.js"></script>
</body>
</html>
I think you can simplify your isPlayerWinner function by ignoring if you are looking for Xs or Os. You must assume the last token used was not the winner in the previous turn or the game would already be finished.
So if you have matrix = [[0,1,2],[3,4,5],[6,7,8]] and the numbers will be progressively replaced by x or o just check if all items in a combination are equal
function isPlayerWinner(){
for(let i=0; i<3; i++){
const row = matrix[i][0] === matrix[i][1] && matrix[i][1] === matrix[i][2];
const column = matrix[0][i] === matrix[1][i] && matrix[1][i] === matrix[2][i];
if(column || row){
return true
}
}
if(matrix[0][0] === matrix[1][1] && matrix[1][1] === matrix[2][2]){
return true;
}
if(matrix[0][2] === matrix[1][1] && matrix[1][1] === matrix[2][0]){
return true;
}
return false
}
I have a tic tac toe game with minimax algorithm. It works by comparing combinations to a winning ones, adds scores and calls itself recursively, until the score is the highest and then makes it's move. In my snippet the program doesn't work due to a bug in it's function that gives me an infinite loop
const statusDiv = document.querySelector(".status");
const resetDiv = document.querySelector(".reset");
const cellDivs = document.querySelectorAll(".game-cell");
const activeCell = document.querySelector(".active");
document.addEventListener("keydown", (e) => move(e.keyCode));
let matrix = [
["1", "2", "3"],
["4", "5", "6"],
["7", "8", "9"],
];
let winCombos = [
["0", "1", "2"],
["3", "4", "5"],
["6", "7", "8"],
["0", "3", "6"],
["1", "4", "7"],
["2", "5", "8"],
["0", "4", "8"],
["6", "4", "2"],
];
let currentX = 0;
let currentY = 0;
let newElem = matrix[currentY][currentX];
let huPlayer = "x";
let aiPlayer = "o";
// game constants
const xSymbol = "✗";
const oSymbol = "○";
let gameIsRunning = true;
let xIsNext = true;
let winner = null;
// functions
const move = (keyCode) => {
if (gameIsRunning) {
let key = null;
switch (event.keyCode) {
case 38:
key = "up";
break;
case 40:
key = "down";
break;
case 37:
key = "left";
break;
case 39:
key = "right";
break;
case 13:
key = "return";
}
if (key === "up") {
currentY--;
if (currentY < 0) {
currentY = 0;
}
newElem = matrix[currentY][currentX];
} else if (key === "down") {
currentY++;
if (currentY > 2) {
currentY = 2;
}
newElem = matrix[currentY][currentX];
} else if (key === "right") {
currentX++;
if (currentX > 2) {
currentX = 2;
}
newElem = matrix[currentY][currentX];
} else if (key === "left") {
currentX--;
if (currentX < 0) {
currentX = 0;
}
newElem = matrix[currentY][currentX];
} else if (key === "return") {
handleCellClick(false, newElem);
}
cellDivs.forEach((div) => {
div.classList.replace("active", "inactive");
if (div.classList[1] === newElem) {
div.classList.replace("inactive", "active");
}
});
}
};
const letterToSymbol = (letter) => {
return letter === "x" ? xSymbol : oSymbol;
};
const handleWin = (letter) => {
gameIsRunning = false;
winner = letter;
if (winner === "x") {
statusDiv.innerHTML = `${letterToSymbol(winner)} has won!`;
} else {
statusDiv.innerHTML = `<span>${letterToSymbol(winner)} has won!</span>`;
}
};
const checkGameStatus = () => {
const topLeft = cellDivs[0].classList[3];
const topMiddle = cellDivs[1].classList[3];
const topRight = cellDivs[2].classList[3];
const middleLeft = cellDivs[3].classList[3];
const middleMiddle = cellDivs[4].classList[3];
const middleRight = cellDivs[5].classList[3];
const bottomLeft = cellDivs[6].classList[3];
const bottomMiddle = cellDivs[7].classList[3];
const bottomRight = cellDivs[8].classList[3];
// check a winner
if (topLeft && topLeft === topMiddle && topLeft === topRight) {
handleWin(topLeft);
cellDivs[0].classList.add("won");
cellDivs[1].classList.add("won");
cellDivs[2].classList.add("won");
} else if (
middleLeft &&
middleLeft === middleMiddle &&
middleLeft === middleRight
) {
handleWin(middleLeft);
cellDivs[3].classList.add("won");
cellDivs[4].classList.add("won");
cellDivs[5].classList.add("won");
} else if (
bottomLeft &&
bottomLeft === bottomMiddle &&
bottomLeft === bottomRight
) {
handleWin(bottomLeft);
cellDivs[6].classList.add("won");
cellDivs[7].classList.add("won");
cellDivs[8].classList.add("won");
} else if (topLeft && topLeft === middleLeft && topLeft === bottomLeft) {
handleWin(topLeft);
cellDivs[0].classList.add("won");
cellDivs[3].classList.add("won");
cellDivs[6].classList.add("won");
} else if (
topMiddle &&
topMiddle === middleMiddle &&
topMiddle === bottomMiddle
) {
handleWin(topMiddle);
cellDivs[1].classList.add("won");
cellDivs[4].classList.add("won");
cellDivs[7].classList.add("won");
} else if (topRight && topRight === middleRight && topRight === bottomRight) {
handleWin(topRight);
cellDivs[2].classList.add("won");
cellDivs[5].classList.add("won");
cellDivs[8].classList.add("won");
} else if (topLeft && topLeft === middleMiddle && topLeft === bottomRight) {
handleWin(topLeft);
cellDivs[0].classList.add("won");
cellDivs[4].classList.add("won");
cellDivs[8].classList.add("won");
} else if (topRight && topRight === middleMiddle && topRight === bottomLeft) {
handleWin(topRight);
cellDivs[2].classList.add("won");
cellDivs[4].classList.add("won");
cellDivs[6].classList.add("won");
} else if (
topLeft &&
topMiddle &&
topRight &&
middleLeft &&
middleMiddle &&
middleRight &&
bottomMiddle &&
bottomRight &&
bottomLeft
) {
gameIsRunning = false;
statusDiv.innerHTML = "Game is tied!";
} else {
xIsNext = !xIsNext;
if (xIsNext) {
statusDiv.innerHTML = `${letterToSymbol("x")} is next`;
} else {
statusDiv.innerHTML = `<span>${letterToSymbol(oSymbol)} is next</span>`;
}
}
};
// event Handlers
const handleReset = (e) => {
xIsNext = true;
statusDiv.innerHTML = `${letterToSymbol(xSymbol)} is next`;
winner = null;
gameIsRunning = true;
cellDivs.forEach((div) => {
div.classList.replace("active", "inactive");
});
for (const cellDiv of cellDivs) {
cellDiv.classList.remove("x");
cellDiv.classList.remove("o");
cellDiv.classList.remove("won");
}
cellDivs[0].classList.replace("inactive", "active");
};
// event listeners
resetDiv.addEventListener("click", handleReset);
const handleCellClick = (e, elem) => {
if (gameIsRunning) {
let classList;
if (e) {
classList = e.target.classList;
} else if (elem) {
cellDivs.forEach((div) => {
if (Array.from(div.classList).includes(elem)) {
classList = div.classList;
}
});
}
if (!gameIsRunning || classList[3] === "x" || classList[3] === "o") {
return;
}
if (xIsNext) {
classList.add("x");
checkGameStatus();
} else {
classList.add("o");
checkGameStatus();
}
if (e.target) {
cellDivs.forEach((div) => {
div.classList.replace("active", "inactive");
});
e.target.classList.replace("inactive", "active");
}
aiTurn();
}
};
const aiTurn = () => {
window.setTimeout(() => {
if (gameIsRunning) {
let availableSpots = emptySquares(cellDivs);
if (xIsNext) {
availableSpots[0].classList.add("x");
checkGameStatus();
} else {
availableSpots[bestSpot(availableSpots)].classList.add("o");
checkGameStatus();
}
}
}, 300);
};
const emptySquares = (elems) => {
return Array.from(elems).filter((div, index) => {
return !div.classList[3];
});
};
const bestSpot = (origBoard) => {
let isAi = xIsNext ? "x" : "o";
return minimax([...cellDivs], isAi);
};
const checkWin = (board, player) => {
const aiMoves = board.reduce(
(a, e, i) => (e.classList[3] === player ? a.concat(i) : a),
[]
);
console.log(aiMoves);
let gameWon = null;
for (let [index, win] of winCombos.entries()) {
if (win.every((elem) => aiMoves.indexOf(elem) > -1)) {
gameWon = { index: index, player: player };
break;
}
}
console.log(gameWon);
return gameWon;
};
function minimax(newBoard, player) {
var availSpots = emptySquares(newBoard, player);
if (checkWin([...newBoard], huPlayer)) {
return { score: -10 };
} else if (checkWin([...newBoard], aiPlayer)) {
return { score: 10 };
} else if (availSpots.length === 0) {
return { score: 0 };
}
var moves = [];
for (var i = 0; i < availSpots.length; i++) {
var move = {};
move.index = newBoard[availSpots[i]];
newBoard[availSpots[i]] = player;
if (player == aiPlayer) {
var result = minimax(newBoard, huPlayer);
move.score = result.score;
} else {
var result = minimax(newBoard, aiPlayer);
move.score = result.score;
}
newBoard[availSpots[i]] = move.index;
moves.push(move);
}
var bestMove;
if (player === aiPlayer) {
var bestScore = -10000;
for (var i = 0; i < moves.length; i++) {
if (moves[i].score > bestScore) {
bestScore = moves[i].score;
bestMove = i;
}
}
} else {
var bestScore = 10000;
for (var i = 0; i < moves.length; i++) {
if (moves[i].score < bestScore) {
bestScore = moves[i].score;
bestMove = i;
}
}
}
return moves[bestMove];
}
for (const cellDiv of cellDivs) {
cellDiv.addEventListener("click", handleCellClick);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
transition: 0.5s all;
}
body {
display: flex;
font-family: sans-serif;
justify-content: center;
align-items: flex-start;
color: #545454;
}
.container {
background: #39cccc;
padding: 50px;
border-radius: 25px;
}
.title {
text-align: center;
}
.title {
span {
color: #f2ebd3;
}
}
.status-action {
margin-top: 25px;
font-size: 25px;
display: flex;
justify-content: space-around;
height: 30px;
}
.reset {
cursor: pointer;
}
.reset:hover {
color: #f2ebd3;
}
.game-grid {
background: #0da192;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
grid-gap: 15px;
margin-top: 30px;
}
.game-cell {
width: 150px;
height: 150px;
background: #39cccc;
display: flex;
align-items: center;
justify-content: center;
transition: 0.5s all;
border: 5px solid transparent;
&:hover {
cursor: pointer;
}
}
.x,
.o {
cursor: default;
}
.x::after {
content: "✗";
font-size: 100px;
}
.o::after {
content: "○";
color: #f2ebd3;
font-size: 130px;
font-weight: bold;
}
.status {
span {
color: #f2ebd3;
}
}
.won::after {
color: #bd022f;
}
#media only screen and (max-width: 1024px) {
.game-grid {
margin-top: 25px;
grid-gap: 16px;
gap: 10px;
}
.game-cell {
height: 100px;
width: 100px;
}
.x::after {
font-size: 85px;
}
.o::after {
font-size: 85px;
}
}
#media only screen and (max-width: 540px) {
.container {
margin: 25px;
padding: 25px;
}
.game-grid {
gap: 5px;
}
.game-cell {
height: 75px;
width: 75px;
}
.x::after {
font-size: 50px;
}
.o::after {
font-size: 50px;
}
}
.active {
border: 5px solid goldenrod;
}
<div class="container">
<h1 class="title">Tic <span>Tac</span> Toe</h1>
<div class="status-action">
<div class="status">✗ is next</div>
<div class="reset">Reset</div>
</div>
<div class="game-grid">
<div class="game-cell 1 active"></div>
<div class="game-cell 2 inactive"></div>
<div class="game-cell 3 inactive"></div>
<div class="game-cell 4 inactive"></div>
<div class="game-cell 5 inactive"></div>
<div class="game-cell 6 inactive"></div>
<div class="game-cell 7 inactive"></div>
<div class="game-cell 8 inactive"></div>
<div class="game-cell 9 inactive"></div>
</div>
</div>
. I cannot find the exact place where the stop is missing. Please look where I might have missed something.
I have a trouble: when button onclicked, I need to return position of a block and hold it till next onclick. On next click, I need to take position and change it again and hold its number. And make it for eternity xD. But I've done something wrong. Please help.
And one thing more: I need to make a reset position by clicking reset button. Reset must be to the 1.
window.onload = function core() {
// creating elements
let platform = document.createElement('div');
platform.setAttribute('id', 'platform');
document.body.appendChild(platform);
let up = document.createElement('button');
up.setAttribute('id', 'up');
up.setAttribute('class', 'actionButton');
document.body.appendChild(up);
document.getElementById('up').innerHTML = 'up';
let down = document.createElement('button');
down.setAttribute('id', 'down');
down.setAttribute('class', 'actionButton');
document.body.appendChild(down);
document.getElementById('down').innerHTML = 'down';
let left = document.createElement('button');
left.setAttribute('id', 'left');
left.setAttribute('class', 'actionButton');
document.body.appendChild(left);
document.getElementById('left').innerHTML = 'left';
let right = document.createElement('button');
right.setAttribute('id', 'right');
right.setAttribute('class', 'actionButton');
document.body.appendChild(right);
document.getElementById('right').innerHTML = 'right';
let reset = document.createElement('button');
reset.setAttribute('id', 'reset');
reset.setAttribute('class', 'actionButton');
document.body.appendChild(reset);
document.getElementById('reset').innerHTML = 'reset';
// binding platform
let count = 0;
let defaultPosition = 1;
for (let i = 1; i < 101; i++) {
let grid = document.createElement('button');
grid.setAttribute('id', i);
grid.setAttribute('class', 'eachGridBlock');
document.getElementById('platform').appendChild(grid);
count++;
if (count === 10) {
let newLine = document.createElement('br');
document.getElementById('platform').appendChild(newLine);
count = 0;
};
};
// waiting for action
document.getElementById(defaultPosition).setAttribute('class', 'focusedBlock');
console.log(defaultPosition);
document.getElementById('up').onclick = function() {
processUp(defaultPosition)
};
document.getElementById('down').onclick = function() {
processDown(defaultPosition)
};
document.getElementById('left').onclick = function() {
processLeft(defaultPosition)
};
document.getElementById('right').onclick = function() {
processRight(defaultPosition)
};
document.getElementById('reset').onclick = function() {
processReset(defaultPosition)
};
// action functions
function processUp(positionX) {
let nowPosition = positionX;
if (nowPosition <= 10) {
alert('you cant step here');
} else {
let nextPosition = nowPosition - 10;
document.getElementById(nowPosition).setAttribute('class', 'eachGridBlock');
document.getElementById(nextPosition).setAttribute('class', 'focusedBlock');
positionX = nextPosition;
};
console.log(positionX);
return positionX;
};
function processDown(positionX) {
let nowPosition = positionX;
if (nowPosition >= 91) {
alert('you cant step here');
} else {
let nextPosition = nowPosition + 10;
document.getElementById(nowPosition).setAttribute('class', 'eachGridBlock');
document.getElementById(nextPosition).setAttribute('class', 'focusedBlock');
positionX = nextPosition;
};
console.log(positionX);
return positionX;
};
function processLeft(positionX) {
let nowPosition = positionX;
if (nowPosition === 1 || nowPosition === 11 || nowPosition === 21 || nowPosition === 31 || nowPosition === 41 || nowPosition === 51 || nowPosition === 61 || nowPosition === 71 || nowPosition === 81 || nowPosition === 91) {
alert('you cant step here');
} else {
let nextPosition = nowPosition - 1;
document.getElementById(nowPosition).setAttribute('class', 'eachGridBlock');
document.getElementById(nextPosition).setAttribute('class', 'focusedBlock');
positionX = nextPosition;
};
console.log(positionX);
return positionX;
};
function processRight(positionX) {
let nowPosition = positionX;
if (nowPosition === 10 || nowPosition === 20 || nowPosition === 30 || nowPosition === 40 || nowPosition === 50 || nowPosition === 60 || nowPosition === 70 || nowPosition === 80 || nowPosition === 90 || nowPosition === 100) {
alert('you cant step here');
} else {
let nextPosition = nowPosition + 1;
document.getElementById(nowPosition).setAttribute('class', 'eachGridBlock');
document.getElementById(nextPosition).setAttribute('class', 'focusedBlock');
positionX = nextPosition;
};
console.log(positionX);
return positionX;
};
function processReset(positionX) {
let nowPosition = positionX
let nextPosition = 1;
document.getElementById(nowPosition).setAttribute('class', 'eachGridBlock');
document.getElementById(nextPosition).setAttribute('class', 'focusedBlock');
return positionX;
};
};
* {
margin: 0;
padding: 0;
}
#plarform {
width: 500px;
height: 500px;
background-color: #222222;
}
.eachGridBlock {
margin: 0;
padding: 0;
width: 50px;
height: 50px;
background-color: #000044;
border: 0;
}
.focusedBlock {
margin: 0;
padding: 0;
width: 50px;
height: 50px;
background-color: #9900ff;
border: 0;
}
.actionButton {
border: 0;
width: 100px;
height: 35px;
background-color: #999999;
color: #222222;
outline: none;
}
.actionButton:hover {
background-color: #dddddd;
}
<html>
<head>
</head>
<body>
</body>
</html>
You need to update defaultPosition when you call the functions, e.g.
document.getElementById('right').onclick = function() {
defaultPosition = processRight(defaultPosition)
};