skip to Main Content

I am so close to finishing this Tic-Tac-Toe game. It plays through and announces the winner but I can’t get it to announce if there is a tie at the end of the game if there is no winner.

I tried to implement a return in my getWinner() function but it ends the game after just one click. What am I missing here? I’m thinking something is wrong with my for loop but can’t figure it out.

I tried ending my function with a return outside of the loop but it ends the game prematurely.

/*----- constants -----*/
//Display of background color for the selected box. Player 1 is 1, pink and X. Player 2 is -1, green and O
const COLORS = {
  '0': 'white',
  '1': 'pink',
  '-1': 'lightgreen'
};

//Display for selected box. X or O
const MARK = {
  '0': '',
  '1': 'X',
  '-1': 'O'
};

//Winning combos to win the math
const COMBOS = [
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8],
  [6, 4, 2],
  [0, 4, 8],
  [0, 3, 6],
  [1, 4, 7],
  [2, 5, 8]
];

/*----- state variables -----*/
let board; //array of 9 boxes
let turn; // 1 or -1
let winner; //null = no winner; 1 or -1 winner; 'T' = Tie

/*----- cached elements  -----*/
const message = document.querySelector('h1');
const resetButton = document.querySelector('button');

/*----- event listeners -----*/
document.getElementById('board').addEventListener('click', handleClick);
resetButton.addEventListener('click', init);

/*----- functions -----*/
init();
//Initializes state and calls render()
function init() {
  board = [0, 0, 0, 0, 0, 0, 0, 0, 0];
  turn = 1;
  winner = null;
  render();
}

//Visualizes all state in the DOM
function render() {
  renderBoard();
  renderMessage();
}

//Iterate over the squares in the board
function renderBoard() {
  board.forEach(function(boardArr, boardIdx) {
    const squareId = `box-${boardIdx}`;
    const squareEl = document.getElementById(squareId);
    //styles for player selection
    squareEl.style.backgroundColor = COLORS[boardArr];
    squareEl.innerHTML = MARK[boardArr];
    squareEl.style.display = 'flex';
    squareEl.style.justifyContent = 'center';
    squareEl.style.alignItems = 'center';
    squareEl.style.fontSize = '19vmin';
  });
}

//Display whose turn it is and the winner
function renderMessage() {
  if (winner === 'T') {
    message.innerHTML = 'Tie Game! Game Over!';
  } else if (winner) {
    message.innerHTML = `Player ${MARK[winner]} Wins!`;
  } else {
    message.innerHTML = `Player ${MARK[turn]}'s Turn`;
  }
}

//Get index of the clicked box
function handleClick(event) {
  const boxIdx = parseInt(event.target.id.replace('box-', ''));
  //if statement in case someone clicks outside box, the box is filled or there is a winner
  if (isNaN(boxIdx) || board[boxIdx] || winner)
    return;
  //update state of board with the current turn value
  board[boxIdx] = turn;
  //switch player turn
  turn *= -1;
  // check for a winner
  winner = getWinner();
  render();
}

//Check for a winner in the state. 1(X) or -1(O), 'T' for Tie, null for no winner yet
//Got really stuck on this section. Had to peak at the solution and research Math.abs function
function getWinner() {
  for (let i = 0; i < COMBOS.length; i++) {
    if (Math.abs(board[COMBOS[i][0]] + board[COMBOS[i][1]] + board[COMBOS[i][2]]) === 3) {
      return board[COMBOS[i][0]];
    } else if (board.includes(null)) {
      return null;
    }
  }
  //return 'T'; 
  //When I impliment this, the game ends after just one move.
}
* {
  box-sizing: border-box;
}

body {
  height: 100vh;
  margin: 0;
  font-family: 'Raleway', sans-serif;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

header {
  margin-top: 5vmin;
  font-size: 10vmin;
  color: darkslategrey;
}

h1 {
  color: slategrey;
}

#board {
  display: grid;
  grid-template-columns: repeat(3, 20vmin);
  grid-template-rows: repeat(3, 20vmin);
}

#board>div {
  border: 0.5vmin solid slategrey;
}

button {
  margin-top: 5vmin;
  margin-bottom: 5vmin;
  padding: 2vmin;
  font-size: 3vmin;
  border-radius: 4vmin;
  border: 0.5vmin solid lightslategrey;
  background-color: aliceblue;
  color: darkslategrey;
}

button:hover {
  color: azure;
  background-color: cadetblue;
}
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@100&display=swap" rel="stylesheet">

<header>Tic-Tac-Toe</header>
<h1>X's Turn</h1>
<section id="board">
  <div id="box-0"></div>
  <div id="box-1"></div>
  <div id="box-2"></div>
  <div id="box-3"></div>
  <div id="box-4"></div>
  <div id="box-5"></div>
  <div id="box-6"></div>
  <div id="box-7"></div>
  <div id="box-8"></div>
</section>
<button>Reset Match</button>

2

