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
You can count the number of zeros remaining in the
board
array. When there are none left, it’s tie usingif (board.filter(x => x == 0).length == 0) return 'T'
: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:
Hope this helps 🙂