Tic Tac Toe Game in Python

Tic Tac Toe Game in Python

Who has not played this amazing game in our childhood days? Probably each and every person might have played this game once in their school days or later. Today, in this article on the Tic Tac Toe Game in Python, we will learn how to build this amazing game along with a detailed explanation of the source code. We used to play this game on paper. Still, this time let us build this using various amazing concepts of Python, along with its libraries that will eventually make our development work easy.

Before starting with the actual coding of the Tic Tac Toe Game in Python, let us first take a basic idea of what exactly the concept of this game is, What features we need to add to this game, and then after we will move on to the coding part. So without wasting a single second let us move on to our first section of this project on Tic Tac Toe in Python.

Main Idea of the Game

The Tic Tac Toe Game in Python will be played out as follows.

  • One user will start by putting their sign in one of the open boxes that are accessible.
  • The second user will then put their sign in one of the open boxes that are accessible.
  • The players’ objective is to completely arrange their individual signs either row-wise, column-wise, or diagonally.
  • By filling all boxes without a winning match, the game continues until a player wins it or it ends in a draw.

Basic Features to add in Tic Tac Toe in Python

Here is a list of all the features that we will add to our GUI development of Tic Tac Toe in Python. Go through them and you will get an overview of all components you need to use to make the GUI look stupendous.

  • Turn Tracker: This is basically to show whose turn is next (X or O)
  • Display Winner: A label to display who the winner of the game is.
  • Play Again Button: This button should be displayed on the screen once the game is over.

With a basic idea of what our GUI should look like, let us move on to the core section of this article which is coding.

Coding Tic Tac Toe Game in Python

Basic library import and setting up the Tkinter window

import tkinter as tk

storage = tk.Tk()
storage.geometry('800x500')
storage.title("Tic Tac Toe - CopyAssignment")

Explanation:

Here we imported our library and used the geometry() function to set the size of our window. We also used the title () function of this library to give a title to our window.

Adding labels to our window

tk.Label(storage, text="CopyAssignment", font=('ariel', 45, ), fg='red').pack()
tk.Label(storage, text="Tic-Tac-Toe", font=('Ariel', 25)).pack()
status_label = tk.Label(storage, text="X's turn", font=('Ariel', 15), bg='blue', fg='snow')
status_label.pack(fill=tk.X)

Explanation:

For the main heading that is “CopyAssignment”, we used the built-in function Lable(). A widget called Tkinter Label is used to create display boxes where text or images can be placed. The developer of this widget has complete control over the text that is displayed. Additionally, it can be used to spread the text across numerous lines and underline specific portions of the text. In such a way we added other two labels. One for the game name and the other to display whose turn it is. Widgets are packed in relation to the preceding widget using the Pack geometry manager. Tkinter essentially arranges each widget in a window one after the other.

The logic behind the play again function and adding its button

def playAgain():
    global currentChar
    currentChar = 'X'
    for point in xoPointCountr:
        point.button.configure(state=tk.NORMAL)
        point.reset()
    status_label.configure(text="X's turn")
    playAgain.pack_forget()
playAgain = tk.Button(storage, text='Play again', font=('Ariel', 15), fg='red', command=playAgain)

Explanation:

Moving on to the next section of Tic Tac Toe in Python, here in this block of code we have done the coding to add the feature of Play Again and its button. We used a function configure() to change the state of the button to NORMAL. The forget_pack() method is used to unmap any widget from the top level or screen. Two different forgetting methods exist. The methods pack() and grid() employ the functions forget _pack() and forget _grid(), respectively. In the end, we added a button that will call the playAgain() function when the user will click on it.

The main logic for the game

currentChar = "X"

play_area = tk.Frame(storage, width=500, height=300, bg='white')
xoPointCountr = []
xPointCountr = []
oPointCountr = []

class XOPoint:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.value = None
        self.button = tk.Button(play_area, text="", width=10, height=5, command=self.set)
        self.button.grid(row=x, column=y)

    def set(self):
        global currentChar
        if not self.value:
            self.button.configure(text=currentChar, bg='snow', fg='black')
            self.value = currentChar
            if currentChar == "X":
                xPointCountr.append(self)
                currentChar = "O"
                status_label.configure(text="O's turn")
            elif currentChar == "O":
                oPointCountr.append(self)
                currentChar = "X"
                status_label.configure(text="X's turn")
        check_win()

    def reset(self):
        self.button.configure(text="", bg='lightgray')
        if self.value == "X":
            xPointCountr.remove(self)
        elif self.value == "O":
            oPointCountr.remove(self)
        self.value = None

Explanation:

This block of code is basically the heart of this project. it will handle the overall working of the game. Initially, we declared some variables, and then we defined a class. We made a grid of buttons by using the grid().

  • set(): In this function, we worked on how to handle the turn of each player. First of all, we will start the game with X turn and once the X takes its turn, we changed the label on the top wherein it states the turn of another player. In order to perform the above operation, we made use of if-else and in that if block we used  status_label.configure(text=”O’s turn”). This will simply change the label on top to O’s turn. In the else block, we used this – status_label.configure(text=”X’s turn”). This will come into the picture when the O took its turn and now it’s X’s turn. So this will help in changing the label to X’s turn.
  • reset(): This function will simply remove all the widgets and labels that are currently there on the screen. This function is called in the playAgain() function to remove all the previously added labels.

Checking for the winning possibilities

The winning possibilities of X and O
The winning possibilities of X and O
for x in range(1, 4):
    for y in range(1, 4):
        xoPointCountr.append(XOPoint(x, y))

class WinningPossibility:
    def __init__(self, x1, y1, x2, y2, x3, y3):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.x3 = x3
        self.y3 = y3
    def check(self, for_chr):
        p1_satisfied = False
        p2_satisfied = False
        p3_satisfied = False
        if for_chr == 'X':
            for point in xPointCountr:
                if point.x == self.x1 and point.y == self.y1:
                    p1_satisfied = True
                elif point.x == self.x2 and point.y == self.y2:
                    p2_satisfied = True
                elif point.x == self.x3 and point.y == self.y3:
                    p3_satisfied = True
        elif for_chr == 'O':
            for point in oPointCountr:
                if point.x == self.x1 and point.y == self.y1:
                    p1_satisfied = True
                elif point.x == self.x2 and point.y == self.y2:
                    p2_satisfied = True
                elif point.x == self.x3 and point.y == self.y3:
                    p3_satisfied = True
        return all([p1_satisfied, p2_satisfied, p3_satisfied])
winning_possibilities = [
    WinningPossibility(1, 1, 1, 2, 1, 3),
    WinningPossibility(2, 1, 2, 2, 2, 3),
    WinningPossibility(3, 1, 3, 2, 3, 3),
    WinningPossibility(1, 1, 2, 1, 3, 1),
    WinningPossibility(1, 2, 2, 2, 3, 2),
    WinningPossibility(1, 3, 2, 3, 3, 3),
    WinningPossibility(1, 1, 2, 2, 3, 3),
    WinningPossibility(3, 1, 2, 2, 1, 3)
]

Explanation:

This class of WinningPossibility is responsible for checking all the possibilities for the player to win. In this, we created a __init__() function that will be called every time an object is created. First of all, we checked for ‘X’. We used a for loop and ran it through the xPointCountr list. Now we used the if-else to check whether the item in xPointCountr is equivalent to the first parameter passed in the __init__() function. In this way, we also checked for ‘O’. Once that is done, we returned all the values that are stored in p1_satisfied, p2_satisfied, and p3_satisfied.

Checking the winner

def check_win():
    for possibility in winning_possibilities:
        if possibility.check('X'):
            status_label.configure(text="Result: X won!")
            disable_game()
            return
        elif possibility.check('O'):
            status_label.configure(text="Result: O won!")
            disable_game()
            return
    if len(xPointCountr) + len(oPointCountr) == 9:
        status_label.configure(text="Result: Draw!")
        disable_game()
play_area.pack(pady=10, padx=10)

storage.mainloop()

Explanation:

Onto the last block of code in Tic Tac Toe Game in Python, check_win() is the function to determine who the actual winner of the game is. For this purpose, we used the for loop, which will run through our list of winning_possibilities. In the above section, we mentioned all the winning possibilities in this list. So this for loop will go through the various elements in this list. After that, we used the if-else block and this block will check if the pattern for X is made, O is made or a DRAW took place. Based upon that, the text on the top will change and the winner will be displayed.

In the end, in each block, we called the function disable_game(). This function will play the role of displaying the Play Again button. In the end, to run the program we used storage. mainloop() that instructs Python to launch the Tkinter event loop. When a button is clicked or a key is pressed, this method watches for the event and blocks any code that follows from running until the window where the method was called is closed. Here’s the end of our coding for Tic Tac Toe in Python.

Output:

Output for Tic Tac Toe Game in Python
Output of the game along with Play Again button

Complete Code for Tic Tac Toe game in Python

import tkinter as tk

storage = tk.Tk()
storage.geometry('800x500')
storage.title("Tic Tac Toe - CopyAssignment")

