Introduction
Tic Tac Toe is a paper and pencil game that can be part of every person's childhood memories. This game is also known as noughts and crosses. Whatever it's called, there is no doubt one of the most memorable parts of our school day.
Only two players can play this game on a three-by-three grid. Player - X and Player - O. If a player has to win this game, he has to give his mark along with three positions.
We used to play this game with pen and paper. As everything is going digital; So now everyone prefers to play most of the games on mobile or computer. Here we will create such a computer game.
In this tutorial, we will create a Tic Tac Toe Game in Python. The graphics of this game is managed using the PyGame library. Now we will play this game against a computer and the computer will play this game using Artificial Intelligence. One of the interesting things about this game is that the computer will be unbeatable. That means you cannot win this game against the computer.
☛Visit Also: Six Degrees of Kevin Bacon in Python: Artificial Intelligence Project
Project Details
There are two ways to make the game unbeatable; using the Minimax algorithm, or Alpha-beta pruning. I used the first one.
Whenever the player makes a decision, the computer takes the most optimal action using the minimax algorithm. There are only two possibilities at the end of the game. Either the computer wins or the game is tied.
This project contains two python files, runner.py, and tictactoe.py. runner.py contains the code for managing the graphics of the game. tictactoe.py contains all the necessary functions and logic that make the optimal move for every state.
Requirement
Before you start writing your code just take a break here. Install the PyGame library if you didn't already.
👉Install PyGame: pip install pygame
tictactoe.py
Importing the libraries
Let's start writing your code by importing these modules.
import math
from random import choice
from math import inf as infinity
X = "X"
O = "O"
EMPTY = None
pl = X
first = True
The Initial state function
The initial_state function returns an empty three-by-three grid on which the program will keep the input.
def initial_state():
"""
Returns starting state of the board.
"""
return [[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]]
The player function
The player function takes the game state and returns the player who's the next to take the next turn. In this case, player - X or player - O. Generally X gets the first move and then the turn rotates for each move until the terminal state comes.
def player(board):
"""
Returns player who has the next turn on the board.
"""
global pl
global first
NoOfX = 0
NoOfO = 0
if first is True:
first = False
return pl
else:
for i in range(0, len(board)):
for j in range(0, len(board[0])):
if board[i][j] == X:
NoOfX += 1
elif board[i][j] == O:
NoOfO += 1
if NoOfX > NoOfO:
pl = O
else:
pl = X
return pl
The action function
The action function takes the board as an input and returns a set of all possible actions available on the board. The possible actions are represented as a tuple (i, j) where 'i' represents the row number(1, 2, or, 3) and 'j' represents the column number(1, 2, or 3).
Possible actions or places are any empty cells on the given board.
def actions(board):
"""
Returns set of all possible actions (i, j) available on the board.
"""
places = []
if not terminal(board):
for x, row in enumerate(board):
for y, cell in enumerate(row):
if cell == None:
places.append([x, y])
return places
The result function
The result function takes the board and action as input and returns the board that results from making a move on the board without modifying the original board.
def result(board, action):
"""
Returns the board that results from making move (i, j) on the board.
"""
b = board
x, y = action[0], action[1]
b[x][y] = player(board)
return b
The winner function
The winner function takes the board as an input and returns the winner of the game, if there is any, otherwise, returns None.
There are nine positions horizontally, vertically, and diagonally. The player has to place his/her sign row-wise in one of the three places to win this game.
def winner(board):
"""
Returns the winner of the game, if there is one.
"""
WinState = [
[board[0][0], board[0][1], board[0][2]],
[board[1][0], board[1][1], board[1][2]],
[board[2][0], board[2][1], board[2][2]],
[board[0][0], board[1][0], board[2][0]],
[board[0][1], board[1][1], board[2][1]],
[board[0][2], board[1][2], board[2][2]],
[board[0][0], board[1][1], board[2][2]],
[board[2][0], board[1][1], board[0][2]],
]
if [X, X, X] in WinState:
return X
elif [O, O, O] in WinState:
return O
else:
return None
The terminal function
The terminal function takes the board as input and checks the current state of the board. If the current state is the terminal state then it returns True, otherwise, it returns False.
def terminal(board):
"""
Returns True if the game is over, False otherwise.
"""
if (winner(board) is not None) or (not any(EMPTY in sublist for
sublist in board) and winner(board) is None):
return True
else:
return False
The utility function
The utility function returns 1 if the X has won the game, and -1 if O has won, otherwise returns 0(for the tie), if and only if the current board state is the terminal state.
def utility(board):
"""
Returns 1 if X has won the game, -1 if O has won, and 0 otherwise.
"""
if terminal(board):
if winner(board) == X:
score = 1
elif winner(board) == O:
score = -1
else:
score = 0
return score
The minimax function
The minimax function is the most complex part of this code. It returns the most optimal move for the computer. I've divided the whole logic of this algorithm into two functions, "AI_Turn" and "minimax" functions.
Working Principle of Minimax Algorithm in Tic Tac Toe Game |
# AI Turn Function
def AI_Turn(board, length, pl):
if pl == X:
best = [-1, -1, -infinity]
elif pl == O:
best = [-1, -1, +infinity]
if length == 0 or terminal(board):
score = utility(board)
return [-1, -1, score]
for cell in actions(board):
x, y = cell[0], cell[1]
board[x][y] = pl
score = AI_Turn(board, length - 1, player(board))
board[x][y] = EMPTY
score[0], score[1] = x, y
if pl == X:
if score[2] > best[2]:
best = score # Max value
else:
if score[2] < best[2]:
best = score # Min value
return best
def minimax(board):
"""
Returns the optimal action for the current player on the board.
"""
length = len(actions(board))
if length == 0 or terminal(board):
return None
if length == 9:
x = choice([0, 1, 2])
y = choice([0, 1, 2])
else:
move = AI_Turn(board, length, pl)
x, y = move[0], move[1]
return [x, y]
runner.py
Look at the yellow line. It's a font file and must be present in the main directory. You can download it through the download button below.
import pygame
import sys
import time
import tictactoe as ttt
pygame.init()
size = width, height = 600, 400
# Colors
black = (0, 0, 0)
white = (255, 255, 255)
screen = pygame.display.set_mode(size)
mediumFont = pygame.font.Font("OpenSans-Regular.ttf", 28)
largeFont = pygame.font.Font("OpenSans-Regular.ttf", 40)
moveFont = pygame.font.Font("OpenSans-Regular.ttf", 60)
user = None
board = ttt.initial_state()
ai_turn = False
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(black)
# Let the user choose a player.
if user is None:
# Draw title
title = largeFont.render("Tic-Tac-Toe", True, white)
titleRect = title.get_rect()
titleRect.center = ((width / 2), 50)
screen.blit(title, titleRect)
# Draw buttons
playXButton = pygame.Rect((width / 8), (height / 2), width / 4, 50)
playX = mediumFont.render("Play as X", True, black)
playXRect = playX.get_rect()
playXRect.center = playXButton.center
pygame.draw.rect(screen, white, playXButton)
screen.blit(playX, playXRect)
playOButton = pygame.Rect(5 * (width / 8), (height / 2), width / 4, 50)
playO = mediumFont.render("Play as O", True, black)
playORect = playO.get_rect()
playORect.center = playOButton.center
pygame.draw.rect(screen, white, playOButton)
screen.blit(playO, playORect)
# Check if button is clicked
click, _, _ = pygame.mouse.get_pressed()
if click == 1:
mouse = pygame.mouse.get_pos()
if playXButton.collidepoint(mouse):
time.sleep(0.2)
user = ttt.X
elif playOButton.collidepoint(mouse):
time.sleep(0.2)
user = ttt.O
else:
# Draw game board
tile_size = 80
tile_origin = (width / 2 - (1.5 * tile_size),
height / 2 - (1.5 * tile_size))
tiles = []
for i in range(3):
row = []
for j in range(3):
rect = pygame.Rect(
tile_origin[0] + j * tile_size,
tile_origin[1] + i * tile_size,
tile_size, tile_size
)
pygame.draw.rect(screen, white, rect, 3)
if board[i][j] != ttt.EMPTY:
move = moveFont.render(board[i][j], True, white)
moveRect = move.get_rect()
moveRect.center = rect.center
screen.blit(move, moveRect)
row.append(rect)
tiles.append(row)
game_over = ttt.terminal(board) # At first Result should be False
player = ttt.player(board) # At first it should be the X
# Show title
if game_over:
winner = ttt.winner(board)
if winner is None:
title = f"Game Over: Tie."
else:
title = f"Game Over: {winner} wins."
elif user == player:
title = f"Your turn {user}"
else:
title = f"Computer thinking..."
title = largeFont.render(title, True, white)
titleRect = title.get_rect()
titleRect.center = ((width / 2), 30)
screen.blit(title, titleRect)
# Check for AI move
if user != player and not game_over:
if ai_turn:
time.sleep(0.5)
move = ttt.minimax(board)
board = ttt.result(board, move)
ai_turn = False
else:
ai_turn = True
# Check for a user move
click, _, _ = pygame.mouse.get_pressed()
if click == 1 and user == player and not game_over:
mouse = pygame.mouse.get_pos()
for i in range(3):
for j in range(3):
if (board[i][j] == ttt.EMPTY and tiles[i][j].collidepoint(mouse)):
board = ttt.result(board, [i, j]) # (i, j)->[i, j]
if game_over:
againButton = pygame.Rect(width / 3, height - 65, width / 3, 50)
again = mediumFont.render("Play Again", True, black)
againRect = again.get_rect()
againRect.center = againButton.center
pygame.draw.rect(screen, white, againButton)
screen.blit(again, againRect)
click, _, _ = pygame.mouse.get_pressed()
if click == 1:
mouse = pygame.mouse.get_pos()
if againButton.collidepoint(mouse):
time.sleep(0.2)
user = None
board = ttt.initial_state()
ai_turn = False
pygame.display.flip()
Download the Source Code
You can download the zip file of this project from my GitHub page through the download button.
Important Note
This project is a solution to project0 CS50's Artificial Intelligence. I have developed the program in the tictactoe.py file on my own. It was a task that was given at lecture0, Search problem. This course is totally free and everyone can take this course from there.
☛Visit Also: Apple Catcher Game in Python - PyGame Project
Summary
Indeed, the popularity of Tic Tac Toe is not the same as before. But since we are talking about Artificial Intelligence here, this simple game is also a great lesson to develop using Artificial Intelligence.
In this tutorial, we developed the Tic Tac Toe Game in Python using Artificial Intelligence. The logic of this game is built using the Minimax algorithm and it makes the computer unbeatable.
The graphics in this game is handled by the PyGame library. Play this game and try to win against the computer. Let me know in the comments section, then what happens.
Thanks for reading!💙
PySeek