1112 words
6 minutes
How Much Faster Do You Need to Be to Beat AI in Tic-Tac-Toe?

Almost everyone in the world has played tic-tac-toe at least once. It’s a simple game that requires nothing more than a piece of paper and a pen. Some players are so skilled that they can beat almost anyone. However, even the best players cannot defeat an AI in this game. If you doubt this, try playing against this AI first. If you’re unfamiliar with tic-tac-toe or how to play it, read on.

The Game#

Tic-tac-toe (American English), noughts and crosses (Commonwealth English), or Xs and Os (Irish English) is a paper-and-pencil game for two players who take turns marking spaces in a three-by-three grid with X or O. The player who succeeds in placing three of their marks in a horizontal, vertical, or diagonal row wins. It is a solved game, meaning the outcome is always a forced draw if both players play optimally.

Wikipedia

How Does the AI Work?#

As you might guess, the AI anticipates all your possible moves. There are 9! (read as “nine factorial”) possible ways to fill a tic-tac-toe board, totaling 362,880. However, since players take turns placing X or O, many of these options are redundant (like filling the entire board with X). In reality, the board can be filled in 3^9 possible states, which equals 19,683. But how does the AI decide where to place its mark to ensure victory? The answer lies in its decision-making algorithm. So, what is this algorithm, and how does it work?

The Minimax Algorithm#

For almost any two-player game where one player’s win means the other’s loss (or a draw is possible), certain AI algorithms can compute the best move to secure a win. These algorithms evaluate all possible opponent responses and score each game state to determine the optimal move. While options like Monte Carlo Tree Search (MCTS) or Alpha-Beta pruning exist, I chose the minimax algorithm for tic-tac-toe because it performs exceptionally well here. Let’s explore how it works.

Imagine you’re X, and the AI is O. Suppose the game reaches this state:

First, we define rules to help the AI score each state. After every move, the AI checks if someone has won. If the AI wins, the state scores +10. If the opponent wins, the state scores -10. A draw results in a score of 0. If the game is still ongoing, the score is calculated based on subsequent possible moves. When the AI makes a decision, it’s called “maximizing”; otherwise, it’s “minimizing.”

Now, the AI must decide its next move. It identifies four possible options:

Option 1 Option 2 Option 3 Option 4

In this scenario, the AI (O) is maximizing. Option 4 results in an immediate win for the AI, scoring +10. For the other states, the game continues, and the AI switches to minimizing mode, evaluating the player’s potential moves. Here’s a diagram illustrating the process:

{ const img = document.getElementById("ai-flow-img"); if (img.requestFullscreen) { img.requestFullscreen(); } else if (img.webkitRequestFullscreen) { img.webkitRequestFullscreen(); } else if (img.msRequestFullscreen) { img.msRequestFullscreen(); } }} >
Zoom in to view better (click to fullscreen)

As you can see, beating the AI in tic-tac-toe is impossible because it evaluates all possible moves faster than any human. The best you can achieve is a draw. If you want a challenge, try playing a variant like 3D tic-tac-toe (though I can’t guarantee an AI won’t be waiting for you there either).

Now, to answer the title’s question: There’s no speed at which you can compute moves that would allow you to beat the AI. Even with the processing power of a quantum computer, defeating the AI is impossible. You can only force endless draws. The only way to “win” is by imposing speed constraints—for example, if each players has to move in 0.001 seconds, and you can compute moves in 0.0000000001 seconds while the AI takes 0.1 seconds. In this case, you’d win not by outplaying the AI but by forcing it to time out.

Implementation#

To implement tic-tac-toe with the minimax AI, use a 3x3 array to track the game state. After each player move, call the AI to determine its move. Here’s the AI class I wrote:

class TTT_AI {
constructor(debug) {
this.debug = debug;
}
setDebug(debug) {
this.debug = debug;
}
randomEmptyPlace(gameBoard) {
var emptyPlaces = [];
for (var i = 0; i < gameBoard.length; i++) {
for (var j = 0; j < gameBoard[i].length; j++) {
if (gameBoard[i][j] == '') {
emptyPlaces.push([i, j]);
}
}
}
if (emptyPlaces.length == 0) {
return false;
} else {
var randomIndex = Math.floor(Math.random() * emptyPlaces.length);
return emptyPlaces[randomIndex];
}
}
scores = {
'O': 10,
'X': -10,
'tie': 0
}
minimax(gameBoard, depth, isMaximizing) {
let aiGameBoard = gameBoard;
let result = checkWinner(aiGameBoard);
if (result !== false) {
return this.scores[result];
}
if (isMaximizing) {
let bestScore = -Infinity;
for (let i = 0; i < aiGameBoard.length; i++) {
for (let j = 0; j < aiGameBoard[i].length; j++) {
if (aiGameBoard[i][j] == '') {
aiGameBoard[i][j] = 'O';
let score = this.minimax(aiGameBoard, depth + 1, false);
aiGameBoard[i][j] = '';
bestScore = Math.max(score, bestScore);
}
}
}
return bestScore;
} else {
let bestScore = Infinity;
for (let i = 0; i < aiGameBoard.length; i++) {
for (let j = 0; j < aiGameBoard[i].length; j++) {
if (aiGameBoard[i][j] == '') {
aiGameBoard[i][j] = 'X';
let score = this.minimax(aiGameBoard, depth + 1, true);
aiGameBoard[i][j] = '';
bestScore = Math.min(score, bestScore);
}
}
}
return bestScore;
}
}
pickBestMove(gameBoard) {
let ai = 'O';
let bestScore = -Infinity;
let aiGameBoard = gameBoard;
var bestMove;
for (let i = 0; i < aiGameBoard.length; i++) {
for (let j = 0; j < aiGameBoard[i].length; j++) {
if (aiGameBoard[i][j] == '') {
aiGameBoard[i][j] = ai;
let score = this.minimax(aiGameBoard, 0, false);
aiGameBoard[i][j] = '';
if (score > bestScore) {
bestScore = score;
bestMove = [i, j];
}
}
}
}
return bestMove;
}
action(gameBoard) {
if (this.debug) {
console.log('AI is thinking...');
}
let bestMove = this.pickBestMove(gameBoard);
console.log(bestMove);
return bestMove;
}
}

To use this AI, create an instance and call the action method, passing the gameBoard array as an argument:

function oAction() {
var minimaxMovement = _AI.action(gameBoard);
gameBoard[minimaxMovement[0]][minimaxMovement[1]] = 'O';
}

Here’s Github repo containing source code:

0xDeviI
/
tic-tac-toe-js
Waiting for api.github.com...
00K
0K
0K
Waiting...

Conclusion#

In this article, I explained how the minimax algorithm works and why it guarantees the AI’s victory in tic-tac-toe. The same algorithm can be applied to other win-lose games like chess. Though, because of the vast amount of possible moves in chess, you may need to use other algorithms, such as alpha-beta pruning, as well.