tk.Label(storage, text="CopyAssignment", font=('ariel', 45, ), fg='red').pack()
tk.Label(storage, text="Tic-Tac-Toe", font=('Ariel', 25)).pack()
status_label = tk.Label(storage, text="X's turn", font=('Ariel', 15), bg='blue', fg='snow')
status_label.pack(fill=tk.X)
def playAgain():
    global currentChar
    currentChar = 'X'
    for point in xoPointCountr:
        point.button.configure(state=tk.NORMAL)
        point.reset()
    status_label.configure(text="X's turn")
    playAgain.pack_forget()
playAgain = tk.Button(storage, text='Play again', font=('Ariel', 15), fg='red', command=playAgain)

currentChar = "X"

play_area = tk.Frame(storage, width=500, height=300, bg='white')
xoPointCountr = []
xPointCountr = []
oPointCountr = []
class XOPoint:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.value = None
        self.button = tk.Button(play_area, text="", width=10, height=5, command=self.set)
        self.button.grid(row=x, column=y)

    def set(self):
        global currentChar
        if not self.value:
            self.button.configure(text=currentChar, bg='snow', fg='black')
            self.value = currentChar
            if currentChar == "X":
                xPointCountr.append(self)
                currentChar = "O"
                status_label.configure(text="O's turn")
            elif currentChar == "O":
                oPointCountr.append(self)
                currentChar = "X"
                status_label.configure(text="X's turn")
        check_win()

    def reset(self):
        self.button.configure(text="", bg='lightgray')
        if self.value == "X":
            xPointCountr.remove(self)
        elif self.value == "O":
            oPointCountr.remove(self)
        self.value = None
for x in range(1, 4):
    for y in range(1, 4):
        xoPointCountr.append(XOPoint(x, y))
class WinningPossibility:
    def __init__(self, x1, y1, x2, y2, x3, y3):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.x3 = x3
        self.y3 = y3
    def check(self, for_chr):
        p1_satisfied = False
        p2_satisfied = False
        p3_satisfied = False
        if for_chr == 'X':
            for point in xPointCountr:
                if point.x == self.x1 and point.y == self.y1:
                    p1_satisfied = True
                elif point.x == self.x2 and point.y == self.y2:
                    p2_satisfied = True
                elif point.x == self.x3 and point.y == self.y3:
                    p3_satisfied = True
        elif for_chr == 'O':
            for point in oPointCountr:
                if point.x == self.x1 and point.y == self.y1:
                    p1_satisfied = True
                elif point.x == self.x2 and point.y == self.y2:
                    p2_satisfied = True
                elif point.x == self.x3 and point.y == self.y3:
                    p3_satisfied = True
        return all([p1_satisfied, p2_satisfied, p3_satisfied])
winning_possibilities = [
    WinningPossibility(1, 1, 1, 2, 1, 3),
    WinningPossibility(2, 1, 2, 2, 2, 3),
    WinningPossibility(3, 1, 3, 2, 3, 3),
    WinningPossibility(1, 1, 2, 1, 3, 1),
    WinningPossibility(1, 2, 2, 2, 3, 2),
    WinningPossibility(1, 3, 2, 3, 3, 3),
    WinningPossibility(1, 1, 2, 2, 3, 3),
    WinningPossibility(3, 1, 2, 2, 1, 3)
]
def disable_game():
    for point in xoPointCountr:
        point.button.configure(state=tk.DISABLED)
    playAgain.pack()
def check_win():
    for possibility in winning_possibilities:
        if possibility.check('X'):
            status_label.configure(text="Result: X won!")
            disable_game()
            return
        elif possibility.check('O'):
            status_label.configure(text="Result: O won!")
            disable_game()
            return
    if len(xPointCountr) + len(oPointCountr) == 9:
        status_label.configure(text="Result: Draw!")
        disable_game()
play_area.pack(pady=10, padx=10)

storage.mainloop()

Reference Links and Tutorials

  • Tkinter Full Video Tutorial

Endnote

This concludes our tutorial. The guide on creating a Tic Tac Toe Game in Python is now complete. We did our best to fully elucidate each concept in this lecture. We sincerely hope that you will find this essay to be a helpful tool. We at CopyAssignments are adamant that the only way to be a leader in Python is to create projects. It is now up to you to add additional challenging functions and advance this Tic Tac Toe Game in Python.

We have already used a range of libraries and implemented a number of fundamental to intermediate-level functions. We have also added a list of Python projects that you can do in order to build up a strong resume. Take a look at them and start your Python projects journey with mind-boggling projects. Keep learning, doing, and building until we return with another lesson. We thank you for visiting our website.


Also Read:

Share:
Avatar of Dhvanil V

Author: Dhvanil V

Leave a Reply

Your email address will not be published. Required fields are marked *