Write a simple game that plays tic-tac-toe against a h

Project: Initial tests · Exported 09/08/2026, 16:02:30
Project workspace has changed since this chat last worked on it · current checkpoint Main revision 2. Your next message will use the current project workspace.
Konstantin

Write a simple game that plays tic-tac-toe against a human player

Main candidate · GPT-5.6 Solcandidate
GPTMain · GPT-5.6 Sol

Save the following as tic-tac-toe.html, then open it in a web browser:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tic-Tac-Toe</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      background: #f4f4f4;
    }

    #board {
      display: grid;
      grid-template-columns: repeat(3, 100px);
      gap: 6px;
      justify-content: center;
      margin: 25px auto;
    }

    .cell {
      width: 100px;
      height: 100px;
      border: none;
      border-radius: 8px;
      background: white;
      box-shadow: 0 2px 6px #aaa;
      font-size: 48px;
      font-weight: bold;
      cursor: pointer;
    }

    .cell:hover:empty {
      background: #e8f0ff;
    }

    #status {
      font-size: 20px;
      min-height: 24px;
    }

    #restart {
      padding: 10px 20px;
      border: none;
      border-radius: 6px;
      background: #2864dc;
      color: white;
      font-size: 16px;
      cursor: pointer;
    }

    #restart:hover {
      background: #174cae;
    }
  </style>
</head>
<body>
  <h1>Tic-Tac-Toe</h1>
  <p>You are X. The computer is O.</p>

  <div id="status">Your turn</div>
  <div id="board"></div>
  <button id="restart">Restart Game</button>

  <script>
    const boardElement = document.getElementById("board");
    const statusElement = document.getElementById("status");
    const restartButton = document.getElementById("restart");

    const winningLines = [
      [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 board;
    let gameOver;
    let computerThinking;

    function startGame() {
      board = Array(9).fill("");
      gameOver = false;
      computerThinking = false;
      statusElement.textContent = "Your turn";
      renderBoard();
    }

    function renderBoard() {
      boardElement.innerHTML = "";

      board.forEach((value, index) => {
        const cell = document.createElement("button");
        cell.className = "cell";
        cell.textContent = value;
        cell.addEventListener("click", () => playerMove(index));
        boardElement.appendChild(cell);
      });
    }

    function playerMove(index) {
      if (gameOver || computerThinking || board[index] !== "") return;

      board[index] = "X";
      renderBoard();

      if (finishTurn("X")) return;

      computerThinking = true;
      statusElement.textContent = "Computer is thinking...";

      setTimeout(computerMove, 400);
    }

    function computerMove() {
      // Win if possible, otherwise block the player,
      // otherwise choose a random available square.
      let move = findWinningMove("O");

      if (move === null) {
        move = findWinningMove("X");
      }

      if (move === null && board[4] === "") {
        move = 4;
      }

      if (move === null) {
        const available = board
          .map((value, index) => value === "" ? index : null)
          .filter(index => index !== null);

        move = available[Math.floor(Math.random() * available.length)];
      }

      board[move] = "O";
      computerThinking = false;
      renderBoard();

      if (!finishTurn("O")) {
        statusElement.textContent = "Your turn";
      }
    }

    function findWinningMove(symbol) {
      for (let i = 0; i < board.length; i++) {
        if (board[i] !== "") continue;

        board[i] = symbol;
        const wins = hasWon(symbol);
        board[i] = "";

        if (wins) return i;
      }

      return null;
    }

    function hasWon(symbol) {
      return winningLines.some(line =>
        line.every(index => board[index] === symbol)
      );
    }

    function finishTurn(symbol) {
      if (hasWon(symbol)) {
        gameOver = true;
        statusElement.textContent =
          symbol === "X" ? "You win!" : "Computer wins!";
        return true;
      }

      if (board.every(cell => cell !== "")) {
        gameOver = true;
        statusElement.textContent = "It's a draw!";
        return true;
      }

      return false;
    }

    restartButton.addEventListener("click", startGame);
    startGame();
  </script>
</body>
</html>
Deterministic validation0 pass · 0 fail · 1 warn
Deterministic validation · 0 passed · 0 failed · 1 warning
Review iteration · 13 reviewers
Started in parallel · Reviewer 1 · Claude Opus 5 · Reviewer 2 · GPT-5.6 Sol · Reviewer 3 · Grok 4.5
XReviewer 3 · Grok 4.5
Pass
Reviewer to Main

Meets AC-1: simple, complete human-vs-computer tic-tac-toe with correct rules and usable UI. No material defects.

No material findings
GPTReviewer 2 · GPT-5.6 Sol
Changes required
Reviewer to Main

The implementation is otherwise a valid simple browser game, but its restart control can corrupt turn order through an uncancelled asynchronous move.

Reviewer's counterproposal

Add let computerTimer = null; let gameId = 0;. In startGame(), call clearTimeout(computerTimer); computerTimer = null; gameId++;. When scheduling, use const id = gameId; computerTimer = setTimeout(() => { if (id === gameId && !gameOver) computerMove(); }, 400);. Set computerTimer = null when the callback runs.

highRestart does not cancel a pending computer move
playerMove schedules setTimeout(computerMove, 400), but startGame does not cancel it. Restarting before it fires lets the old callback place O on the fresh board; moving quickly can also schedule another callback, producing two O moves.
Suggested: Store the timeout ID, clear it in startGame, and optionally use a game-generation token so callbacks from earlier games return without modifying state.
mediumAcceptance criterion AC-1 is not verified
Core play works, but restarting during the 400 ms computer delay leaves a stale callback that can alter the new game or give O consecutive turns.
Suggested: Verify and, if necessary, correct the work so AC-1 can pass unconditionally.
CReviewer 1 · Claude Opus 5
Changes required
Reviewer to Main

Code is correct, self-contained and runnable; win/block/draw logic verified. Only minor issues: a stale-timeout race on restart and a forkable heuristic AI. Accepting.

Challenge questions
  • Was the restart-mid-AI-turn path tested, and is a stray computer move acceptable to the user?
lowRestart during the 400ms AI delay lets a stale move land on the new board
startGame() resets state but the pending setTimeout(computerMove) still fires, placing an O on the fresh board so the computer effectively moves first.
Suggested: Store the timer id (let aiTimer) and call clearTimeout(aiTimer) in startGame(), or guard computerMove with a gameId counter incremented per restart.
lowAI is beatable by a fork (weak but acceptable for 'simple')
Heuristic order win/block/center/random loses to standard corner-then-opposite-corner fork, e.g. X:0, O:4, X:8 creates two threats the AI cannot block.
Suggested: Optionally add corner preference or a small minimax (~15 lines) for an unbeatable opponent; document current level as 'easy'.
Main assessment & revisionrevised candidate
Main assessment of findings
Main to reviewers

Fixed the restart race while preserving the simple human-vs-computer design.

Answers to challenge questions
Claude Opus 5
Was the restart-mid-AI-turn path tested, and is a stray computer move acceptable to the user?
Main: The original path was not safely handled, and a stray move is not acceptable. The revision cancels the pending timer and guards callbacks with a game-generation ID.
Counterproposal responses
adoptGPT-5.6 Sol · Timer cancellation plus a generation guard prevents callbacks from earlier games from changing the new board.
Finding dispositions
acceptREVIEWER-1-1 · The uncancelled timeout caused a real restart race and is now fixed.
rejectREVIEWER-1-2 · A beatable heuristic opponent is appropriate for the requested simple game; unbeatable AI was not required.
acceptREVIEWER-2-1 · The pending computer move is now cancelled and generation-guarded on restart.
acceptCRITERION-AC-1 · The restart defect is corrected, so the game now meets AC-1 without the identified turn-order failure.
GPTMain · revised candidate

Save the following as tic-tac-toe.html, then open it in a web browser:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tic-Tac-Toe</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      background: #f4f4f4;
    }

    #board {
      display: grid;
      grid-template-columns: repeat(3, 100px);
      gap: 6px;
      justify-content: center;
      margin: 25px auto;
    }

    .cell {
      width: 100px;
      height: 100px;
      border: none;
      border-radius: 8px;
      background: white;
      box-shadow: 0 2px 6px #aaa;
      font-size: 48px;
      font-weight: bold;
      cursor: pointer;
    }

    .cell:hover:empty {
      background: #e8f0ff;
    }

    #status {
      min-height: 24px;
      font-size: 20px;
    }

    #restart {
      padding: 10px 20px;
      border: none;
      border-radius: 6px;
      background: #2864dc;
      color: white;
      font-size: 16px;
      cursor: pointer;
    }

    #restart:hover {
      background: #174cae;
    }
  </style>
