Tic-Tac-Toe Game with Minimax Algorithm in Python

Tic-Tac-Toe Game with Minimax Algorithm in Python

·

5 min read

Introduction

In this article, we will explore how to create a Tic-Tac-Toe game using the minimax algorithm in Python. We'll discuss the problem statement, dive into the algorithmic approach, provide step-by-step explanations, and demonstrate a practical implementation. By the end, you will have a solid understanding of how the minimax algorithm can be applied to create an intelligent Tic-Tac-Toe game.

Problem Statement

Tic-Tac-Toe is a classic game played on a 3x3 grid. The goal of the game is to get three of your symbols (X or O) in a row, either horizontally, vertically, or diagonally, before the opponent does. In this problem, we aim to create an AI player that can play a perfect game of Tic-Tac-Toe using the minimax algorithm.

Algorithm

The minimax algorithm is a recursive algorithm used in game theory to determine the optimal move for a player. It considers all possible moves and assigns a score to each move based on the outcome of the game. The algorithm assumes that both players play optimally, trying to maximize their own score or minimize the opponent's score.

Here are the key steps of the minimax algorithm for Tic-Tac-Toe:

  1. Define the evaluation function to assign a score to a terminal state (win, loss, or draw) for the AI player.

  2. Implement the minimax function:

    • If the current state is a terminal state, return the evaluation score.

    • If it's the AI player's turn, maximize the score by recursively calling minimax on all possible moves and returning the maximum score.

    • If it's the opponent's turn, minimize the score by recursively calling minimax on all possible moves and returning the minimum score.

  3. Implement the find_best_move function:

    • Iterate through all possible moves.

    • For each move, calculate the minimax score by recursively calling minimax on the resulting state.

    • Return the move with the maximum score.

Pseudo Code

function minimax(state, depth, maximizing_player):
    if state is a terminal state:
        return evaluation score

    if maximizing_player:
        max_score = -infinity
        for each possible move in state:
            make the move
            score = minimax(state, depth + 1, False)
            undo the move
            max_score = max(max_score, score)
        return max_score

    else:
        min_score = +infinity
        for each possible move in state:
            make the move
            score = minimax(state, depth + 1, True)
            undo the move
            min_score = min(min_score, score)
        return min_score

function find_best_move(state):
    best_score = -infinity
    best_move = None
    for each possible move in state:
        make the move
        score = minimax(state, 0, False)
        undo the move
        if score > best_score:
            best_score = score
            best_move = move
    return best_move

Implementation

Now, let

's implement the Tic-Tac-Toe game with the minimax algorithm in Python.

# Define the Tic-Tac-Toe board
board = ['-'] * 9

# Define the players
player = 'X'
opponent = 'O'

# Define the winning combinations
winning_combinations = [
    [0, 1, 2], [3, 4, 5], [6, 7, 8],  # Rows
    [0, 3, 6], [1, 4, 7], [2, 5, 8],  # Columns
    [0, 4, 8], [2, 4, 6]               # Diagonals
]

# Function to print the Tic-Tac-Toe board
def print_board():
    print(board[0], board[1], board[2])
    print(board[3], board[4], board[5])
    print(board[6], board[7], board[8])
    print()

# Function to check if a player has won
def is_winner(player):
    for combination in winning_combinations:
        if board[combination[0]] == board[combination[1]] == board[combination[2]] == player:
            return True
    return False

# Function to check if the board is full
def is_board_full():
    return '-' not in board

# Function to make a move
def make_move(move, player):
    board[move] = player

# Function to undo a move
def undo_move(move):
    board[move] = '-'

# Minimax function
def minimax(depth, maximizing_player):
    # Base cases
    if is_winner(player):
        return 1
    if is_winner(opponent):
        return -1
    if is_board_full():
        return 0

    if maximizing_player:
        max_score = float('-inf')
        for move in range(9):
            if board[move] == '-':
                make_move(move, player)
                score = minimax(depth + 1, False)
                undo_move(move)
                max_score = max(max_score, score)
        return max_score

    else:
        min_score = float('inf')
        for move in range(9):
            if board[move] == '-':
                make_move(move, opponent)
                score = minimax(depth + 1, True)
                undo_move(move)
                min_score = min(min_score, score)
        return min_score

# Function to find the best move using minimax
def find_best_move():
    best_score = float('-inf')
    best_move = None
    for move in range(9):
        if board[move] == '-':
            make_move(move, player)
            score = minimax(0, False)
            undo_move(move)
            if score > best_score:
                best_score = score
                best_move = move
    return best_move

# Main game loop
while True:
    print_board()
    if is_board_full() or is_winner(player) or is_winner(opponent):
        break
    if player == 'X':
        move = int(input("Enter your move (0-8): "))
        while board[move] != '-':
            move = int(input("Invalid move. Enter your move (0-8): "))
    else:
        move = find_best_move()
    make_move(move, player)
    player, opponent = opponent, player

# Game over
print_board()
if is_winner(player):
    print("You win!")
elif is_winner(opponent):
    print("You lose!")
else:
    print("It's a draw!")

Explanation The implementation starts by defining the Tic-Tac-Toe board, players, and winning combinations. The print_board function is used to print the current state of the board.

The is_winner function checks if a player has won by iterating through the winning combinations and comparing the board positions. The is_board_full function checks if the board is completely filled with moves.

The make_move function updates the board with the player's move, while the undo_move function undoes the last move.

The minimax function implements the minimax algorithm recursively. It evaluates the score for each possible move by recursively calling minimax on the resulting state. The algorithm considers the maximizing player (player X) and the minimizing player (player O) alternately. The function returns the maximum score for the maximizing player and the minimum score for the minimizing player.

The find_best_move function iterates through all possible moves and calculates the minimax score for each move. It returns the move with the highest score.

Finally, the main game loop takes player input for move selection (if player X) or uses the find_best_move function (if player O) to make moves. The loop continues until the board is full or a player wins. After the game ends, the result is displayed.

Example

Let's consider a sample game:

- - -
- X -
- - -

In this state, it's player O's turn. The find_best_move function evaluates the best move and returns 0 (top-left corner). Player O makes the move:

O - -
- X -
- - -

Now, it's player X's turn. Let's assume they choose the center position:

O - -
- X -
- - -

The game continues until a player wins or the board is full.

Conclusion

In this article, we implemented the Tic-Tac-Toe game using the minimax algorithm in Python. We explored the problem statement, discussed the algorithmic approach, provided step-by-step explanations, and presented a practical implementation. By understanding and implementing the minimax algorithm, you can create an intelligent AI player for Tic-Tac-Toe that plays optimally and poses a challenge to human players.

Did you find this article valuable?

Support Aswnss Blog by becoming a sponsor. Any amount is appreciated!