skip to Main Content

I’m making a tic tac toe game in JS. Code is below.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tic Tac Toe</title>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="tis.css" />
  </head>
  <body>
    <div id="gameContainer">
      <h1>Tic Tac Toe</h1>
      <h2 id="statusText">Testing...</h2>
      <div id="cellContainer">
        <div cellIndex="0" class="cell"></div>
        <div cellIndex="1" class="cell"></div>
        <div cellIndex="2" class="cell"></div>
        <div cellIndex="3" class="cell"></div>
        <div cellIndex="4" class="cell"></div>
        <div cellIndex="5" class="cell"></div>
        <div cellIndex="6" class="cell"></div>
        <div cellIndex="7" class="cell"></div>
        <div cellIndex="8" class="cell"></div>
      </div>

      <button id="restartBtn">RESTART</button>
    </div>

    <script src="tic.js"></script>
  </body>
</html>

body {
  padding: 0;
  margin: 0;
  font-family: "Press Start 2P", sans-serif;
  background-color: black;
  height: 100vh; /*height is the height of the browser vwindow*/
  text-align: center;
}

h1 {
  color: white;
  text-align: center;
  font-size: 50px;
  margin-top: 10%;
}

#statusText {
  color: white;
  text-align: center;
  font-size: 15px;
  margin-top: 2em; /*1m is margin size of the font size}*/
  margin-bottom: 2em;
}

#cellContainer {
  width: 300px;
  height: 300px;
  margin: 0 auto; /*centre the element to its parent container*/

  color: #fff;
  border: 2px solid white;

  display: grid;
  grid-template: repeat(3, 1fr) / repeat(3, 1fr); /*each column and each row a third of the grid*/
}

.cell {
  border: 2px solid white;
  border-radius: 2px;
  font-weight: bold;
  font-size: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
}


#restartBtn {
  padding: 15px;
  margin: 1.5em;
  border-radius: 10px;
  border: none;
  cursor: pointer;

  font-size: 30px;
  font-family: "Press Start 2P", sans-serif;
  background-color: #d62839;
  color: white;
}

const cells = document.querySelectorAll(".cell");
const statusText = document.querySelector("#statusText");
const restartBtn = document.querySelector("#restartBtn");
const winConditions = [
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8],
  [0, 3, 6],
  [1, 4, 7],
  [2, 5, 8],
  [0, 4, 8],
  [2, 4, 6],
];

let options = ["", "", "", "", "", "", "", "", ""]; //empty board. array of empty trings representing the board  options
let currentPlayer = "X"; //starting player, to keep track
let isGameActive = false; //keep track of wheteher game is running. will be stwitched to true when game is initialised
initialiseGame(); //this function will be called whenever we open the browser
function initialiseGame() {
  cells.forEach((cell) => cell.addEventListener("click", cellClicked)); //for each cell carry out the cell clicked function
  restartBtn.addEventListener("click", restartGame); //when we click restarttn, run the restart game
  statusText.textContent = `Waiting for Player ${currentPlayer} to start game`;
  isGameActive = true;
}

function cellClicked() {
  const cellIndex = this.getAttribute("cellIndex"); //get the attribute. we want the elements with the attribute cellIndex to execute the cunction
  //if the cell isnt empty or game isnt running dont do anything if cell is clicked
  if (options[cellIndex] != "" || !isGameActive) {
    return;
  }
  updateCell(this, cellIndex); //otherwise reun the updateCellFunction with this as a parameter?

  checkWinner();
}

function updateCell(cell, index) {
  options[index] = currentPlayer; //update the placeholders in the options array, aka generate options array using index and place current players value within the index of the cell clicked
  cell.textContent = currentPlayer; //display the above within the cell
  addColour();
}
function addColour() {
  if (currentPlayer == "X") {
    document.querySelector(".cell").style.color = "red";
  }
}

function changePlayer() {
  currentPlayer = currentPlayer == "X" ? "O" : "X";
  statusText.textContent = `Waiting for Player ${currentPlayer} to play turn`;
}