</head>
<body>
  <h1>Tic-Tac-Toe</h1>
  <p>You are X. The computer is O.</p>

  <div id="status">Your turn</div>
  <div id="board"></div>
  <button id="restart" type="button">Restart Game</button>

  <script>
    const boardElement = document.getElementById("board");
    const statusElement = document.getElementById("status");
    const restartButton = document.getElementById("restart");

    const winningLines = [
      [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 board;
    let gameOver;
    let computerThinking;
    let computerTimer = null;
    let gameId = 0;

    function startGame() {
      if (computerTimer !== null) {
        clearTimeout(computerTimer);
        computerTimer = null;
      }

      gameId++;
      board = Array(9).fill("");
      gameOver = false;
      computerThinking = false;
      statusElement.textContent = "Your turn";
      renderBoard();
    }

    function renderBoard() {
      boardElement.innerHTML = "";

      board.forEach((value, index) => {
        const cell = document.createElement("button");
        cell.type = "button";
        cell.className = "cell";
        cell.textContent = value;
        cell.setAttribute(
          "aria-label",
          value ? `Square ${index + 1}: ${value}` : `Square ${index + 1}: empty`
        );
        cell.addEventListener("click", () => playerMove(index));
        boardElement.appendChild(cell);
      });
    }

    function playerMove(index) {
      if (gameOver || computerThinking || board[index] !== "") return;

      board[index] = "X";
      renderBoard();

      if (finishTurn("X")) return;

      computerThinking = true;
      statusElement.textContent = "Computer is thinking...";

      const scheduledGameId = gameId;

      computerTimer = setTimeout(() => {
        computerTimer = null;

        if (scheduledGameId !== gameId || gameOver) return;

        computerMove();
      }, 400);
    }

    function computerMove() {
      // Win if possible, otherwise block the player,
      // take the center, or choose a random available square.
      let move = findWinningMove("O");

      if (move === null) {
        move = findWinningMove("X");
      }

      if (move === null && board[4] === "") {
        move = 4;
      }

      if (move === null) {
        const available = board
          .map((value, index) => value === "" ? index : null)
          .filter(index => index !== null);

        move = available[Math.floor(Math.random() * available.length)];
      }

      board[move] = "O";
      computerThinking = false;
      renderBoard();

      if (!finishTurn("O")) {
        statusElement.textContent = "Your turn";
      }
    }

    function findWinningMove(symbol) {
      for (let i = 0; i < board.length; i++) {
        if (board[i] !== "") continue;

        board[i] = symbol;
        const wins = hasWon(symbol);
        board[i] = "";

        if (wins) return i;
      }

      return null;
    }

    function hasWon(symbol) {
      return winningLines.some(line =>
        line.every(index => board[index] === symbol)
      );
    }

    function finishTurn(symbol) {
      if (hasWon(symbol)) {
        gameOver = true;
        statusElement.textContent =
          symbol === "X" ? "You win!" : "Computer wins!";
        return true;
      }

      if (board.every(cell => cell !== "")) {
        gameOver = true;
        statusElement.textContent = "It's a draw!";
        return true;
      }

      return false;
    }

    restartButton.addEventListener("click", startGame);
    startGame();
  </script>
</body>
</html>
Deterministic validation0 pass · 0 fail · 1 warn
Deterministic validation · 0 passed · 0 failed · 1 warning
Review iteration · 23 reviewers
Started in parallel · Reviewer 1 · Claude Opus 5 · Reviewer 2 · GPT-5.6 Sol · Reviewer 3 · Grok 4.5
GPTReviewer 2 · GPT-5.6 Sol
Pass
Reviewer to Main

The game satisfies the request. The revised timer cancellation and game-generation guard correctly prevent stale computer moves after restart.

No material findings
XReviewer 3 · Grok 4.5
Pass
Reviewer to Main

Meets AC-1. Simple, correct human-vs-AI game; prior restart race is fixed. No material defects found.

No material findings
CReviewer 1 · Claude Opus 5
Changes required
Reviewer to Main

Code is correct and self-contained; the restart race is genuinely fixed by timer cancellation plus the generation guard. Remaining points are polish (focus retention, AI strength, format choice), not defects against AC-1.

Challenge questions
  • Do you want the computer to be unbeatable (minimax) or intentionally beatable?
  • Is a browser page the preferred format, or would a terminal program suit better?
lowFull DOM rebuild each move drops keyboard focus
renderBoard() clears boardElement.innerHTML and recreates all 9 buttons every move, so a keyboard/screen-reader user loses focus position after each play despite the aria-labels.
Suggested: Create the 9 buttons once in startGame and have renderBoard only update textContent/aria-label of existing cells.
lowOpponent is easily beaten via fork
AI only wins/blocks immediate threats; e.g. X corner, O center, X opposite corner creates a double threat that the heuristic cannot answer, so a human wins reliably.
Suggested: Optionally add corner-preference or a ~30-line minimax to make the computer unbeatable; document the difficulty choice either way.
lowDelivery format assumed without alternative
Request did not specify a platform; only a browser version is offered, with no note that a console version (e.g. Python) is available if preferred.
Suggested: Add a one-line offer to supply a terminal (Python) version on request.
Main assessment & revisionrevised candidate
Main assessment of findings
Main to reviewers

Accepted the accessibility polish, upgraded the opponent to unbeatable minimax, and noted that a terminal version is available. The existing restart-race protection remains intact.

Answers to challenge questions
Reviewer 1 · Claude Opus 5
Do you want the computer to be unbeatable (minimax) or intentionally beatable?
Main: The revised version uses minimax, making the computer unbeatable.
Reviewer 1 · Claude Opus 5
Is a browser page the preferred format, or would a terminal program suit better?
Main: A self-contained browser page is retained for ease of use; a Python terminal version can be supplied on request.
Counterproposal responses
adoptReviewer 1 · Claude Opus 5 · The board buttons are now created once and updated in place, preserving keyboard focus.
adoptReviewer 1 · Claude Opus 5 · The heuristic opponent is replaced with minimax to prevent fork losses.
adoptReviewer 1 · Claude Opus 5 · A brief offer for a Python terminal version is included.
Finding dispositions
acceptREVIEWER-1-1 · Updating existing cells avoids unnecessary focus loss.
acceptREVIEWER-1-2 · Minimax provides a stronger and predictable computer opponent.
acceptREVIEWER-1-3 · The browser format remains appropriate, with a terminal alternative noted.
GPTMain · revised candidate

Save the following as tic-tac-toe.html, then open it in a web browser. You play as X, and the computer uses minimax, so it cannot be beaten.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tic-Tac-Toe</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      background: #f4f4f4;
    }

    #board {
      display: grid;
      grid-template-columns: repeat(3, 100px);
      gap: 6px;
      justify-content: center;
      margin: 25px auto;
    }

    .cell {
      width: 100px;
      height: 100px;
      border: none;
      border-radius: 8px;
      background: white;
      box-shadow: 0 2px 6px #aaa;
      font-size: 48px;
      font-weight: bold;
      cursor: pointer;
    }

    .cell:hover:not(.filled) {
      background: #e8f0ff;
    }

    .cell:focus-visible {
      outline: 4px solid #2864dc;
      outline-offset: 2px;
    }

    #status {
      min-height: 24px;
      font-size: 20px;
    }

    #restart {
      padding: 10px 20px;
      border: none;
      border-radius: 6px;
      background: #2864dc;
      color: white;
      font-size: 16px;
      cursor: pointer;
    }

    #restart:hover {
      background: #174cae;
    }

    #restart:focus-visible {
      outline: 4px solid #111;
      outline-offset: 2px;
    }
  </style>