Answers


  1. You can count the number of zeros remaining in the board array. When there are none left, it’s tie using if (board.filter(x => x == 0).length == 0) return 'T':

    /*----- constants -----*/
    //Display of background color for the selected box. Player 1 is 1, pink and X. Player 2 is -1, green and O
    const COLORS = {
      '0': 'white',
      '1': 'pink',
      '-1': 'lightgreen'
    };
    
    //Display for selected box. X or O
    const MARK = {
      '0': '',
      '1': 'X',
      '-1': 'O'
    };
    
    //Winning combos to win the math
    const COMBOS = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [6, 4, 2],
      [0, 4, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8]
    ];
    
    /*----- state variables -----*/
    let board; //array of 9 boxes
    let turn; // 1 or -1
    let winner; //null = no winner; 1 or -1 winner; 'T' = Tie
    
    /*----- cached elements  -----*/
    const message = document.querySelector('h1');
    const resetButton = document.querySelector('button');
    
    /*----- event listeners -----*/
    document.getElementById('board').addEventListener('click', handleClick);
    resetButton.addEventListener('click', init);
    
    /*----- functions -----*/
    init();
    //Initializes state and calls render()
    function init() {
      board = [0, 0, 0, 0, 0, 0, 0, 0, 0];
      turn = 1;
      winner = null;
      render();
    }
    
    //Visualizes all state in the DOM
    function render() {
      renderBoard();
      renderMessage();
    }
    
    //Iterate over the squares in the board
    function renderBoard() {
      board.forEach(function(boardArr, boardIdx) {
        const squareId = `box-${boardIdx}`;
        const squareEl = document.getElementById(squareId);
        //styles for player selection
        squareEl.style.backgroundColor = COLORS[boardArr];
        squareEl.innerHTML = MARK[boardArr];
        squareEl.style.display = 'flex';
        squareEl.style.justifyContent = 'center';
        squareEl.style.alignItems = 'center';
        squareEl.style.fontSize = '19vmin';
      });
    }
    
    //Display whose turn it is and the winner
    function renderMessage() {
      if (winner === 'T') {
        message.innerHTML = 'Tie Game! Game Over!';
      } else if (winner) {
        message.innerHTML = `Player ${MARK[winner]} Wins!`;
      } else {
        message.innerHTML = `Player ${MARK[turn]}'s Turn`;
      }
    }
    
    //Get index of the clicked box
    function handleClick(event) {
      const boxIdx = parseInt(event.target.id.replace('box-', ''));
      //if statement in case someone clicks outside box, the box is filled or there is a winner
      if (isNaN(boxIdx) || board[boxIdx] || winner)
        return;
      //update state of board with the current turn value
      board[boxIdx] = turn;
      //switch player turn
      turn *= -1;
      // check for a winner
      winner = getWinner();
      render();
    }
    
    //Check for a winner in the state. 1(X) or -1(O), 'T' for Tie, null for no winner yet
    //Got really stuck on this section. Had to peak at the solution and research Math.abs function
    function getWinner() {
      for (let i = 0; i < COMBOS.length; i++) {
        if (Math.abs(board[COMBOS[i][0]] + board[COMBOS[i][1]] + board[COMBOS[i][2]]) === 3) {
          return board[COMBOS[i][0]];
        } else if (board.includes(null)) {
          return null;
        }
        if (board.filter(x => x == 0).length == 0) return 'T';
      }
      //return 'T'; 
      //When I impliment this, the game ends after just one move.
    }
    * {
      box-sizing: border-box;
    }
    
    body {
      height: 100vh;
      margin: 0;
      font-family: 'Raleway', sans-serif;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }
    
    header {
      margin-top: 5vmin;
      font-size: 10vmin;
      color: darkslategrey;
    }
    
    h1 {
      color: slategrey;
    }
    
    #board {
      display: grid;
      grid-template-columns: repeat(3, 20vmin);
      grid-template-rows: repeat(3, 20vmin);
    }
    
    #board>div {
      border: 0.5vmin solid slategrey;
    }
    
    button {
      margin-top: 5vmin;
      margin-bottom: 5vmin;
      padding: 2vmin;
      font-size: 3vmin;
      border-radius: 4vmin;
      border: 0.5vmin solid lightslategrey;
      background-color: aliceblue;
      color: darkslategrey;
    }
    
    button:hover {
      color: azure;
      background-color: cadetblue;
    }
    <link href="https://fonts.googleapis.com/css2?family=Raleway:wght@100&display=swap" rel="stylesheet">
    
    <header>Tic-Tac-Toe</header>
    <h1>X's Turn</h1>
    <section id="board">
      <div id="box-0"></div>
      <div id="box-1"></div>
      <div id="box-2"></div>
      <div id="box-3"></div>
      <div id="box-4"></div>
      <div id="box-5"></div>
      <div id="box-6"></div>
      <div id="box-7"></div>
      <div id="box-8"></div>
    </section>
    <button>Reset Match</button>
    Login or Signup to reply.
  2. one way for a solution could be to add a counter of the clicked fields. Then you could check in your getWinner function this counter at your tie line. This line then should only hit if all fields have been clicked an no winner is found.

    Like so:

    function getWinner() {
      for (let i = 0; i < COMBOS.length; i++) {
        if (Math.abs(board[COMBOS[i][0]] + board[COMBOS[i][1]] + board[COMBOS[i][2]]) === 3) {
          return board[COMBOS[i][0]];
        } else if (board.includes(null)) {
          return null;
        }
      }
      if(filledFields >= 9)
      {
          return 'T';
      }
    }
    

    Hope this helps 🙂

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