function checkWinner() {
  let roundWon = false;
  for (let i = 0; i < winConditions.length; i++) {
    const condition = winConditions[i]; //we take the index of all the win conditions
    const cellA = options[condition[0]]; //first index in winconditions
    const cellB = options[condition[1]]; //second index in winconditions
    const cellC = options[condition[2]]; //third index in winconditions

    //if we check wincondition's index 0 aka 0,1,2. cell a will be 0, cellb will be 1 cell c will be 2 for index 2 a=3 b=4 c=5, for index 7 a =2 =4 c=6
    //3 cells as 3 indices ber wincondition and 3x3 grid
    if (cellA == "" || cellB == "" || cellC == "") {
      continue; //aka keep loop running if the indices arent complete therefore condition hasnt been met
    }
    //checking if all the indices are Xs or Os for the winning condition
    if (cellA == cellB && cellB == cellC) {
      roundWon = true;
      break; //break out of the for loop if win condition me
    }
  }
  if (roundWon) {
    statusText.textContent = `${currentPlayer} has Won!`;
    isGameActive = false; //end game aka make nothing be able to be clicked anymore since game has been won

    //if options does not include any spaces then update status aka all the board has been played with no winners so far
  } else if (!options.includes("")) {
    statusText.textContent = "Match draw";
    //if noone has won yet and noone has drawn (aka there are still empty cells, run the function to change player)
  } else {
    changePlayer();
  }
}

function restartGame() {
  currentPlayer = "X";
  options = ["", "", "", "", "", "", "", "", ""];
  statusText.textContent = `${currentPlayer}'s turn`;
  cells.forEach((cell) => (cell.textContent = ""));
  isGameActive = true;
}

I am a beginner (as in very beginner), this is my first project. I have managed to make a working game by following a tutorial. I want to make player X one colour, and player O another. So that when X clicks it will the color style of the text content within the cell a specific colour and same for O.

function updateCell(cell, index) {
  options[index] = currentPlayer; //update the placeholders in the options array, aka generate options array using index and place current players value within the index of the cell clicked
  cell.textContent = currentPlayer; //display the above within the cell
  addColour();
}
function addColour() {
  if (currentPlayer == "X") {
    document.querySelector(".cell").style.color = "red";
  }
}

