Ian Watkins iankwatkins
Lesson 5 · Game Dev Fundamentals

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.

This is the final mechanics lesson. After today, tic-tac-toe is genuinely done — every state has an ending, and the game can loop forever.

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();
}
Order is the whole game's logic

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
}
Why empty the array in place instead of reassigning it

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

  1. Add isBoardFull() and insert the draw check in handleClick, after the win check.
  2. Add resetGame() that restores board, currentPlayer, and isGameOver, then renders.
  3. Add the <button id="reset"> and wire it with addEventListener.
Your task

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

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.

That's all ten lessons! See your course progress and celebration →