Search code examples
javascriptprojecttic-tac-toe

How do I change the colour of a player in tic-tac-toe game?


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.


Solution

  • 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.