</head>
<body>
  <h1>Tic-Tac-Toe</h1>
  <p>You are X. The computer is O.</p>

  <div id="status" aria-live="polite">Your turn</div>
  <div id="board" aria-label="Tic-tac-toe board"></div>
  <button id="restart" type="button">Restart Game</button>

  <script>
    const boardElement = document.getElementById("board");
    const statusElement = document.getElementById("status");
    const restartButton = document.getElementById("restart");

    const winningLines = [
      [0, 1, 2], [3, 4, 5], [6, 7, 8],
      [0, 3, 6], [1, 4, 7], [2, 5, 8],
      [0, 4, 8], [2, 4, 6]
    ];

    const cells = [];
    let board;
    let gameOver;
    let computerThinking;
    let computerTimer = null;
    let gameId = 0;

    function createBoard() {
      for (let index = 0; index < 9; index++) {
        const cell = document.createElement("button");
        cell.type = "button";
        cell.className = "cell";
        cell.addEventListener("click", () => playerMove(index));

        cells.push(cell);
        boardElement.appendChild(cell);
      }
    }

    function startGame() {
      if (computerTimer !== null) {
        clearTimeout(computerTimer);
        computerTimer = null;
      }

      gameId++;
      board = Array(9).fill("");
      gameOver = false;
      computerThinking = false;
      statusElement.textContent = "Your turn";
      renderBoard();
    }

    function renderBoard() {
      board.forEach((value, index) => {
        const cell = cells[index];

        cell.textContent = value;
        cell.classList.toggle("filled", value !== "");
        cell.setAttribute(
          "aria-label",
          value
            ? `Square ${index + 1}: ${value}`
            : `Square ${index + 1}: empty`
        );
      });
    }

    function playerMove(index) {
      if (gameOver || computerThinking || board[index] !== "") {
        return;
      }

      board[index] = "X";
      renderBoard();

      if (finishTurn("X")) {
        return;
      }

      computerThinking = true;
      statusElement.textContent = "Computer is thinking...";

      const scheduledGameId = gameId;

      computerTimer = setTimeout(() => {
        computerTimer = null;

        if (scheduledGameId !== gameId || gameOver) {
          return;
        }

        computerMove();
      }, 400);
    }

    function computerMove() {
      const move = findBestComputerMove();

      board[move] = "O";
      computerThinking = false;
      renderBoard();

      if (!finishTurn("O")) {
        statusElement.textContent = "Your turn";
      }
    }

    function findBestComputerMove() {
      let bestScore = -Infinity;
      let bestMove = null;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "O";
        const score = minimax(false, 0);
        board[index] = "";

        if (score > bestScore) {
          bestScore = score;
          bestMove = index;
        }
      }

      return bestMove;
    }

    function minimax(computerTurn, depth) {
      if (hasWon("O")) {
        return 10 - depth;
      }

      if (hasWon("X")) {
        return depth - 10;
      }

      if (board.every(cell => cell !== "")) {
        return 0;
      }

      if (computerTurn) {
        let bestScore = -Infinity;

        for (let index = 0; index < board.length; index++) {
          if (board[index] !== "") {
            continue;
          }

          board[index] = "O";
          bestScore = Math.max(
            bestScore,
            minimax(false, depth + 1)
          );
          board[index] = "";
        }

        return bestScore;
      }

      let bestScore = Infinity;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "X";
        bestScore = Math.min(
          bestScore,
          minimax(true, depth + 1)
        );
        board[index] = "";
      }

      return bestScore;
    }

    function hasWon(symbol) {
      return winningLines.some(line =>
        line.every(index => board[index] === symbol)
      );
    }

    function finishTurn(symbol) {
      if (hasWon(symbol)) {
        gameOver = true;
        statusElement.textContent =
          symbol === "X" ? "You win!" : "Computer wins!";
        return true;
      }

      if (board.every(cell => cell !== "")) {
        gameOver = true;
        statusElement.textContent = "It's a draw!";
        return true;
      }

      return false;
    }

    restartButton.addEventListener("click", startGame);

    createBoard();
    startGame();
  </script>
