Draws and the Reset Flow
Your game can be won. But two endings are still missing: the cat's game, where the board fills with no winner, and the ability to play again without refreshing the page. Close these and you have a complete game.
Part 1: Detecting a draw
A draw is the quiet ending: every square filled, nobody won. The
key insight about ordering — you only check for a
draw after you've confirmed there's no win. A
full board with a winning line is a win, not a draw. So
the sequence in handleClick becomes: place →
check win → check draw → flip.
The question "is the board full?" has a clean one-line answer. A board is full when none of its slots are empty:
function isBoardFull() {
return board.every(cell => cell !== "");
}
Now wire it into the handler, in the right order:
function handleClick(i) {
if (board[i] !== "" || isGameOver) return;
board[i] = currentPlayer;
if (checkWin()) { // 1. win takes priority
isGameOver = true;
render();
statusEl.textContent = currentPlayer.toUpperCase() + " wins!";
return;
}
if (isBoardFull()) { // 2. no win + full board = draw
isGameOver = true;
render();
statusEl.textContent = "It's a draw!";
return;
}
currentPlayer = currentPlayer === "x" ? "o" : "x"; // 3. game continues
render();
}
Read those three branches top to bottom: win beats draw, draw beats continue. That ordering is the rules of tic-tac-toe expressed in code. Swap the win and draw checks and a winning final move would be miscalled a draw. The sequence encodes priority.
Part 2: Play again — the reset
Resetting is a beautiful test of everything you've built, because it asks one question: what is the complete starting state of the game? To reset, you set every state variable back to its opening value, then render. Nothing else.
List your state: the board,
currentPlayer, and isGameOver. Reset
is just restoring all three:
function resetGame() {
for (let i = 0; i < board.length; i++) {
board[i] = ""; // empty every slot
}
currentPlayer = "x"; // x starts again
isGameOver = false; // unlock the board
render(); // redraw the fresh state
}
You might want to write board = ["", "", ...].
You can't — board is a
const. But more importantly, looping and
clearing keeps the same array that the rest of your
code already references. This connects to that open thread:
const stops you reassigning the variable, but
the array's contents are always yours to change.
Reset is the perfect example.
Wiring the button
Add a button to index.html and connect it once,
when the script loads:
<button id="reset">Play again</button>
document.querySelector("#reset")
.addEventListener("click", resetGame);
Live demo
Play to a win or force a draw (the board fills with no three-in-a-row). Then hit Play again and the game resets to a clean opening.
Build it yourself
-
Add
isBoardFull()and insert the draw check inhandleClick, after the win check. -
Add
resetGame()that restores board,currentPlayer, andisGameOver, then renders. -
Add the
<button id="reset">and wire it withaddEventListener.
Two checks. (1) Force a real draw and confirm the status reads "It's a draw!" not a win. (2) Win a game, hit Play again, and win a second game without refreshing the page. If the second game works end to end, your reset truly restored every piece of state — that's the proof. If something carries over from the first game (a stuck "wins!" message, a locked board), you missed a state variable. Which one?
What you just learned — and what you actually built
-
Array predicates:
.every()(all) and.some()(any) replace flag-and-loop boilerplate. - Priority via ordering: win > draw > continue. The branch order is the rulebook.
-
Reset = restore all state: the cleanest
definition of your game's state is "everything
resetGamehas to touch." -
Passing a function vs calling it:
resetGamenotresetGame()as a listener.
Step back and look at what you have. A complete game with a real loop: state → render → input → update → render, with every ending handled and the whole thing replayable. That is not a tic-tac-toe skill. That is the game skill. Snake is this loop with a moving array. A platformer is this loop running 60 times a second. You now own the shape.
checkWin return the
line). Add a score counter across games. Or start a brand-new
game from a blank folder to prove the loop is yours. When you're
ready to publish a small game for real players,
itch.io is where
browser games go.