Creating GUI enabled Tic Tac Toe in Python

The best way of learning a programming language is by doing exciting projects. And what will be better than making our favourite Tic Tac Toe in Python game and that too with graphics? Isn’t it great! And if you are thinking it is going to be tough, you are wrong, my friend. It’s super easy to make tic tac toe in python. All you need is a basic understanding of python programming language.


For making this project with Graphical Interface (where user interaction is possible), we will use python’s Tkinter
module, which is very easy to implement. Also, we will use fundamental concepts of python like lists, if condition and functions. So, what are we waiting for? Let’s learn how to make a tic tac toe in python.

Note: This code is for python version 3 and above. The logic will be the same for version 2 but there will be syntactical changes.

Making the Graphical Interface for tic tac toe in python

tic tac toe in python

Our goal is to make an interface like above. For this purpose, in python, we have a pre-installed library called tkinter, which is used to create such graphics.
As this module comes pre-installed with python, we can directly import it.

from tkinter import *

Using * after import means we have imported all the methods, variables of the tkinter library.

root=Tk ()

Here, we have created a Tk widget and assigned it to a variable root. Tk is the parent window containing a title bar and some other decorations provided by the window manager. We have to create this widget before any other widget, and there can be only one such type of widget.

label1=Label(root,text="player1 : X",font="times 15")
label1.grid(row=0,column=1)
label2=Label(root,text="player2 : O",font="times 15")
label2.grid(row=0,column=2)

The above line of code creates a Label widget, where we can write something in the parent window. In the first parameter, we give the value of the parent window, in this case, root. As the second parameter, we give what text we have to display. In the third parameter, we have set the font type to ‘times 15’. You can change it according to your wish.

After that, we want to display this text in the 0th-row 1st column, and for that, we have used label1.grid(row=0,column=1).
As tic tac toe is a two-player game, we need two such labels. So, we have created label2 similar to label1.

Creating cells in tic tac toe

b1=Button(root,width=20,height=10,command=lambda:define_sign(1))
b1.grid(row=1,column=1)

In a tic tac toe, there are a total of 9 cells. And for every cell, we need to create a button. So that when a player clicks a button, we will know which cell. In the above code, we have used the Button method of tkinter. As the first parameter, we have given the value root as we want the button to be inside the parent window. In the second and third parameters, we have set the values of the button’s width and height. You can play with those values for better understanding. After that, we need to do something when this button gets click, so in the 4th parameter, we are giving the command to call the ‘define_sign()’ using the lambda keyword whenever this button is clicked

Don’t get confused if you don’t know what lambda keyword is. For now, think it as a way for calling define_sign() method when button1 is clicked. We have passed ‘1’ to the define_sign() method because button1 has been clicked. And don’t worry about the define_sign() method. We will understand it when we know the concept used behind the game.
After that, we will put the button in the 1st row and 1 column just as we put the Label widget.
Similiarly, we have to create 8 more buttons same as button1.

b2=Button(root,width=20,height=10,command=lambda:define_sign(2))
b2.grid(row=1,column=2)
b3=Button(root,width=20,height=10,command=lambda: define_sign(3))
b3.grid(row=1,column=3)
b4=Button(root,width=20,height=10,command=lambda: define_sign(4))
b4.grid(row=2,column=1)
b5=Button(root,width=20,height=10,command=lambda: define_sign(5))
b5.grid(row=2,column=2)
b6=Button(root,width=20,height=10,command=lambda: define_sign(6))
b6.grid(row=2,column=3)
b7=Button(root,width=20,height=10,command=lambda: define_sign(7))
b7.grid(row=3,column=1)
b8=Button(root,width=20,height=10,command=lambda: define_sign(8))
b8.grid(row=3,column=2)
b9=Button(root,width=20,height=10,command=lambda: define_sign(9))
b9.grid(row=3,column=3)
root.mainloop() #Implements the main window apllication

Voila! We have successfully created the graphical interface for our game. Wasn’t that easy? Believe me, the rest of the code is as easy as this.

How is the Winner decided in Tic Tac Toe?

In tic tac toe, there are two players playing the game. ‘X’ is the mark used by Player 1, and ‘Player2 uses O’. The Winner is decided if all the values of any of the row or column have the same mark (‘X’ or ‘O’). If the mark in either of the diagonals is identical, the Winner is decided.
So, there are eight combinations when the Winner can be decided.

Implementing the logic-

def result(boards,mark):
return ((boards[1] == boards[2] == boards [3] == mark)
or (boards[4] == boards[5] == boards [6] == mark)
or (boards[7] == boards[8] == boards [9] == mark)
or (boards[1] == boards[4] == boards [7] == mark)
or (boards[2] == boards[5] == boards [8] == mark)
or (boards[3] == boards[6] == boards [9] == mark)
or (boards[1] == boards[5] == boards [9] == mark)
or (boards[3] == boards[5] == boards [7] == mark))