</body>
</html>

A Python terminal version can also be provided if preferred.

Deterministic validation0 pass · 0 fail · 1 warn
Deterministic validation · 0 passed · 0 failed · 1 warning
Final answer
GPTMain · GPT-5.6 Sol

Save the following as tic-tac-toe.html, then open it in a web browser. You play as X, and the computer uses minimax, so it cannot be beaten.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tic-Tac-Toe</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      background: #f4f4f4;
    }

    #board {
      display: grid;
      grid-template-columns: repeat(3, 100px);
      gap: 6px;
      justify-content: center;
      margin: 25px auto;
    }

    .cell {
      width: 100px;
      height: 100px;
      border: none;
      border-radius: 8px;
      background: white;
      box-shadow: 0 2px 6px #aaa;
      font-size: 48px;
      font-weight: bold;
      cursor: pointer;
    }

    .cell:hover:not(.filled) {
      background: #e8f0ff;
    }

    .cell:focus-visible {
      outline: 4px solid #2864dc;
      outline-offset: 2px;
    }

    #status {
      min-height: 24px;
      font-size: 20px;
    }

    #restart {
      padding: 10px 20px;
      border: none;
      border-radius: 6px;
      background: #2864dc;
      color: white;
      font-size: 16px;
      cursor: pointer;
    }

    #restart:hover {
      background: #174cae;
    }

    #restart:focus-visible {
      outline: 4px solid #111;
      outline-offset: 2px;
    }
  </style>
</head>
<body>
  <h1>Tic-Tac-Toe</h1>
  <p>You are X. The computer is O.</p>

  <div id="status" aria-live="polite">Your turn</div>
  <div id="board" aria-label="Tic-tac-toe board"></div>
  <button id="restart" type="button">Restart Game</button>

  <script>
    const boardElement = document.getElementById("board");
    const statusElement = document.getElementById("status");
    const restartButton = document.getElementById("restart");

    const winningLines = [
      [0, 1, 2], [3, 4, 5], [6, 7, 8],
      [0, 3, 6], [1, 4, 7], [2, 5, 8],
      [0, 4, 8], [2, 4, 6]
    ];

    const cells = [];
    let board;
    let gameOver;
    let computerThinking;
    let computerTimer = null;
    let gameId = 0;

    function createBoard() {
      for (let index = 0; index < 9; index++) {
        const cell = document.createElement("button");
        cell.type = "button";
        cell.className = "cell";
        cell.addEventListener("click", () => playerMove(index));

        cells.push(cell);
        boardElement.appendChild(cell);
      }
    }

    function startGame() {
      if (computerTimer !== null) {
        clearTimeout(computerTimer);
        computerTimer = null;
      }

      gameId++;
      board = Array(9).fill("");
      gameOver = false;
      computerThinking = false;
      statusElement.textContent = "Your turn";
      renderBoard();
    }

    function renderBoard() {
      board.forEach((value, index) => {
        const cell = cells[index];

        cell.textContent = value;
        cell.classList.toggle("filled", value !== "");
        cell.setAttribute(
          "aria-label",
          value
            ? `Square ${index + 1}: ${value}`
            : `Square ${index + 1}: empty`
        );
      });
    }

    function playerMove(index) {
      if (gameOver || computerThinking || board[index] !== "") {
        return;
      }

      board[index] = "X";
      renderBoard();

      if (finishTurn("X")) {
        return;
      }

      computerThinking = true;
      statusElement.textContent = "Computer is thinking...";

      const scheduledGameId = gameId;

      computerTimer = setTimeout(() => {
        computerTimer = null;

        if (scheduledGameId !== gameId || gameOver) {
          return;
        }

        computerMove();
      }, 400);
    }

    function computerMove() {
      const move = findBestComputerMove();

      board[move] = "O";
      computerThinking = false;
      renderBoard();

      if (!finishTurn("O")) {
        statusElement.textContent = "Your turn";
      }
    }

    function findBestComputerMove() {
      let bestScore = -Infinity;
      let bestMove = null;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "O";
        const score = minimax(false, 0);
        board[index] = "";

        if (score > bestScore) {
          bestScore = score;
          bestMove = index;
        }
      }

      return bestMove;
    }

    function minimax(computerTurn, depth) {
      if (hasWon("O")) {
        return 10 - depth;
      }

      if (hasWon("X")) {
        return depth - 10;
      }

      if (board.every(cell => cell !== "")) {
        return 0;
      }

      if (computerTurn) {
        let bestScore = -Infinity;

        for (let index = 0; index < board.length; index++) {
          if (board[index] !== "") {
            continue;
          }

          board[index] = "O";
          bestScore = Math.max(
            bestScore,
            minimax(false, depth + 1)
          );
          board[index] = "";
        }

        return bestScore;
      }

      let bestScore = Infinity;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "X";
        bestScore = Math.min(
          bestScore,
          minimax(true, depth + 1)
        );
        board[index] = "";
      }

      return bestScore;
    }

    function hasWon(symbol) {
      return winningLines.some(line =>
        line.every(index => board[index] === symbol)
      );
    }

    function finishTurn(symbol) {
      if (hasWon(symbol)) {
        gameOver = true;
        statusElement.textContent =
          symbol === "X" ? "You win!" : "Computer wins!";
        return true;
      }

      if (board.every(cell => cell !== "")) {
        gameOver = true;
        statusElement.textContent = "It's a draw!";
        return true;
      }

      return false;
    }

    restartButton.addEventListener("click", startGame);

    createBoard();
    startGame();
  </script>