I tried adding a function in the updateCell function that I thought would do so but it only turns the first X red (for reasons I cannot comprehend.

2

Answers


  1. The addColour() function is querying the cell class, not targeting the selected cell. Change it to accept a cell as its parameter. Set only that cell’s color depending on the current player. Like this…

    function updateCell(cell, index) {
      options[index] = currentPlayer; //update the placeholders in the options array, aka generate options array using index and place current players value within the index of the cell clicked
      cell.textContent = currentPlayer; //display the above within the cell
      addColour(cell);  // <- call it with the selected cell
    }
    
    function addColour(cell) {
      // I chose red and green, or you could make these constants
      const color = (currentPlayer == "X") ? "red" : "green";
      cell.style.color = color;
    }
    

    With only that change, the app runs as follows…

    const cells = document.querySelectorAll(".cell");
    const statusText = document.querySelector("#statusText");
    const restartBtn = document.querySelector("#restartBtn");
    const winConditions = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6],
    ];
    
    let options = ["", "", "", "", "", "", "", "", ""]; //empty board. array of empty trings representing the board  options
    let currentPlayer = "X"; //starting player, to keep track
    let isGameActive = false; //keep track of wheteher game is running. will be stwitched to true when game is initialised
    initialiseGame(); //this function will be called whenever we open the browser
    function initialiseGame() {
      cells.forEach((cell) => cell.addEventListener("click", cellClicked)); //for each cell carry out the cell clicked function
      restartBtn.addEventListener("click", restartGame); //when we click restarttn, run the restart game
      statusText.textContent = `Waiting for Player ${currentPlayer} to start game`;
      isGameActive = true;
    }
    
    function cellClicked() {
      const cellIndex = this.getAttribute("cellIndex"); //get the attribute. we want the elements with the attribute cellIndex to execute the cunction
      //if the cell isnt empty or game isnt running dont do anything if cell is clicked
      if (options[cellIndex] != "" || !isGameActive) {
        return;
      }
      updateCell(this, cellIndex); //otherwise reun the updateCellFunction with this as a parameter?
    
      checkWinner();
    }
    
    function updateCell(cell, index) {
      options[index] = currentPlayer; //update the placeholders in the options array, aka generate options array using index and place current players value within the index of the cell clicked
      cell.textContent = currentPlayer; //display the above within the cell
      addColour(cell);
    }
    
    function addColour(cell) {
      const color = (currentPlayer == "X") ? "red" : "green";
      cell.style.color = color;
    }
    
    function changePlayer() {
      currentPlayer = currentPlayer == "X" ? "O" : "X";
      statusText.textContent = `Waiting for Player ${currentPlayer} to play turn`;
    }
    
    function checkWinner() {
      let roundWon = false;
      for (let i = 0; i < winConditions.length; i++) {
        const condition = winConditions[i]; //we take the index of all the win conditions
        const cellA = options[condition[0]]; //first index in winconditions
        const cellB = options[condition[1]]; //second index in winconditions
        const cellC = options[condition[2]]; //third index in winconditions
    
        //if we check wincondition's index 0 aka 0,1,2. cell a will be 0, cellb will be 1 cell c will be 2 for index 2 a=3 b=4 c=5, for index 7 a =2 =4 c=6
        //3 cells as 3 indices ber wincondition and 3x3 grid
        if (cellA == "" || cellB == "" || cellC == "") {
          continue; //aka keep loop running if the indices arent complete therefore condition hasnt been met
        }
        //checking if all the indices are Xs or Os for the winning condition
        if (cellA == cellB && cellB == cellC) {
          roundWon = true;
          break; //break out of the for loop if win condition me
        }
      }
      if (roundWon) {
        statusText.textContent = `${currentPlayer} has Won!`;
        isGameActive = false; //end game aka make nothing be able to be clicked anymore since game has been won
    
        //if options does not include any spaces then update status aka all the board has been played with no winners so far
      } else if (!options.includes("")) {
        statusText.textContent = "Match draw";
        //if noone has won yet and noone has drawn (aka there are still empty cells, run the function to change player)
      } else {
        changePlayer();
      }
    }
    
    function restartGame() {
      currentPlayer = "X";
      options = ["", "", "", "", "", "", "", "", ""];
      statusText.textContent = `${currentPlayer}'s turn`;
      cells.forEach((cell) => (cell.textContent = ""));
      isGameActive = true;
    }
    body {
      padding: 0;
      margin: 0;
      font-family: "Press Start 2P", sans-serif;
      background-color: black;
      height: 100vh;
      /*height is the height of the browser vwindow*/
      text-align: center;
    }
    
    h1 {
      color: white;
      text-align: center;
      font-size: 50px;
      margin-top: 10%;
    }
    
    #statusText {
      color: white;
      text-align: center;
      font-size: 15px;
      margin-top: 2em;
      /*1m is margin size of the font size}*/
      margin-bottom: 2em;
    }
    
    #cellContainer {
      width: 300px;
      height: 300px;
      margin: 0 auto;
      /*centre the element to its parent container*/
      color: #fff;
      border: 2px solid white;
      display: grid;
      grid-template: repeat(3, 1fr) / repeat(3, 1fr);
      /*each column and each row a third of the grid*/
    }
    
    .cell {
      border: 2px solid white;
      border-radius: 2px;
      font-weight: bold;
      font-size: 50px;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    #restartBtn {
      padding: 15px;
      margin: 1.5em;
      border-radius: 10px;
      border: none;
      cursor: pointer;
      font-size: 30px;
      font-family: "Press Start 2P", sans-serif;
      background-color: #d62839;
      color: white;
    }
    <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Tic Tac Toe</title>
      <link rel="preconnect" href="https://fonts.googleapis.com" />
      <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
      <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet" />
      <link rel="stylesheet" href="tis.css" />
    </head>
    
    <body>
      <div id="gameContainer">
        <h1>Tic Tac Toe</h1>
        <h2 id="statusText">Testing...</h2>
        <div id="cellContainer">
          <div cellIndex="0" class="cell"></div>
          <div cellIndex="1" class="cell"></div>
          <div cellIndex="2" class="cell"></div>
          <div cellIndex="3" class="cell"></div>
          <div cellIndex="4" class="cell"></div>
          <div cellIndex="5" class="cell"></div>
          <div cellIndex="6" class="cell"></div>
          <div cellIndex="7" class="cell"></div>
          <div cellIndex="8" class="cell"></div>
        </div>
    
        <button id="restartBtn">RESTART</button>
      </div>
    
      <script src="tic.js"></script>
    </body>
    Login or Signup to reply.
  2. In the function addColor, you have this line:

    document.querySelector(".cell").style.color = "red";
    

    The querySelector searches the DOM for elements matching the cell class, and returns the first result it finds. Then your code changes the color of that cell to red. It gives no regard to the cell that was actually clicked, it just changes the first cell in the DOM that it finds.

    To fix this, you can make the changeColour function take the cell element as a parameter, like this:

    function addColour(cell) {
      if (currentPlayer == "X") {
        cell.style.color = "red";
      }
    }
    

    Then, in updateCell, instead of addColour(), use addColour(cell). See here for a working example.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search