Problem
Given a Tic-Tac-Toe board as a string array board
, return true
if and only if it is possible to reach this board position during the course of a valid tic-tac-toe game.
The board is a 3 x 3
array that consists of characters ' '
, 'X'
, and 'O'
. The ' '
character represents an empty square.
Here are the rules of Tic-Tac-Toe:
- Players take turns placing characters into empty squares
' '
. - The first player always places
'X'
characters, while the second player always places'O'
characters. 'X'
and'O'
characters are always placed into empty squares, never filled ones.- The game ends when there are three of the same (non-empty) character filling any row, column, or diagonal.
- The game also ends if all squares are non-empty.
- No more moves can be played if the game is over.
Examples
Example 1:
$$ \begin{array}{|c|c|c|} \hline \cellcolor{orange} O & & \ \hline & & \ \hline & & \ \hline \end{array} $$
Input: board = ["O "," "," "]
Output: false
Explanation: The first player always plays "X".
Example 2:
$$ \begin{array}{|c|c|c|} \hline \cellcolor{lightblue} X & \cellcolor{orange} O & \cellcolor{lightblue} X \ \hline & \cellcolor{lightblue} X & \ \hline & & \ \hline \end{array} $$
Input: board = ["XOX"," X "," "]
Output: false
Explanation: Players take turns making moves.
Example 3:
$$ \begin{array}{|c|c|c|} \hline \cellcolor{lightblue} X & \cellcolor{orange} O & \cellcolor{lightblue} X \ \hline \cellcolor{orange} O & & \cellcolor{orange} O \ \hline \cellcolor{lightblue} X & \cellcolor{orange} O & \cellcolor{lightblue} X \ \hline \end{array} $$
Input: board = ["XOX","O O","XOX"]
Output: true
Solution
Method 1 - Simple Heuristics
Here are the key points:
- Player Turn Rules:
- Since player ‘X’ always starts, the count of ‘X’ should be greater than or equal to the count of ‘O’. Therefore, if
o_count > x_count
, the board is invalid. - Players alternate turns, so the difference between the count of ‘X’ and ‘O’ should not exceed one. Thus, if
x_count - o_count > 1
, the board is invalid.
- Since player ‘X’ always starts, the count of ‘X’ should be greater than or equal to the count of ‘O’. Therefore, if
- Winning Conditions:
- If player ‘O’ has a winning condition, ensure that player ‘X’ does not have a winning condition at the same time. This is because the game should stop as soon as a winning condition is met.
- If player ‘O’ wins, then
x_count
should equalo_count
since ‘O’ plays second and the counts must match up to this condition. - If player ‘X’ wins, then
x_count
should be exactly one more thano_count
because ‘X’ starts first.
- Algorithm:
- Check initial conditions for count validity.
- Evaluate the board to see if either player has a winning configuration.
- Validate that the counts and winning conditions align correctly based on the rules of the game.
Here is a more detailed explanation:
- Check Initial Count Validity:
- If
o_count > x_count
, returnFalse
. - If
x_count - o_count > 1
, returnFalse
.
- If
- Check Winning Conditions:
- If player ‘O’ wins:
- Ensure player ‘X’ does not also win. If both win, return
False
. - Ensure
x_count == o_count
. If not, returnFalse
.
- Ensure player ‘X’ does not also win. If both win, return
- If player ‘X’ wins:
- Ensure
x_count == o_count + 1
. If not, returnFalse
.
- Ensure
- If player ‘O’ wins:
Code
Java
class Solution {
public boolean validTicTacToe(String[] board) {
int xCount = 0, oCount = 0;
for (String row : board) {
for (char c : row.toCharArray()) {
if (c == 'X') xCount++;
if (c == 'O') oCount++;
}
}
// Initial count validity
if (oCount > xCount) return false;
if (xCount > oCount + 1) return false;
boolean xWins = isWinner(board, 'X');
boolean oWins = isWinner(board, 'O');
// Checking winning conditions
if (oWins) {
if (xWins) return false; // Both can't win simultaneously
if (xCount != oCount) return false; // 'O' wins so counts must be equal
}
if (xWins) {
if (xCount != oCount + 1) return false; // 'X' wins so must have one more count than 'O'
}
return true;
}
private boolean isWinner(String[] board, char p) {
for (int i = 0; i < 3; i++) {
if (board[i].charAt(0) == p && board[i].charAt(1) == p && board[i].charAt(2) == p) return true;
if (board[0].charAt(i) == p && board[1].charAt(i) == p && board[2].charAt(i) == p) return true;
}
if (board[0].charAt(0) == p && board[1].charAt(1) == p && board[2].charAt(2) == p) return true;
if (board[0].charAt(2) == p && board[1].charAt(1) == p && board[2].charAt(0) == p) return true;
return false;
}
}
Python
class Solution:
def validTicTacToe(self, board: List[str]) -> bool:
x_count = sum(row.count('X') for row in board)
o_count = sum(row.count('O') for row in board)
# Initial count validity
if o_count > x_count:
return False
if x_count > o_count + 1:
return False
x_wins = self.is_winner(board, 'X')
o_wins = self.is_winner(board, 'O')
# Checking winning conditions
if o_wins:
if x_wins:
return False # Both can't win simultaneously
if x_count != o_count:
return False # 'O' wins so counts must be equal
if x_wins:
if x_count != o_count + 1:
return False # 'X' wins so must have one more count than 'O'
return True
def is_winner(self, board: List[str], p: str) -> bool:
for i in range(3):
if board[i][0] == p and board[i][1] == p and board[i][2] == p:
return True
if board[0][i] == p and board[1][i] == p and board[2][i] == p:
return True
if board[0][0] == p and board[1][1] == p and board[2][2] == p:
return True
if board[0][2] == p and board[1][1] == p and board[2][0] == p:
return True
return False
`
Complexity
- ⏰ Time complexity:
O(1)
, because the board size is fixed at3 x 3
. Checking each cell and verifying win conditions is constant. - 🧺 Space complexity:
O(1)
, as no additional space proportional to input size is required beyond a few counters and boolean flags.