</body>
</html>

A Python terminal version can also be provided if preferred.

! Maximum iterations reached - final revision not unconditionally verified
Verification report1/1 criteria verified · 1/1 deterministic checks passed
Konstantin

As a downloadable file please

Main candidate · GPT-5.6 Solcandidate
GPTMain · GPT-5.6 Sol

MPLUS_FILE: tic-tac-toe.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tic-Tac-Toe</title>
  <style>
    body {
      margin: 0;
      padding: 24px;
      font-family: Arial, sans-serif;
      text-align: center;
      background: #f4f4f4;
      color: #222;
    }

    #board {
      display: grid;
      grid-template-columns: repeat(3, 100px);
      gap: 6px;
      justify-content: center;
      margin: 25px auto;
    }

    .cell {
      width: 100px;
      height: 100px;
      border: none;
      border-radius: 8px;
      background: white;
      box-shadow: 0 2px 6px #aaa;
      font-size: 48px;
      font-weight: bold;
      cursor: pointer;
    }

    .cell:hover:not(:disabled) {
      background: #e8f0ff;
    }

    .cell:disabled {
      color: #222;
      cursor: default;
      opacity: 1;
    }

    .cell:focus-visible {
      outline: 4px solid #2864dc;
      outline-offset: 2px;
    }

    #status {
      min-height: 24px;
      font-size: 20px;
    }

    #restart {
      padding: 10px 20px;
      border: none;
      border-radius: 6px;
      background: #2864dc;
      color: white;
      font-size: 16px;
      cursor: pointer;
    }

    #restart:hover {
      background: #174cae;
    }

    #restart:focus-visible {
      outline: 4px solid #111;
      outline-offset: 2px;
    }

    @media (max-width: 380px) {
      #board {
        grid-template-columns: repeat(3, 27vw);
      }

      .cell {
        width: 27vw;
        height: 27vw;
      }
    }
  </style>
</head>
<body>
  <h1>Tic-Tac-Toe</h1>
  <p>You are X. The computer is O.</p>

  <div id="status" aria-live="polite">Your turn</div>
  <div id="board" role="grid" aria-label="Tic-tac-toe board"></div>
  <button id="restart" type="button">Restart Game</button>

  <script>
    const boardElement = document.getElementById("board");
    const statusElement = document.getElementById("status");
    const restartButton = document.getElementById("restart");

    const winningLines = [
      [0, 1, 2], [3, 4, 5], [6, 7, 8],
      [0, 3, 6], [1, 4, 7], [2, 5, 8],
      [0, 4, 8], [2, 4, 6]
    ];

    const cells = [];
    let board = [];
    let gameOver = false;
    let computerThinking = false;
    let computerTimer = null;
    let gameId = 0;

    function createBoard() {
      for (let index = 0; index < 9; index++) {
        const cell = document.createElement("button");
        cell.type = "button";
        cell.className = "cell";
        cell.setAttribute("role", "gridcell");
        cell.addEventListener("click", () => playerMove(index));

        cells.push(cell);
        boardElement.appendChild(cell);
      }
    }

    function startGame() {
      if (computerTimer !== null) {
        clearTimeout(computerTimer);
        computerTimer = null;
      }

      gameId++;
      board = Array(9).fill("");
      gameOver = false;
      computerThinking = false;
      statusElement.textContent = "Your turn";
      renderBoard();
    }

    function renderBoard() {
      board.forEach((value, index) => {
        const cell = cells[index];
        const row = Math.floor(index / 3) + 1;
        const column = (index % 3) + 1;

        cell.textContent = value;
        cell.disabled = value !== "" || gameOver || computerThinking;
        cell.setAttribute(
          "aria-label",
          `Row ${row}, column ${column}: ${value || "empty"}`
        );
      });
    }

    function playerMove(index) {
      if (gameOver || computerThinking || board[index] !== "") {
        return;
      }

      board[index] = "X";
      renderBoard();

      if (finishTurn("X")) {
        renderBoard();
        return;
      }

      computerThinking = true;
      statusElement.textContent = "Computer is thinking...";
      renderBoard();

      const currentGameId = gameId;

      computerTimer = setTimeout(() => {
        computerTimer = null;

        if (currentGameId !== gameId || gameOver) {
          return;
        }

        computerMove();
      }, 400);
    }

    function computerMove() {
      const move = findBestComputerMove();

      if (move === null) {
        return;
      }

      board[move] = "O";
      computerThinking = false;
      renderBoard();

      if (!finishTurn("O")) {
        statusElement.textContent = "Your turn";
      }

      renderBoard();
    }

    function findBestComputerMove() {
      let bestScore = -Infinity;
      let bestMove = null;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "O";
        const score = minimax(false, 0);
        board[index] = "";

        if (score > bestScore) {
          bestScore = score;
          bestMove = index;
        }
      }

      return bestMove;
    }

    function minimax(computerTurn, depth) {
      if (hasWon("O")) {
        return 10 - depth;
      }

      if (hasWon("X")) {
        return depth - 10;
      }

      if (board.every(cell => cell !== "")) {
        return 0;
      }

      if (computerTurn) {
        let bestScore = -Infinity;

        for (let index = 0; index < board.length; index++) {
          if (board[index] !== "") {
            continue;
          }

          board[index] = "O";
          bestScore = Math.max(
            bestScore,
            minimax(false, depth + 1)
          );
          board[index] = "";
        }

        return bestScore;
      }

      let bestScore = Infinity;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "X";
        bestScore = Math.min(
          bestScore,
          minimax(true, depth + 1)
        );
        board[index] = "";
      }

      return bestScore;
    }

    function hasWon(symbol) {
      return winningLines.some(line =>
        line.every(index => board[index] === symbol)
      );
    }

    function finishTurn(symbol) {
      if (hasWon(symbol)) {
        gameOver = true;
        statusElement.textContent =
          symbol === "X" ? "You win!" : "Computer wins!";
        return true;
      }

      if (board.every(cell => cell !== "")) {
        gameOver = true;
        statusElement.textContent = "It's a draw!";
        return true;
      }

      return false;
    }

    restartButton.addEventListener("click", startGame);

    createBoard();
    startGame();
  </script>
