Tic-Tac-Toe Against a Deterministic Automatic Opponent
DatabricksSoftware Engineer · OnsiteLast updated
Build an automated Tic-Tac-Toe opponent with perfect-play minimax, deterministic move ties, board events, invalid attempts, and terminal-state handling.
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]
actorisXorO;acceptedis"1"or"0".- Board rows contain
X,O, and..statusisIN_PROGRESS,X_WON,O_WON, orDRAW. next_playerisXorOwhile unfinished, otherwise-.moveis the attempted coordinate asrow,column, for example1,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
Owin has score+1, a draw score0, and anXwin score-1. - On future
Oturns maximize this score; on futureXturns minimize it. - Choose the legal
Omove 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
Xstarts. 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
Xevents 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"]
]
Hints
Hint 1
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.
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.