Home > other >  if statement not running || 0r else if inside a forEach loop for tic tac toe game over logic
if statement not running || 0r else if inside a forEach loop for tic tac toe game over logic

Time:01-12

I am trying to get console.log("game over"), when three specified cells are clicked. The if statement runs the code once but then it doesn't run the || case or the else if.

    "use strict";

     const gameClock = document.querySelector(".clock");
    playerTitle = document.querySelector(".player-title");
    const ticTacToeBoard = document.querySelector(".tic-tac-toe-container");
    const cells = document.querySelectorAll(".cell");
    const startPauseBtn = document.querySelector(".start-btn");
    const resetBtn = document.querySelector(".resetBtn");
    const cellsClicked = [];
    const winningMoves = [
      "box-1",
      "box-2",
      "box-3",
      "box-4",
      "box-5",
      "box-6",
      "box-7",
      "box-8",
      "box-9",
    ];
    let seconds = 0;
    let minutes = 0;
    let displaySeconds = 0;
    let displayMinutes = 0;
    let interval = null;
    let stopWatchStatus = "stopped";

    ticTacToeBoard.classList.add("player-turn");
    //functions

    const playerMove = () => {
      cells.forEach((cell) => {
         const playerEventListener = () => {
          if (ticTacToeBoard.classList.contains("player-turn")) {
            let currentPlayer = document.createElement("p");
            currentPlayer.classList.add("player-1");
            currentPlayer.innerHTML = "X";
            cell.append(currentPlayer);
            playerTitle.innerHTML = "Player: 2";
            ticTacToeBoard.classList.toggle("player-turn");
            cellsClicked.push(cell.getAttribute("id"));
            if (
              cellsClicked[0] === winningMoves[0] &&
              cellsClicked[1] === winningMoves[1] &&
              cellsClicked[2] === winningMoves[2]
            ) {
              console.log("game over");
            } else if (
              cellsClicked[3] === winningMoves[3] &&
              cellsClicked[4] === winningMoves[4] &&
              cellsClicked[5] === winningMoves[5]
             ) {
              console.log("game over");
            } else if (
              cellsClicked[6] === winningMoves[6] &&
              cellsClicked[7] === winningMoves[7] &&
              cellsClicked[8] === winningMoves[8]
            ) {
              console.log("game over");
            } else {
              console.log(false);
              console.log(cellsClicked, winningMoves);
            }
          } else {
            let currentPlayer = document.createElement("p");
            currentPlayer.classList.add("player-2");
            currentPlayer.innerHTML = "O";
            cell.append(currentPlayer);
            playerTitle.innerHTML = "Player: 1";
            ticTacToeBoard.classList.toggle("player-turn");
            if (
              (cell.contains(currentPlayer) &&
                cell.getAttribute("id") === "box-1" &&
                cell.getAttribute("id") === "box-2" &&
                cell.getAttribute("id") === "box-3") ||
              (cell.contains(currentPlayer) &&
                cell.getAttribute("id") === "box-4" &&
                cell.getAttribute("id") === "box-5" &&
                cell.getAttribute("id") === "box-6") ||
               (cell.contains(currentPlayer) &&
                cell.getAttribute("id") === "box-7" &&
                cell.getAttribute("id") === "box-8" &&
                cell.getAttribute("id") === "box-9") ||
              (cell.contains(currentPlayer) &&
                cell.getAttribute("id") === "box-1" &&
                cell.getAttribute("id") === "box-4" &&
                cell.getAttribute("id") === "box-7") ||
              (cell.contains(currentPlayer) &&
                cell.getAttribute("id") === "box-2" &&
                cell.getAttribute("id") === "box-5" &&
                cell.getAttribute("id") === "box-8") ||
              (cell.contains(currentPlayer) &&
                cell.getAttribute("id") === "box-3" &&
                cell.getAttribute("id") === "box-6" &&
                cell.getAttribute("id") === "box-9") ||
              (cell.contains(currentPlayer) &&
                cell.getAttribute("id") === "box-1" &&
                cell.getAttribute("id") === "box-5" &&
                cell.getAttribute("id") === "box-9") ||
               (cell.contains(currentPlayer) &&
                cell.getAttribute("id") === "box-3" &&
                cell.getAttribute("id") === "box-5" &&
                cell.getAttribute("id") === "box-7")
            ) {
               console.log("game over");
             }
          }
        };

        cell.addEventListener("click", playerEventListener, { once: true });
      });
    };
    const stopWatch = () => {
      seconds  ;
      if (seconds / 60 === 1) {
        seconds = 0;
        minutes  ;
        if (minutes / 60 === 1) {
          minutes = 0;
        }
      }
      if (seconds < 10) {
        displaySeconds = "0"   seconds.toString();
      } else {
        displaySeconds = seconds;
      }
      if (minutes < 10) {
        displayMinutes = "0"   minutes.toString();
      } else {
        displayMinutes = minutes;
       }
      gameClock.innerHTML = `${displayMinutes}:${displaySeconds}`;
    };

    const startStop = () => {
      if (stopWatchStatus === "stopped") {
        interval = setInterval(stopWatch, 1000);
        stopWatchStatus = "started";
        startPauseBtn.textContent = "Pause";
      } else {
        clearInterval(interval);
        stopWatchStatus = "stopped";
        startPauseBtn.textContent = "Start Game";
      }
    };

    const startGame = () => {
      startPauseBtn.addEventListener("click", () => {
        if (startPauseBtn.textContent === "Start Game") {
          startPauseBtn.textContent = "Pause";
          startStop();
         } else if ((startPauseBtn.textContent = "Pause")) {
          startPauseBtn.textContent = "Start Game";
          startStop();
        }
      });
    };

    startGame();
    playerMove();