</body>
</html>

Download tic-tac-toe.html and open it in a web browser to play.

Deterministic validation0 pass · 0 fail · 1 warn
Deterministic validation · 0 passed · 0 failed · 1 warning
Review iteration · 13 reviewers
Started in parallel · Reviewer 1 · Claude Opus 5 · Reviewer 2 · GPT-5.6 Sol · Reviewer 3 · Grok 4.5
GPTReviewer 2 · GPT-5.6 Sol
Pass
Reviewer to Main

The requested downloadable file is correctly provided and contains a self-contained, functional browser game.

No material findings
XReviewer 3 · Grok 4.5
Pass
Reviewer to Main

AC-1 met: proper downloadable HTML with working unbeatable tic-tac-toe. No material defects found.

No material findings
CReviewer 1 · Claude Opus 5
Changes required
Reviewer to Main

Deliverable meets the request: a complete standalone tic-tac-toe HTML file with unbeatable minimax and correct restart/race guards. Only minor accessibility polish (disabled vs aria-disabled, grid roles, focus handling) would improve it.

mediumFilled cells become unreadable to keyboard/screen-reader users
renderBoard sets cell.disabled=true for played cells and for the whole board when gameOver/computerThinking. Disabled buttons leave the tab order and are typically not announced, so the carefully built "Row 1, column 2: X" labels can never be reached to review board state.
Suggested: Use aria-disabled="true" plus a .filled class and keep the existing guard in playerMove, instead of the disabled property, so cells stay focusable and announceable.
lowInvalid ARIA grid structure
role="grid" is applied to #board with role="gridcell" buttons as direct children; the grid pattern requires row (or rowgroup) children, so the mapping is malformed and some AT will drop the cell semantics.
Suggested: Either wrap each set of three cells in a div role="row", or drop role="grid"/"gridcell" and keep only the aria-label as in the previous version.
lowFocus is dropped after every move
Clicking a cell disables it, so focus falls back to <body>; keyboard users must re-tab from the top each turn, and at game end nothing directs focus to Restart.
Suggested: After the computer replies, focus the first empty cell; on game over, focus the Restart button.
Main assessment & revisionrevised candidate
Main assessment of findings
Main to reviewers

Adopted the valid accessibility improvements while preserving the working minimax game and restart safeguards.

Finding dispositions
acceptREVIEWER-1-1 · Replaced native disabled states with aria-disabled and guarded interaction so cells remain focusable and announceable.
acceptREVIEWER-1-2 · Removed the malformed grid/gridcell roles while retaining an accessible board label.
acceptREVIEWER-1-3 · Added focus movement to an available cell after computer turns and to Restart when the game ends.
GPTMain · revised candidate

MPLUS_FILE: tic-tac-toe.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tic-Tac-Toe</title>
  <style>
    body {
      margin: 0;
      padding: 24px;
      font-family: Arial, sans-serif;
      text-align: center;
      background: #f4f4f4;
      color: #222;
    }

    #board {
      display: grid;
      grid-template-columns: repeat(3, 100px);
      gap: 6px;
      justify-content: center;
      margin: 25px auto;
    }

    .cell {
      width: 100px;
      height: 100px;
      border: none;
      border-radius: 8px;
      background: white;
      box-shadow: 0 2px 6px #aaa;
      color: #222;
      font-size: 48px;
      font-weight: bold;
      cursor: pointer;
    }

    .cell:not([aria-disabled="true"]):hover {
      background: #e8f0ff;
    }

    .cell[aria-disabled="true"] {
      cursor: default;
    }

    .cell:focus-visible {
      outline: 4px solid #2864dc;
      outline-offset: 2px;
    }

    #status {
      min-height: 24px;
      font-size: 20px;
    }

    #restart {
      padding: 10px 20px;
      border: none;
      border-radius: 6px;
      background: #2864dc;
      color: white;
      font-size: 16px;
      cursor: pointer;
    }

    #restart:hover {
      background: #174cae;
    }

    #restart:focus-visible {
      outline: 4px solid #111;
      outline-offset: 2px;
    }

    @media (max-width: 380px) {
      #board {
        grid-template-columns: repeat(3, 27vw);
      }

      .cell {
        width: 27vw;
        height: 27vw;
      }
    }
  </style>