Here we will create a function named result() which will return True if the mark in all the three cells is the same. In the first parameter ‘boards’ we will give a list which contains the mark on each of the cell. For example, if the player1 had clicked on button3, this list will have the value=’X’ at index 3.
The second parameter, ‘mark,’ contains the value either ’X’ or ‘O.’ This function will check if the list passed (boards) has the mark (‘X’ or ‘O’) in these cells or not. If yes, it will return True otherwise, False
The board will always contain ‘1’ at index 0 because the index of a list starts with 0, so we have to adjust to that.
For example, if the value passed in the first parameter is -[1, ‘X’, ‘O’, ‘O’, ‘X’, ‘X’, ‘O’, ‘O’, ‘X’, ‘X’] and the mark =’X’
Our function will check if these values satisfy the conditions in our function.
Here, boards[1]==boards[5]==boards[9]==’X’, so our function will return True.

Implementing the define_sign() function

Now, all that is left is the define_sign() function. Whenever a click is made on any of the buttons, this method is called (check the ‘command’ parameter of the Button method of Tkinter) containing the information of the button in its argument.

#A list containing all the cell numbers
numbers=[1,2,3,4,5,6,7,8,9]
y='X' for player1 and 'O' for player2
y=""
#x is the counter to keep counting the number of chances
x=0
"""boards is a list to store the mark with respect to the cell number"""
boards=["board"]*10
def destroys():
# destroys the window when called
root.destroy()
def define_sign(number):
    global x,y,numbers
    """ Checking which button has been clicked and checking if the button 
    has been already clicked or not to avoid over-writing """
    if number==1 and number in numbers:
        numbers.remove(number)
       
        # If the value of x is even, Person1 will play and vivee versa
        if x%2==0:
            y='X'
            boards[number]=y
        elif x%2!=0:
            y='O'
            boards[number]=y
        #Using config, we write mark the button with appropriate value. 
        b1.config(text=y)
        x=x+1
        mark=y
        # Here we are calling the result() to decide whether we have got the winner or not
        if(result(boards,mark) and mark=='X' ):
            #If Player1 is the winner show a dialog box stating the winner
            showinfo("Result","Player1 wins")
            #Call the destroy function to close the GUI
            destroys()
        elif(result(boards,mark) and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==2 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y
        elif x%2!=0:
            y='O'
            boards[number]=y
            
        b2.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark)and mark=='X' ):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark)and mark=='O' ):
            showinfo("Reuslt","Player2 wins")
            destroys()
        
    if number==3 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y

        elif x%2!=0:
            y='O'
            boards[number]=y    
        b3.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark)and mark=='X'):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark) and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==4 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y
        
        elif x%2!=0:
            y='O'
            boards[number]=y  
        b4.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark)and mark=='X'):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark) and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==5 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y
        elif x%2!=0:
            y='O'
            boards[number]=y
                       
        b5.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark)and mark=='X' ):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,"O")and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==6 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y
        elif x%2!=0:
            y='O'
            boards[number]=y

        b6.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark) and mark=='X'):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark)and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==7 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y

        elif x%2!=0:
            y='O'
            boards[number]=y

        b7.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark) and mark=='X' ):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark) and mark=='O'):
            print("Player2 wins")
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==8 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y

        elif x%2!=0:
            y='O'
            boards[number]=y
            
        b8.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark) and mark=='X'):
            print("Player1 wins")
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,"O")and mark=='O'):
            print("Player2 wins")
            showinfo("Result","Player2 wins")
            destroys()
    if number==9 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y

        elif x%2!=0:
            y='O'
            boards[number]=y
            
        b9.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark) and mark=='X'):
            print("Player1 wins")
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark) and mark=='O'):
            print("Player2 wins")
            showinfo("Result","Player2 wins")
            destroys()
            
    # If we have not got any winner, display the dialogbox stating the match has bee tied.
    if(x>8 and result(boards,'X')==False and result(boards,'O')==False):
        showinfo("Result","Match Tied")
        destroys()
        

Full Code for Tic Tac Toe in Python


from tkinter import *
from tkinter.messagebox import showinfo
import warnings

#Removes all the warning from the output
warnings.filterwarnings('ignore')

root=Tk()


numbers=[1,2,3,4,5,6,7,8,9] 
# y='X' for player1 and 'O' for player2
y=""
# x is the counter to keep counting the number of chances
x=0
#boards is a list to store the mark with respect to the cell number
boards=["board"]*10

def result(boards,mark):
    return ((boards[1] == boards[2] == boards [3] == mark) 
            or (boards[4] == boards[5] == boards [6] == mark) 
            or (boards[7] == boards[8] == boards [9] == mark) 
            or (boards[1] == boards[4] == boards [7] == mark) 
            or (boards[2] == boards[5] == boards [8] == mark)
            or (boards[3] == boards[6] == boards [9] == mark)
            or (boards[1] == boards[5] == boards [9] == mark) 
            or (boards[3] == boards[5] == boards [7] == mark))


