Validate an extended tic-tac-toe state
Company: Snowflake
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates the ability to validate game states by reasoning about turn counts, win detection, and termination rules, testing competencies in game-state invariants and correctness checks within the Coding & Algorithms domain.
Constraints
- board always contains exactly 3 strings, each of length 3
- Each character is one of 'X', 'O', or '.'
- X always moves first, and players strictly alternate turns
- The game stops immediately after the first winning line is created
Examples
Input: (['...','...','...'],)
Expected Output: 'X_turn'
Explanation: No moves have been played yet, so the state is valid and X goes first.
Input: (['X..','...','...'],)
Expected Output: 'O_turn'
Explanation: X has made the first move and there is no winner, so it is now O's turn.
Input: (['XXX','OO.','...'],)
Expected Output: 'X_wins'
Explanation: X has a full top row and has exactly one more move than O, so this is a valid winning state for X.
Input: (['XXO','XO.','O..'],)
Expected Output: 'O_wins'
Explanation: O has the anti-diagonal and both players have made the same number of moves, so O must have moved last and won legally.
Input: (['XOX','OXX','OXO'],)
Expected Output: 'draw'
Explanation: The board is full and neither player has 3 in a row.
Input: (['OO.','...','...'],)
Expected Output: 'invalid'
Explanation: O cannot have more moves than X because X always starts.
Input: (['XXX','OOO','...'],)
Expected Output: 'invalid'
Explanation: Both players cannot be winners in a valid game, because the game stops as soon as the first win happens.
Input: (['XXX','OO.','O..'],)
Expected Output: 'invalid'
Explanation: X has a winning line, but X and O have played the same number of moves. If X wins, X must have exactly one extra move.
Input: (['XXX','OOX','OOX'],)
Expected Output: 'X_wins'
Explanation: X has 5 moves and O has 4. X can legally win on the final move by placing the shared cell that completes both the top row and the right column.
Solution
def solution(board):
x_count = sum(row.count('X') for row in board)
o_count = sum(row.count('O') for row in board)
# X starts, so valid move counts are either equal or X has one extra move.
if not (x_count == o_count or x_count == o_count + 1):
return 'invalid'
lines = []
for i in range(3):
lines.append([board[i][0], board[i][1], board[i][2]])
lines.append([board[0][i], board[1][i], board[2][i]])
lines.append([board[0][0], board[1][1], board[2][2]])
lines.append([board[0][2], board[1][1], board[2][0]])
def wins(player):
return any(all(cell == player for cell in line) for line in lines)
x_win = wins('X')
o_win = wins('O')
# Both players winning is impossible in a valid game.
if x_win and o_win:
return 'invalid'
# If X wins, X must have moved last.
if x_win:
return 'X_wins' if x_count == o_count + 1 else 'invalid'
# If O wins, O must have moved last.
if o_win:
return 'O_wins' if x_count == o_count else 'invalid'
# No winner.
if x_count + o_count == 9:
return 'draw'
return 'X_turn' if x_count == o_count else 'O_turn'
Time complexity: O(1). Space complexity: O(1).
Hints
- Start by counting how many X's and O's are on the board. Only two count relationships are ever possible in a valid game.
- Check all 8 possible winning lines. If X wins, X must have exactly one more move than O. If O wins, the counts must be equal.