Home / Articles / Game Trees

Game Trees

Game trees are a fundamental concept in AI for two-player games. The minimax algorithm uses a game tree to choose the optimal move by looking ahead several turns and assuming both players play perfectly.

What Is a Game Tree?

A game tree is a tree structure where:

For a game like tic-tac-toe, the full tree is small enough to explore completely. For chess, the tree is astronomically large — so we search only a few levels deep.

The Minimax Algorithm

The core idea: one player tries to maximize the score, the other tries to minimize it.

The algorithm recursively evaluates all possible future states to a given depth, then backs up scores to the root.

Scoring

At leaf nodes (or at the depth limit), a heuristic evaluation function assigns a score:

For chess, this might be based on material count. For tic-tac-toe, +1 for X wins, -1 for O wins, 0 for draw.

Minimax in C

Here's a basic minimax implementation with a configurable lookahead depth:

#include <stdio.h>
#include <limits.h>

#define MAX_DEPTH 4

/* Returns the score of the best move for the current player.
   is_max: 1 if the current player is maximizing, 0 if minimizing. */
int minimax(int board[], int depth, int is_max) {
    int score = evaluate(board);

    /* Terminal conditions */
    if (score == 10)  return score;  /* MAX wins */
    if (score == -10) return score;  /* MIN wins */
    if (no_moves_left(board)) return 0;  /* Draw */
    if (depth == 0) return score;  /* Depth limit reached */

    if (is_max) {
        int best = INT_MIN;
        for (int i = 0; i < 9; i++) {
            if (board[i] == 0) {
                board[i] = 1;  /* MAX plays */
                int val = minimax(board, depth - 1, 0);
                if (val > best) best = val;
                board[i] = 0;  /* Undo move */
            }
        }
        return best;
    } else {
        int best = INT_MAX;
        for (int i = 0; i < 9; i++) {
            if (board[i] == 0) {
                board[i] = -1;  /* MIN plays */
                int val = minimax(board, depth - 1, 1);
                if (val < best) best = val;
                board[i] = 0;  /* Undo move */
            }
        }
        return best;
    }
}

/* Find the best move for MAX */
int best_move(int board[]) {
    int best_val = INT_MIN;
    int best_pos = -1;

    for (int i = 0; i < 9; i++) {
        if (board[i] == 0) {
            board[i] = 1;
            int move_val = minimax(board, MAX_DEPTH, 0);
            board[i] = 0;

            if (move_val > best_val) {
                best_val = move_val;
                best_pos = i;
            }
        }
    }
    return best_pos;
}

Lookahead Depth

The depth controls how far ahead the algorithm looks. A higher depth means better play but exponentially more computation. The number of nodes examined grows as b^d, where:

For chess, b ≈ 35, so depth 4 means examining roughly 35^4 ≈ 1.5 million nodes. This is why commercial chess engines use advanced techniques like alpha-beta pruning and iterative deepening on top of minimax.

Alpha-Beta Pruning

Alpha-beta pruning is an optimization that prunes branches of the tree that cannot possibly influence the final decision. It achieves the same result as minimax but examines far fewer nodes — in the best case, the number of nodes is roughly b^(d/2) instead of b^d.

The idea: maintain two values during the search:

When beta <= alpha, the current branch can be cut off — neither player would choose to go down it.

Applications

Game trees apply to any perfect-information two-player zero-sum game:

The same framework extends to multi-player games and stochastic (chance-based) games with modifications like the expectimax algorithm.