I tried both matching the cellsClicked and winningMoves index and I tried to see if cell.getAttribute("id") === "box-1", box-2, etc both of them have the same result the first case runs then after that nothing consoles.

CodePudding user response:

Introduction

There are some problems I spotted in your code and I decided to make some additions to make things more clear. In particular, since I'm holding the game state in the dom, I added some superflous data attributes that will show with no ambiguity and no math to which row/col belong each cell.

I'm not very proud of how I determines the winning position because I'm sure it could be much more efficient than that... anyway just my 2 cents.

Problems spotted:

As I pointed out in the comments, you have an odd strategy in your if conditions checking if the same value matches different values in AND.

//...
cell.getAttribute("id") === "box-1" &&
cell.getAttribute("id") === "box-2" &&
//...

of course it will always return false.

Another problem I spotted was in your playerEventListener where you take for granted the variable cell. Inside an event handler, to grab the element firing the event you should use event.target.

Refactor:

To simplify the scenario I stripped away part of your logic that wasn't strictly bound to the game itself (the clock, the start/reset buttons, the initialization of the vars in the global scope).

So here you have 2 main functions: isGameOver and onCellClick.

onCellClick(event)

It's the callback passed to addEventListener to listen for the click event on the whole board. I'm using event delegation here so that there's one event handler on the parent element that will be triggered also when the event will bubble from the children cells.

The whole state of the board is hold inside the dom itself so that each cell now has data attributes indicating: the column, the row, the cell nr, the player marking that cell.

