AniUI Academy

Build tic-tac-toe

mediumState & Data+210 XP

Build a 3x3 tic-tac-toe board. Players alternate, X first.

After each move, check whether the player who just moved has three in a row — any row, column, or diagonal — and if so, declare them the winner and stop accepting moves.

Examples

// X: 0, O: 4, X: 1, O: 3, X: 2
//  X | X | X
// -----------
//  O | O | .
// -----------
//  . | . | .
// → status becomes "X wins!"; further clicks on empty cells are ignored.

The winner-check runs after every move, not just the ninth.

const WINS = [
  [0,1,2],[3,4,5],[6,7,8], // rows
  [0,3,6],[1,4,7],[2,5,8], // columns
  [0,4,8],[2,4,6],         // diagonals
];

function winner(cells) {
  for (const [a,b,c] of WINS) {
    if (cells[a] && cells[a] === cells[b] && cells[a] === cells[c]) {
      return cells[a];
    }
  }
  return null;
}

Pre-compute the 8 winning triples once; derive the winner from the board rather than tracking it in state.

What it has to do

  • Nine cells, `data-testid="cell-0"` through `data-testid="cell-8"` (row-major: 0,1,2 is the top row).
  • A `data-testid="status"` element mentions whose turn it is (containing "X" or "O") before the game ends.
  • Clicking an empty cell places the current player's mark and passes the turn.
  • Clicking an already-filled cell does nothing — no mark change, no turn change.
  • Once a player completes a line, the status becomes exactly "X wins!" or "O wins!", and no further clicks place a mark anywhere on the board.

Notes

  • Derive both the current player and winner from `cells` — don't track them in separate state slots that can drift out of sync.
  • A tie needs its own branch: no winner AND no empty cells → "Draw". Not tested here but trivial to add once the winner check exists.
  • Lock the board with a single early return in the click handler: `if (winner || cells[i]) return;` — one guard, both no-op cases handled.
  • A `key` prop on cells is unnecessary — the cells are a fixed-length array rendered by index. The React warning only matters for dynamic lists.
Asked atGoogleAmazonMicrosoft

Stuck? The react course covers the ideas this problem is built on.

import { useState } from "react";

export default function App() {
  const [cells, setCells] = useState(Array(9).fill(null));
  const [xIsNext, setXIsNext] = useState(true);

  // TODO: after each move, check whether the player who just moved has
  // three in a row (any row, column or diagonal) and if so, lock the
  // board and show "X wins!" / "O wins!" via data-testid="status".

  function handleClick(index) {
    if (cells[index]) return;
    const next = [...cells];
    next[index] = xIsNext ? "X" : "O";
    setCells(next);
    setXIsNext(!xIsNext);
  }

  return (
    <div style={{ fontFamily: "sans-serif", padding: 16 }}>
      <p data-testid="status">{xIsNext ? "X" : "O"}'s turn</p>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 48px)", gap: 4 }}>
        {cells.map((cell, index) => (
          <button
            key={index}
            data-testid={`cell-${index}`}
            onClick={() => handleClick(index)}
            style={{ height: 48, fontSize: 24 }}
          >
            {cell}
          </button>
        ))}
      </div>
    </div>
  );
}

5 tests · +210 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.