Tic-tac-toe is played by two players A and B on a 3 x 3 grid. The rules of Tic-Tac-Toe are:
' '.A always places 'X' characters, while the second player B always places 'O' characters.'X' and 'O' characters are always placed into empty squares, never on filled ones.Given a 2D integer array moves where moves[i] = [rowi, coli] indicates that the ith move will be played on grid[rowi][coli]. return the winner of the game if it exists (A or B). In case the game ends in a draw return "Draw". If there are still movements to play return "Pending".
You can assume that moves is valid (i.e., it follows the rules of Tic-Tac-Toe), the grid is initially empty, and A will play first.
Example 1:
Input: moves = [[0,0],[2,0],[1,1],[2,1],[2,2]] Output: "A" Explanation: A wins, they always play first.
Example 2:
Input: moves = [[0,0],[1,1],[0,1],[0,2],[1,0],[2,0]] Output: "B" Explanation: B wins.
Example 3:
Input: moves = [[0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2]] Output: "Draw" Explanation: The game ends in a draw since there are no moves to make.
Constraints:
1 <= moves.length <= 9moves[i].length == 20 <= rowi, coli <= 2moves.moves follow the rules of tic tac toe.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to finding the Tic Tac Toe winner involves checking every single possible way a player could win after each move is made. This means after each placement, we examine all rows, columns, and diagonals to see if they now contain three of the same mark.
Here's how the algorithm would work step-by-step:
def find_winner_tic_tac_toe(moves):
board_size = 3
board = [['' for _ in range(board_size)] for _ in range(board_size)]
player_one = 'A'
player_two = 'B'
for move_index, move in enumerate(moves):
row_coordinate, column_coordinate = move
current_player = player_one if move_index % 2 == 0 else player_two
board[row_coordinate][column_coordinate] = current_player
# Check for a winner after each move
if move_index >= 2:
# Check rows for a win
for row_index in range(board_size):
if board[row_index][0] == board[row_index][1] == board[row_index][2] != '':
return board[row_index][0]
# Check columns for a win
for column_index in range(board_size):
if board[0][column_index] == board[1][column_index] == board[2][column_index] != '':
return board[0][column_index]
# Check diagonals for a win
if board[0][0] == board[1][1] == board[2][2] != '':
return board[0][0]
if board[0][2] == board[1][1] == board[2][0] != '':
return board[0][2]
# Check if the game is a draw
if len(moves) == board_size * board_size:
return 'Draw'
# If no winner and moves remain, the game is pending
return 'Pending'We don't need to check every possible winning line after each move. The best approach is to keep track of how close each player is to winning as the game goes on. This way, we only need to check for a winner once someone has a potential winning line completed.
Here's how the algorithm would work step-by-step:
def find_winner_on_tic_tac_toe_game(moves):
board_size = 3
row_counts_player_one = [0] * board_size
column_counts_player_one = [0] * board_size
diagonal_count_player_one = 0
anti_diagonal_count_player_one = 0
row_counts_player_two = [0] * board_size
column_counts_player_two = [0] * board_size
diagonal_count_player_two = 0
anti_diagonal_count_player_two = 0
for move_index, move in enumerate(moves):
row, column = move
player = (move_index % 2) + 1
if player == 1:
row_counts_player_one[row] += 1
column_counts_player_one[column] += 1
if row == column:
diagonal_count_player_one += 1
if row + column == board_size - 1:
anti_diagonal_count_player_one += 1
# Check win condition after each move
if (row_counts_player_one[row] == board_size or\
column_counts_player_one[column] == board_size or\
diagonal_count_player_one == board_size or\
anti_diagonal_count_player_one == board_size):
return "A"
else:
row_counts_player_two[row] += 1
column_counts_player_two[column] += 1
if row == column:
diagonal_count_player_two += 1
if row + column == board_size - 1:
anti_diagonal_count_player_two += 1
# Check win condition after each move
if (row_counts_player_two[row] == board_size or\
column_counts_player_two[column] == board_size or\
diagonal_count_player_two == board_size or\
anti_diagonal_count_player_two == board_size):
return "B"
# If all moves are made and no winner, it's a draw.
if len(moves) == board_size * board_size:
return "Draw"
# If moves remain and no winner, the game is pending.
return "Pending"| Case | How to Handle |
|---|---|
| Null or empty moves array | Return 'Pending' immediately as the game hasn't started. |
| Invalid move coordinates (out of board bounds) | Throw an IllegalArgumentException or return an error string like 'Invalid Move' since the board is only 3x3. |
| A move is placed on an already occupied cell | Throw an IllegalArgumentException or return an error string, indicating invalid move as it violates the rules. |
| Moves do not alternate between players (e.g., player A moves twice in a row) | Maintain player turn information to ensure correct alternation and return an error if it's violated. |
| Game ends before all 9 moves are made with no winner | After all moves are processed and no winner is found, return 'Draw'. |
| Input contains only moves by player A or player B | The algorithm should check for alternating moves and return an error if moves are not alternating. |
| A player wins before all possible moves are made | The algorithm should detect the win and return 'A' or 'B' immediately. |
| The input contains negative values or non-integer values | The algorithm should validate the input and throw an exception, or return an appropriate error string to indicate invalid input. |