</head>
<body>
  <h1>Tic-Tac-Toe</h1>
  <p>You are X. The computer is O.</p>

  <div id="status" aria-live="polite">Your turn</div>
  <div id="board" aria-label="Tic-tac-toe board"></div>
  <button id="restart" type="button">Restart Game</button>

  <script>
    const boardElement = document.getElementById("board");
    const statusElement = document.getElementById("status");
    const restartButton = document.getElementById("restart");

    const winningLines = [
      [0, 1, 2], [3, 4, 5], [6, 7, 8],
      [0, 3, 6], [1, 4, 7], [2, 5, 8],
      [0, 4, 8], [2, 4, 6]
    ];

    const cells = [];
    let board = [];
    let gameOver = false;
    let computerThinking = false;
    let computerTimer = null;
    let gameId = 0;

    function createBoard() {
      for (let index = 0; index < 9; index++) {
        const cell = document.createElement("button");
        cell.type = "button";
        cell.className = "cell";
        cell.addEventListener("click", () => playerMove(index));

        cells.push(cell);
        boardElement.appendChild(cell);
      }
    }

    function startGame(moveFocus = false) {
      if (computerTimer !== null) {
        clearTimeout(computerTimer);
        computerTimer = null;
      }

      gameId++;
      board = Array(9).fill("");
      gameOver = false;
      computerThinking = false;
      statusElement.textContent = "Your turn";
      renderBoard();

      if (moveFocus) {
        cells[0].focus();
      }
    }

    function renderBoard() {
      board.forEach((value, index) => {
        const cell = cells[index];
        const row = Math.floor(index / 3) + 1;
        const column = (index % 3) + 1;
        const unavailable =
          value !== "" || gameOver || computerThinking;

        cell.textContent = value;
        cell.classList.toggle("filled", value !== "");
        cell.setAttribute(
          "aria-disabled",
          unavailable ? "true" : "false"
        );
        cell.setAttribute(
          "aria-label",
          `Row ${row}, column ${column}: ${value || "empty"}`
        );
      });
    }

    function focusFirstEmptyCell() {
      const emptyIndex = board.findIndex(value => value === "");

      if (emptyIndex !== -1) {
        cells[emptyIndex].focus();
      }
    }

    function playerMove(index) {
      if (gameOver || computerThinking || board[index] !== "") {
        return;
      }

      board[index] = "X";
      renderBoard();

      if (finishTurn("X")) {
        renderBoard();
        restartButton.focus();
        return;
      }

      computerThinking = true;
      statusElement.textContent = "Computer is thinking...";
      renderBoard();

      const currentGameId = gameId;

      computerTimer = setTimeout(() => {
        computerTimer = null;

        if (currentGameId !== gameId || gameOver) {
          return;
        }

        computerMove();
      }, 400);
    }

    function computerMove() {
      const move = findBestComputerMove();

      if (move === null) {
        return;
      }

      board[move] = "O";
      computerThinking = false;
      renderBoard();

      if (finishTurn("O")) {
        renderBoard();
        restartButton.focus();
        return;
      }

      statusElement.textContent = "Your turn";
      renderBoard();
      focusFirstEmptyCell();
    }

    function findBestComputerMove() {
      let bestScore = -Infinity;
      let bestMove = null;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "O";
        const score = minimax(false, 0);
        board[index] = "";

        if (score > bestScore) {
          bestScore = score;
          bestMove = index;
        }
      }

      return bestMove;
    }

    function minimax(computerTurn, depth) {
      if (hasWon("O")) {
        return 10 - depth;
      }

      if (hasWon("X")) {
        return depth - 10;
      }

      if (board.every(cell => cell !== "")) {
        return 0;
      }

      if (computerTurn) {
        let bestScore = -Infinity;

        for (let index = 0; index < board.length; index++) {
          if (board[index] !== "") {
            continue;
          }

          board[index] = "O";
          bestScore = Math.max(
            bestScore,
            minimax(false, depth + 1)
          );
          board[index] = "";
        }

        return bestScore;
      }

      let bestScore = Infinity;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "X";
        bestScore = Math.min(
          bestScore,
          minimax(true, depth + 1)
        );
        board[index] = "";
      }

      return bestScore;
    }

    function hasWon(symbol) {
      return winningLines.some(line =>
        line.every(index => board[index] === symbol)
      );
    }

    function finishTurn(symbol) {
      if (hasWon(symbol)) {
        gameOver = true;
        statusElement.textContent =
          symbol === "X" ? "You win!" : "Computer wins!";
        return true;
      }

      if (board.every(cell => cell !== "")) {
        gameOver = true;
        statusElement.textContent = "It's a draw!";
        return true;
      }

      return false;
    }

    restartButton.addEventListener("click", () => startGame(true));

    createBoard();
    startGame();
  </script>
</body>
</html>

Download tic-tac-toe.html and open it in a web browser to play.

Deterministic validation0 pass · 0 fail · 1 warn
Deterministic validation · 0 passed · 0 failed · 1 warning
Review iteration · 23 reviewers
Started in parallel · Reviewer 1 · Claude Opus 5 · Reviewer 2 · GPT-5.6 Sol · Reviewer 3 · Grok 4.5
GPTReviewer 2 · GPT-5.6 Sol
Pass
Reviewer to Main

The downloadable HTML file satisfies the request and contains a functional human-versus-computer tic-tac-toe game.

No material findings
XReviewer 3 · Grok 4.5
Pass
Reviewer to Main

Downloadable HTML file meets AC-1; game logic and restart safeguards are sound. No material defects.

No material findings
CReviewer 1 · Claude Opus 5
Changes required
Reviewer to Main

Deliverable meets the request: a single downloadable, self-contained tic-tac-toe.html with a correct unbeatable minimax opponent, restart/timer guards, and no external dependencies. Only cosmetic/a11y polish items remain (focus jump, unnamed board container, dead .filled class).

Challenge questions
  • Was the file verified end-to-end in a browser (win, loss-impossible, draw, restart mid-'thinking') after the aria-disabled refactor, or only reasoned about?
lowFocus jump to first empty cell after each computer move
computerMove() calls focusFirstEmptyCell(), so a keyboard user who played cell 5 is thrown back to cell 0 (or the first free cell) every turn, losing tab position.
Suggested: Keep focus on the cell the human just played (it remains focusable via aria-disabled), and only redirect focus if that element was removed or the game ended.
lowaria-label on a div with no role is ignored
<div id="board" aria-label="Tic-tac-toe board"> has no role; naming a generic element is not supported by ARIA, so the label is dropped by most screen readers.
Suggested: Add role="group" (or wrap in a fieldset/legend) so the accessible name for the board is actually exposed.
lowDead CSS/state: .filled class no longer styled
renderBoard() toggles class "filled" but the stylesheet now keys hover/cursor off [aria-disabled]; .filled has no rule.
Suggested: Remove the classList.toggle("filled", ...) line or add a .filled rule, to avoid misleading state.
Main assessment & revisionrevised candidate
Main assessment of findings
Main to reviewers

Accepted the three accessibility and cleanup improvements; core game logic remains unchanged.

Answers to challenge questions
Claude Opus 5
Was the file verified end-to-end in a browser after the aria-disabled refactor, or only reasoned about?
Main: It was reviewed by reasoning rather than independently executed in a browser in this environment.
Counterproposal responses
adoptClaude Opus 5 · Preserve focus on the human player's selected cell instead of jumping to the first empty cell.
adoptClaude Opus 5 · Add role="group" so the board's accessible name is exposed.
adoptClaude Opus 5 · Remove the unused filled-class state.
Finding dispositions
acceptREVIEWER-1-1 · Automatic focus movement was unnecessary and potentially disorienting.
acceptREVIEWER-1-2 · The generic board container needs an ARIA role for its label.
acceptREVIEWER-1-3 · The filled class was no longer used by the stylesheet.
GPTMain · revised candidate

