Assume n is the size of the game board. The Tic-Tac-Toe board is size n x n.
A move is guaranteed to be valid and is placed on an empty block.
A player who achieves n same marks (row, column, diagonal, or anti-diagonal) wins the game.
Implement the TicTacToe class:
TicTacToe(int n) Initializes the object with the size of the board n.int move(int row, int col, int player) Indicates that player player moves at cell (row, col) on the board. The function returns:0 if no one wins.1 if player 1 wins.2 if player 2 wins.Example 1:
Input
["TicTacToe", "move", "move", "move", "move", "move", "move"]
[[3], [0, 0, 1], [0, 2, 2], [2, 2, 1], [1, 1, 2], [2, 0, 1], [1, 0, 2]]
Output
[null, 0, 0, 0, 0, 0, 2]
Explanation
TicTacToe ticTacToe = new TicTacToe(3);
// Assume that player 1 is "X" and player 2 is "O"
Board after move("X", 0, 0) =
| X | | |
| | | |
| | | |
move("X", 0, 0); // return 0 (no one wins)
Board after move("O", 0, 2) =
| X | | O |
| | | |
| | | |
move("O", 0, 2); // return 0 (no one wins)
Board after move("X", 2, 2) =
| X | | O |
| | | |
| | | X |
move("X", 2, 2); // return 0 (no one wins)
Board after move("O", 1, 1) =
| X | | O |
| | O | |
| | | X |
move("O", 1, 1); // return 0 (no one wins)
Board after move("X", 2, 0) =
| X | | O |
| | O | |
| X | | X |
move("X", 2, 0); // return 0 (no one wins)
Board after move("O", 1, 0) =
| X | | O |
| O | O | |
| X | | X |
move("O", 1, 0); // return 2 (player 2 wins, because there is a row with three 'O's).
Constraints:
2 <= n <= 100player is 1 or 2.0 <= row, col < n(row, col) are valid and empty.n2 calls will be made to move.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 designing Tic-Tac-Toe is like imagining every single possible game that could ever be played. We explore every move a player could make at each step of the game until the game ends.
Here's how the algorithm would work step-by-step:
The optimal strategy for Tic-Tac-Toe involves representing the game board efficiently and then quickly checking for winning conditions after each move. We can avoid complex checks by focusing on what actually matters: a complete line of three for either player.
Here's how the algorithm would work step-by-step:
class TicTacToe:
def __init__(self, board_size):
self.board_size = board_size
self.rows_player1 = [0] * board_size
self.rows_player2 = [0] * board_size
self.columns_player1 = [0] * board_size
self.columns_player2 = [0] * board_size
self.diagonal_main_player1 = 0
self.diagonal_main_player2 = 0
self.diagonal_anti_player1 = 0
self.diagonal_anti_player2 = 0
self.moves_made = 0
def move(self, player_identifier, row_index, column_index):
# Player 1 is represented by 1, Player 2 by 2.
if player_identifier == 1:
self.rows_player1[row_index] += 1
self.columns_player1[column_index] += 1
# Check if the move is on the main diagonal.
if row_index == column_index:
self.diagonal_main_player1 += 1
# Check if the move is on the anti-diagonal.
if row_index + column_index == self.board_size - 1:
self.diagonal_anti_player1 += 1
# Winning condition must be checked after each player's move.
if self.rows_player1[row_index] == self.board_size or \
self.columns_player1[column_index] == self.board_size or \
self.diagonal_main_player1 == self.board_size or \
self.diagonal_anti_player1 == self.board_size:
return 1
else:
self.rows_player2[row_index] += 1
self.columns_player2[column_index] += 1
# Check if the move is on the main diagonal.
if row_index == column_index:
self.diagonal_main_player2 += 1
# Check if the move is on the anti-diagonal.
if row_index + column_index == self.board_size - 1:
self.diagonal_anti_player2 += 1
# Winning condition must be checked after each player's move.
if self.rows_player2[row_index] == self.board_size or \
self.columns_player2[column_index] == self.board_size or \
self.diagonal_main_player2 == self.board_size or \
self.diagonal_anti_player2 == self.board_size:
return 2
self.moves_made += 1
# Return 0 if no winner and game is not a draw.
if self.moves_made == self.board_size * self.board_size:
return 0
else:
return -1
| Case | How to Handle |
|---|---|
| Null or empty list of moves | An empty or null input should result in the game state being 'ongoing' with no winner or draw. |
| Moves exceeding the 3x3 board dimensions | Moves with row or column indices outside the 0-2 range should be considered invalid and potentially ignored or raise an error. |
| Duplicate moves at the same row and column | The system should handle subsequent moves to an already occupied cell by either ignoring them or flagging an error, depending on strictness. |
| Moves made after a player has already won | The system should recognize a win state and ideally stop processing further moves, or at least not alter the game state once a winner is declared. |
| A full board with no winner (draw condition) | The system must correctly identify a draw when all 9 cells are filled and no player has met the winning criteria. |
| A sequence of moves that could lead to simultaneous wins if not checked sequentially | The game state should be checked after each individual move to declare the winner as soon as the winning condition is met. |
| A player making moves of the wrong type (not 'X' or 'O') | Invalid player characters should be rejected or flagged as errors to maintain game integrity. |
| The total number of moves being less than 5 (minimum for a potential win) | The system should correctly report the game as 'ongoing' if insufficient moves have been made to possibly achieve a win. |