//the click event handler on the board
const onCellClick = (event) => {    
  //the clicked cell comes from the passed event argument
  const clickedCell = event.target;

  //retrieves the player index having the previous move (1 or 2)
  const prevPlayer = ticTacToeBoard.dataset.previousplayer;

  //builds the object for the current player
  const currPlayer = (prevPlayer == 1) ? { i: 2, symb: 'O'} : { i: 1, symb: 'X'};

  //shows the move on the tic-tac-toe container
  clickedCell.textContent = currPlayer.symb;  
  playerTitle.innerHTML = `Player: ${currPlayer.i} moved`;                
  

  //sets the data-ownedBy as the mark placed by the current player
  clickedCell.dataset.ownedBy = currPlayer.symb;

  //if it's game over...
  const gameover = isGameOver(clickedCell);     
  if(gameover){    
    playerTitle.innerHTML = `Player${currPlayer.i} won the game!`;                
    playerTitle.style.background = 'yellow';
    ticTacToeBoard.classList.add('gameover');
  }

  //updates the latest move played by
  ticTacToeBoard.dataset.previousplayer = currPlayer.i;                
}

isGameOver(cell)

This is called by the click event handler and it will just check, starting from the currenct cell, if there's any winning combination for the current player on the same row, same column or same diagonal(s if any).

//returns true if there's any winning position on the board
const isGameOver = (cell) => {  
  
  //returns a selector fetching elements with the data attributes set in o argument
  const getSelector = (o)=>{
    let selector = '.cell';
    if(o.nr)
      selector  = `[data-nr="${o.nr}"]`; 
    if(o.row)
      selector  = `[data-row="${o.row}"]`;
    if(o.col)
      selector  = `[data-col="${o.col}"]`;            
    return selector  = `[data-owned-by="${o.symb}"]`;
  };
    
  //the selectors for the winning lines to check for
  const rowSelector = getSelector({row: cell.dataset.row, symb: cell.dataset.ownedBy});
  const colSelector = getSelector({col: cell.dataset.col, symb: cell.dataset.ownedBy});
  const diagSelectors = [];

  //if cellnr is odd (only odd cells are into a diagonal)
  if(parseInt(cell.dataset.nr) % 2 == 1){              
    //pushes all the diagonals to which the currNr belongs to
    const diagonals = [];
    if ([1,5,9].includes(parseInt(cell.dataset.nr)))
      diagonals.push([1,5,9]);
    if ([3,5,7].includes(parseInt(cell.dataset.nr)))
      diagonals.push([3,5,7]);                
    //for each of those diagonals
    diagonals.forEach(diagonal =>{
      //adds to diagSelectors the corresponding diagonal selector
      const diagSelector = diagonal.reduce(
        (acc, curr)=>{
          acc  = `,${getSelector({nr:curr, symb:cell.dataset.ownedBy})}`;
          return acc;
        },
        ''
      ).substring(1);              
      diagSelectors.push(diagSelector);      
    });
  }        
    
  //returns true if there's any winning position
  if (document.querySelectorAll(rowSelector).length == 3)
    return true
  if (document.querySelectorAll(colSelector).length == 3)                
    return true
  for(const diagSelector of diagSelectors){
    if(document.querySelectorAll(diagSelector).length == 3){
      return true
    }            
  }

  //otherwise false
  return false;
}

Working demo:

let playerTitle = document.querySelector(".player-title");
const ticTacToeBoard = document.querySelector(".tic-tac-toe-container");