MPLUS_FILE: tic-tac-toe.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tic-Tac-Toe</title>
  <style>
    body {
      margin: 0;
      padding: 24px;
      font-family: Arial, sans-serif;
      text-align: center;
      background: #f4f4f4;
      color: #222;
    }

    #board {
      display: grid;
      grid-template-columns: repeat(3, 100px);
      gap: 6px;
      justify-content: center;
      margin: 25px auto;
    }

    .cell {
      width: 100px;
      height: 100px;
      border: none;
      border-radius: 8px;
      background: white;
      box-shadow: 0 2px 6px #aaa;
      color: #222;
      font-size: 48px;
      font-weight: bold;
      cursor: pointer;
    }

    .cell:not([aria-disabled="true"]):hover {
      background: #e8f0ff;
    }

    .cell[aria-disabled="true"] {
      cursor: default;
    }

    .cell:focus-visible {
      outline: 4px solid #2864dc;
      outline-offset: 2px;
    }

    #status {
      min-height: 24px;
      font-size: 20px;
    }

    #restart {
      padding: 10px 20px;
      border: none;
      border-radius: 6px;
      background: #2864dc;
      color: white;
      font-size: 16px;
      cursor: pointer;
    }

    #restart:hover {
      background: #174cae;
    }

    #restart:focus-visible {
      outline: 4px solid #111;
      outline-offset: 2px;
    }

    @media (max-width: 380px) {
      #board {
        grid-template-columns: repeat(3, 27vw);
      }

      .cell {
        width: 27vw;
        height: 27vw;
      }
    }
  </style>
</head>
<body>
  <h1>Tic-Tac-Toe</h1>
  <p>You are X. The computer is O.</p>

  <div id="status" aria-live="polite">Your turn</div>
  <div
    id="board"
    role="group"
    aria-label="Tic-tac-toe board"
  ></div>
  <button id="restart" type="button">Restart Game</button>

  <script>
    const boardElement = document.getElementById("board");
    const statusElement = document.getElementById("status");
    const restartButton = document.getElementById("restart");

    const winningLines = [
      [0, 1, 2], [3, 4, 5], [6, 7, 8],
      [0, 3, 6], [1, 4, 7], [2, 5, 8],
      [0, 4, 8], [2, 4, 6]
    ];

    const cells = [];
    let board = [];
    let gameOver = false;
    let computerThinking = false;
    let computerTimer = null;
    let gameId = 0;

    function createBoard() {
      for (let index = 0; index < 9; index++) {
        const cell = document.createElement("button");
        cell.type = "button";
        cell.className = "cell";
        cell.addEventListener("click", () => playerMove(index));

        cells.push(cell);
        boardElement.appendChild(cell);
      }
    }

    function startGame(moveFocus = false) {
      if (computerTimer !== null) {
        clearTimeout(computerTimer);
        computerTimer = null;
      }

      gameId++;
      board = Array(9).fill("");
      gameOver = false;
      computerThinking = false;
      statusElement.textContent = "Your turn";
      renderBoard();

      if (moveFocus) {
        cells[0].focus();
      }
    }

    function renderBoard() {
      board.forEach((value, index) => {
        const cell = cells[index];
        const row = Math.floor(index / 3) + 1;
        const column = (index % 3) + 1;
        const unavailable =
          value !== "" || gameOver || computerThinking;

        cell.textContent = value;
        cell.setAttribute(
          "aria-disabled",
          unavailable ? "true" : "false"
        );
        cell.setAttribute(
          "aria-label",
          `Row ${row}, column ${column}: ${value || "empty"}`
        );
      });
    }

    function playerMove(index) {
      if (gameOver || computerThinking || board[index] !== "") {
        return;
      }

      board[index] = "X";
      renderBoard();

      if (finishTurn("X")) {
        renderBoard();
        restartButton.focus();
        return;
      }

      computerThinking = true;
      statusElement.textContent = "Computer is thinking...";
      renderBoard();

      const currentGameId = gameId;

      computerTimer = setTimeout(() => {
        computerTimer = null;

        if (currentGameId !== gameId || gameOver) {
          return;
        }

        computerMove();
      }, 400);
    }

    function computerMove() {
      const move = findBestComputerMove();

      if (move === null) {
        return;
      }

      board[move] = "O";
      computerThinking = false;
      renderBoard();

      if (finishTurn("O")) {
        renderBoard();
        restartButton.focus();
        return;
      }

      statusElement.textContent = "Your turn";
      renderBoard();
    }

    function findBestComputerMove() {
      let bestScore = -Infinity;
      let bestMove = null;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "O";
        const score = minimax(false, 0);
        board[index] = "";

        if (score > bestScore) {
          bestScore = score;
          bestMove = index;
        }
      }

      return bestMove;
    }

    function minimax(computerTurn, depth) {
      if (hasWon("O")) {
        return 10 - depth;
      }

      if (hasWon("X")) {
        return depth - 10;
      }

      if (board.every(cell => cell !== "")) {
        return 0;
      }

      if (computerTurn) {
        let bestScore = -Infinity;

        for (let index = 0; index < board.length; index++) {
          if (board[index] !== "") {
            continue;
          }

          board[index] = "O";
          bestScore = Math.max(
            bestScore,
            minimax(false, depth + 1)
          );
          board[index] = "";
        }

        return bestScore;
      }

      let bestScore = Infinity;

      for (let index = 0; index < board.length; index++) {
        if (board[index] !== "") {
          continue;
        }

        board[index] = "X";
        bestScore = Math.min(
          bestScore,
          minimax(true, depth + 1)
        );
        board[index] = "";
      }

      return bestScore;
    }

    function hasWon(symbol) {
      return winningLines.some(line =>
        line.every(index => board[index] === symbol)
      );
    }

    function finishTurn(symbol) {
      if (hasWon(symbol)) {
        gameOver = true;
        statusElement.textContent =
          symbol === "X" ? "You win!" : "Computer wins!";
        return true;
      }

      if (board.every(cell => cell !== "")) {
        gameOver = true;
        statusElement.textContent = "It's a draw!";
        return true;
      }

      return false;
    }

    restartButton.addEventListener("click", () => startGame(true));

    createBoard();
    startGame();
  </script>
</body>
</html>

Download tic-tac-toe.html and open it in a web browser to play.

Deterministic validation0 pass · 0 fail · 1 warn
Deterministic validation · 0 passed · 0 failed · 1 warning
Final answer
GPTMain · GPT-5.6 Sol

Download tic-tac-toe.html and open it in a web browser to play.

! Maximum iterations reached - final revision not unconditionally verified
Verification report1/1 criteria verified · 1/1 deterministic checks passed
HTMLtic-tac-toe.htmlGenerated output · 6.6 KB