def define_sign(number):
    global x,y,numbers
    """ Checking which button has been clicked and checking if the button has been already clicked or not to avoid over-writing"""
    if number==1 and number in numbers:
        numbers.remove(number)
       
        # If the value of x is even, Person1 will play and vivee versa
        if x%2==0:
            y='X'
            boards[number]=y
        elif x%2!=0:
            y='O'
            boards[number]=y
        #Using config, we write mark the button with appropriate value. 
        b1.config(text=y)
        x=x+1
        mark=y
        # Here we are calling the result() to decide whether we have got the winner or not
        if(result(boards,mark) and mark=='X' ):
            #If Player1 is the winner show a dialog box stating the winner
            showinfo("Result","Player1 wins")
            #Call the destroy function to close the GUI
            destroys()
        elif(result(boards,mark) and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==2 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y
        elif x%2!=0:
            y='O'
            boards[number]=y
            
        b2.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark)and mark=='X' ):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark)and mark=='O' ):
            showinfo("Reuslt","Player2 wins")
            destroys()
        
    if number==3 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y

        elif x%2!=0:
            y='O'
            boards[number]=y    
        b3.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark)and mark=='X'):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark) and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==4 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y
        
        elif x%2!=0:
            y='O'
            boards[number]=y  
        b4.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark)and mark=='X'):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark) and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==5 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y
        elif x%2!=0:
            y='O'
            boards[number]=y
                       
        b5.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark)and mark=='X' ):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,"O")and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==6 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y
        elif x%2!=0:
            y='O'
            boards[number]=y

        b6.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark) and mark=='X'):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark)and mark=='O'):
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==7 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y

        elif x%2!=0:
            y='O'
            boards[number]=y

        b7.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark) and mark=='X' ):
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark) and mark=='O'):
            print("Player2 wins")
            showinfo("Result","Player2 wins")
            destroys()
        
    if number==8 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y

        elif x%2!=0:
            y='O'
            boards[number]=y
            
        b8.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark) and mark=='X'):
            print("Player1 wins")
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,"O")and mark=='O'):
            print("Player2 wins")
            showinfo("Result","Player2 wins")
            destroys()
    if number==9 and number in numbers:
        numbers.remove(number)
        if x%2==0:
            y='X'
            boards[number]=y

        elif x%2!=0:
            y='O'
            boards[number]=y
            
        b9.config(text=y)
        x=x+1
        mark=y
        if(result(boards,mark) and mark=='X'):
            print("Player1 wins")
            showinfo("Result","Player1 wins")
            destroys()
        elif(result(boards,mark) and mark=='O'):
            print("Player2 wins")
            showinfo("Result","Player2 wins")
            destroys()
            
    # If we have not got any winner, display the dialogbox stating the match has bee tied.
    if(x>8 and result(boards,'X')==False and result(boards,'O')==False):
        showinfo("Result","Match Tied")
        destroys()
        


label1=Label(root,text="player1 : X",font="times 15")
label1.grid(row=0,column=1)


l2=Label(root,text="player2 : O",font="times 15")
l2.grid(row=0,column=2)


def destroys():
    # destroys the window when called
    root.destroy()


b1=Button(root,width=20,height=10,command=lambda:define_sign(1))
b1.grid(row=1,column=1)
b2=Button(root,width=20,height=10,command=lambda:define_sign(2))
b2.grid(row=1,column=2)
b3=Button(root,width=20,height=10,command=lambda: define_sign(3))
b3.grid(row=1,column=3)
b4=Button(root,width=20,height=10,command=lambda: define_sign(4))
b4.grid(row=2,column=1)
b5=Button(root,width=20,height=10,command=lambda: define_sign(5))
b5.grid(row=2,column=2)
b6=Button(root,width=20,height=10,command=lambda: define_sign(6))
b6.grid(row=2,column=3)
b7=Button(root,width=20,height=10,command=lambda: define_sign(7))
b7.grid(row=3,column=1)
b8=Button(root,width=20,height=10,command=lambda: define_sign(8))
b8.grid(row=3,column=2)
b9=Button(root,width=20,height=10,command=lambda: define_sign(9))
b9.grid(row=3,column=3)
root.mainloop()

Must Read:

Conclusion-

  • The time complexity for implementing tic tac toe in python is O(1).
  • The minimum number of turns required to win is 5.
  • If both players play optimally, the result will end in a draw.
  • You can add much exciting stuff to this project like you can ask to play again, or you can add a scoreboard displaying the number of wins of each player. You can ask the players for their names. There is so much you can do to improve your code and hence yourself. Try to run this tic tac toe in python in your local system, and we are there for you if you face any problem.

Try to run the programs on your side and let me know if you have any queries.

Happy Coding!

Leave a Reply