//returns true if there's any winning position on the board
const isGameOver = (cell) => {  
  
  //returns a selector fetching elements with the data attributes set in o argument
  const getSelector = (o)=>{
    let selector = '.cell';
    if(o.nr)
      selector  = `[data-nr="${o.nr}"]`; 
    if(o.row)
      selector  = `[data-row="${o.row}"]`;
    if(o.col)
      selector  = `[data-col="${o.col}"]`;            
    return selector  = `[data-owned-by="${o.symb}"]`;
  };
    
  //the selectors for the winning lines to check for
  const rowSelector = getSelector({row: cell.dataset.row, symb: cell.dataset.ownedBy});
  const colSelector = getSelector({col: cell.dataset.col, symb: cell.dataset.ownedBy});
  const diagSelectors = [];

  //if cellnr is odd (only odd cells are into a diagonal)
  if(parseInt(cell.dataset.nr) % 2 == 1){              
    //pushes all the diagonals to which the currNr belongs to
    const diagonals = [];
    if ([1,5,9].includes(parseInt(cell.dataset.nr)))
      diagonals.push([1,5,9]);
    if ([3,5,7].includes(parseInt(cell.dataset.nr)))
      diagonals.push([3,5,7]);                
    //for each of those diagonals
    diagonals.forEach(diagonal =>{
      //adds to diagSelectors the corresponding diagonal selector
      const diagSelector = diagonal.reduce(
        (acc, curr)=>{
          acc  = `,${getSelector({nr:curr, symb:cell.dataset.ownedBy})}`;
          return acc;
        },
        ''
      ).substring(1);              
      diagSelectors.push(diagSelector);      
    });
  }        
    
  //returns true if there's any winning position
  if (document.querySelectorAll(rowSelector).length == 3)
    return true
  if (document.querySelectorAll(colSelector).length == 3)                
    return true
  for(const diagSelector of diagSelectors){
    if(document.querySelectorAll(diagSelector).length == 3){
      return true
    }            
  }

  //otherwise false
  return false;
}

//the click event handler on the board
const onCellClick = (event) => {    
  //the clicked cell comes from the passed event argument
  const clickedCell = event.target;

  if(clickedCell.dataset.ownedBy)
    return;

  //retrieves the player index having the previous move (1 or 2)
  const prevPlayer = ticTacToeBoard.dataset.previousplayer;

  //builds the object for the current player
  const currPlayer = (prevPlayer == 1) ? { i: 2, symb: 'O'} : { i: 1, symb: 'X'};

  //shows the move on the tic-tac-toe container
  clickedCell.textContent = currPlayer.symb;  
  playerTitle.innerHTML = `Player: ${currPlayer.i} moved`;                
  
  //sets the data-ownedBy as the mark placed by the current player
  clickedCell.dataset.ownedBy = currPlayer.symb;

  //if it's game over...
  const gameover = isGameOver(clickedCell);     
  if(gameover){    
    playerTitle.innerHTML = `Player${currPlayer.i} won the game!`;                
    playerTitle.style.background = 'yellow';
    ticTacToeBoard.classList.add('gameover');
  }

  //updates the latest move played by
  ticTacToeBoard.dataset.previousplayer = currPlayer.i;                
}

//adds the click event listener to the whole ticTacToeContainer
ticTacToeBoard.addEventListener("click", onCellClick);
*{
  box-sizing: border-box;
}

body, html{
  font-size: 20px;
  margin: 0;
  padding: 0;
  font-family: sans-serif;
}

.player-title{
  width: 100%;
  text-align: center;
  padding: 1em;    
  font-weight: 600;
}

.tic-tac-toe-container{
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  margin: 1rem auto;  
  width: fit-content;
  gap: 2px;
}

.cell{
  outline: solid 2px gray;  
  width: 3em;
  height: 3em;
  cursor: pointer;
  font-weight: 600;
  font-size: 2rem;
  
  display: flex;
  align-items: center;
  justify-content: center;
}

.gameover{
  pointer-events: none;
  background: lightgray;
}
<div >Player 1, make the first move!</div>
<div >
  <div id="box-1"  data-nr="1" data-row="1" data-col="1"></div>
  <div id="box-2"  data-nr="2" data-row="1" data-col="2"></div>
  <div id="box-3"  data-nr="3" data-row="1" data-col="3"></div>
  <div id="box-4"  data-nr="4" data-row="2" data-col="1"></div>
  <div id="box-5"  data-nr="5" data-row="2" data-col="2"></div>
  <div id="box-6"  data-nr="6" data-row="2" data-col="3"></div>
  <div id="box-7"  data-nr="7" data-row="3" data-col="1"></div>
  <div id="box-8"  data-nr="8" data-row="3" data-col="2"></div>
  <div id="box-9"  data-nr="9" data-row="3" data-col="3"></div>
</div>

  • Related