Katkat

Build a Configurable Two-Player Tic-Tac-Toe Board in React

RobloxFrontend Engineer · Technical Screen

Last updated

Tests your ability to architect a scalable, accessible React component with dynamic state, efficient win-detection logic for large grid sizes, and robust handling of prop updates.

Problem Statement

Implement a configurable, two-player Tic-Tac-Toe component in React. The component accepts an integer prop size ($N \times N$ board) where $3 \le N \le 20$.

Two players alternatingly place marks on the board: Player X always starts first, followed by Player O. The game ends when either player places $N$ consecutive marks in any row, column, or diagonal, or when all cells are filled resulting in a draw.

Functional Requirements

  1. Board Rendering: Render an $N \times N$ grid of clickable cells based on the size prop (default to 3 if omitted or invalid).
  2. Turn Management: Alternate turns starting with 'X'. Clicking an already occupied cell or clicking after the game is over must have no effect.
  3. Win & Draw Detection: Identify a winner immediately when $N$ marks align horizontally, vertically, or diagonally. If all $N^2$ cells are filled without a winner, declare a draw.
  4. Status Display: Display whose turn it is (e.g., "Turn: X"), who won (e.g., "Winner: X"), or if it is a draw (e.g., "Draw!").
  5. Reset Action: Provide a button that resets the current game without unmounting the component.
  6. Prop Reactivity: If the size prop changes at runtime, reset the game state to match the new board size.
  7. Accessibility (a11y): Use semantic grid semantics (role="grid", role="row", role="gridcell"), make cells keyboard-navigable (Tab / Enter), and ensure status updates are announced to screen readers via aria-live.

Component Signature

interface TicTacToeProps {
  size?: number; // integer between 3 and 20, default 3
}

export const TicTacToe: React.FC = ({ size = 3 }) => {
  // Implementation
};

Required Selectors (data-testid)

To facilitate automated testing, your component must include:

  • status: For the text element displaying current turn, winner, or draw.
  • cell-{row}-{col}: Zero-indexed cell buttons (e.g., cell-0-0, cell-2-1).
  • reset-button: For the reset button.

Performance & Edge Cases

  • At $N = 20$, the board has 400 cells. Avoid scanning the entire $N \times N$ matrix on every move; instead, determine game outcome in $O(1)$ or $O(N)$ time per move using row, column, and diagonal counters.
  • Guard against invalid prop updates (e.g., non-integers, values $ 20$).

Hints

Hint 1

Rather than traversing all cells after every move, maintain count arrays for rows, columns, and the two diagonals where 'X' adds 1 and 'O' subtracts 1.

Hint 2

Handle the `size` prop change either by tracking previous size in state or using a `key` pattern to reset board state automatically.

Hint 3

Ensure screen readers announce turns and game outcomes dynamically by adding an `aria-live="polite"` region to your status message.

Follow-up questions

  • How would you modify the win-checking algorithm if the win condition is modified to $K$ in a row (e.g., Gomoku style: $K=5$ on a $20 \times 20$ board)?
  • How would you implement full undo/redo functionality while preserving $O(1)$ time complexity per move?
  • If this board were connected to a real-time WebSocket backend for multiplayer play, how would you handle optimistic updates and race conditions?