Tic-Tac-Toe Against a Deterministic Automatic Opponent

Read the full interview experience this question came from →

Quick Overview

Build an automated Tic-Tac-Toe opponent with perfect-play minimax, deterministic move ties, board events, invalid attempts, and terminal-state handling.

Tic-Tac-Toe Against a Deterministic Automatic Opponent

Company: Databricks

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

Extend Tic-Tac-Toe with an automatic opponent. A human plays `X`, the computer plays `O`, and the board must be available for display after every human attempt and every actual computer move. Stop placing marks when either player wins or the board is a draw. Implement `play_against_ai(human_moves: int[][]) -> string[][]`. Each input row is a human attempt `[row, column]`. For each attempt, first emit the resulting human event. If the attempt is accepted and leaves an unfinished game, choose and apply one automatic `O` move and emit its event. Return all events in order. Each event contains eight strings: ```text [actor, accepted, board_row_0, board_row_1, board_row_2, status, next_player, move] ``` - `actor` is `X` or `O`; `accepted` is `"1"` or `"0"`. - Board rows contain `X`, `O`, and `.`. `status` is `IN_PROGRESS`, `X_WON`, `O_WON`, or `DRAW`. - `next_player` is `X` or `O` while unfinished, otherwise `-`. - `move` is the attempted coordinate as `row,column`, for example `1,2`, with no spaces. ### Automatic-player policy The report requests an AI opponent but does not specify its strength. For this deterministic practice version, use perfect-play minimax with these exact rules: - An `O` win has score `+1`, a draw score `0`, and an `X` win score `-1`. - On future `O` turns maximize this score; on future `X` turns minimize it. - Choose the legal `O` move with the best resulting score. Among tied moves, choose the smallest row, then smallest column. - Do not prefer faster wins or slower losses beyond that score and tie rule. ### Constraints & Assumptions - The board is initially empty, is 3 by 3, and `X` starts. There are at most 100 valid-coordinate human attempts. - Occupied-cell human attempts are rejected; they do not trigger an AI move or consume the human's turn. - Human attempts after a terminal result emit rejected `X` events with unchanged final state and do not trigger an AI move. - Check for a win before a full-board draw after every accepted move, including the human move. A terminal human move must never be followed by a computer move. - Search trial moves are internal only. Do not emit events for them, and undo them before applying the chosen move. - All returned snapshots are independent. An empty input returns an empty event list. ### Examples ```text human_moves = [[0,0]] result = [ ["X","1","X..","...","...","IN_PROGRESS","O","0,0"], ["O","1","X..",".O.","...","IN_PROGRESS","X","1,1"] ] ``` ```text human_moves = [[1,1],[1,1]] result = [ ["X","1","...",".X.","...","IN_PROGRESS","O","1,1"], ["O","1","O..",".X.","...","IN_PROGRESS","X","0,0"], ["X","0","O..",".X.","...","IN_PROGRESS","X","1,1"] ] ``` ```hint Reuse one terminal-state definition The live game and the search must agree about winning lines, draws, and whose turn follows a legal move. Evaluate terminal states before generating more moves. ```

Overview: Build an automated Tic-Tac-Toe opponent with perfect-play minimax, deterministic move ties, board events, invalid attempts, and terminal-state handling.

Read the full Databricks Software Engineer interview experience this question came from

|Home/Coding & Algorithms/Databricks
Databricks logo
Databricks
Sep 6, 2026
mediumSoftware EngineerOnsiteCoding & Algorithms
0
0

Extend Tic-Tac-Toe with an automatic opponent. A human plays X, the computer plays O, and the board must be available for display after every human attempt and every actual computer move. Stop placing marks when either player wins or the board is a draw.

Implement play_against_ai(human_moves: int[][]) -> string[][]. Each input row is a human attempt [row, column]. For each attempt, first emit the resulting human event. If the attempt is accepted and leaves an unfinished game, choose and apply one automatic O move and emit its event. Return all events in order.

Each event contains eight strings:

[actor, accepted, board_row_0, board_row_1, board_row_2, status, next_player, move]
  • actor is X or O ; accepted is "1" or "0" .
  • Board rows contain X , O , and . . status is IN_PROGRESS , X_WON , O_WON , or DRAW .
  • next_player is X or O while unfinished, otherwise - .
  • move is the attempted coordinate as row,column , for example 1,2 , with no spaces.

Automatic-player policy

The report requests an AI opponent but does not specify its strength. For this deterministic practice version, use perfect-play minimax with these exact rules:

  • An O win has score +1 , a draw score 0 , and an X win score -1 .
  • On future O turns maximize this score; on future X turns minimize it.
  • Choose the legal O move with the best resulting score. Among tied moves, choose the smallest row, then smallest column.
  • Do not prefer faster wins or slower losses beyond that score and tie rule.

Constraints & Assumptions

  • The board is initially empty, is 3 by 3, and X starts. There are at most 100 valid-coordinate human attempts.
  • Occupied-cell human attempts are rejected; they do not trigger an AI move or consume the human's turn.
  • Human attempts after a terminal result emit rejected X events with unchanged final state and do not trigger an AI move.
  • Check for a win before a full-board draw after every accepted move, including the human move. A terminal human move must never be followed by a computer move.
  • Search trial moves are internal only. Do not emit events for them, and undo them before applying the chosen move.
  • All returned snapshots are independent. An empty input returns an empty event list.

Examples

human_moves = [[0,0]]
result = [
  ["X","1","X..","...","...","IN_PROGRESS","O","0,0"],
  ["O","1","X..",".O.","...","IN_PROGRESS","X","1,1"]
]
human_moves = [[1,1],[1,1]]
result = [
  ["X","1","...",".X.","...","IN_PROGRESS","O","1,1"],
  ["O","1","O..",".X.","...","IN_PROGRESS","X","0,0"],
  ["X","0","O..",".X.","...","IN_PROGRESS","X","1,1"]
]

Submit Your Answer to Earn 20XP

Sign in to leave a comment

Loading comments...