Just Made a Tic-Tac-Toe Game in Python (How You Can Too)

It's easy to think that making a tic-tac-toe game using Python is complicated, time-consuming, and just for really advanced developers. In reality it isn't, and in just a few steps, I'll show you how to make a simple tic-tac-toe game. Step 1: Create the Layout We'll start by creating a Python file. Afterwards, we'll import Tkinter (module for creating GUIs in Python) then define things such as fonts, the GUI's size and background, and so on: from tkinter import * from tkinter import font root = Tk() root.title("Python-Tic-Tac-Toe Game") root.configure(bg='lightblue') # Sets GUI background to lightblue root.geometry("300x400") # Defines size of GUI root.resizable(False, False) # Says GUI can't be resized f = font.Font(family='Courier', size=24) o = font.Font(family='Courier', size=18) Great! Now, wouldn't it be good to have a welcome page w/ a 'Yes' or 'No' button? I bet you said yes (and if you didn't, go back to Ohio!). To do that, simply type/copy and paste: l = Label(root, text="Python Tic\nTac Toe Game", font=f, bg='lightblue') l.place(x=30, y=30) b = Label(root, text="Wish to continue?", font=o, bg='lightblue') b.place(x=30, y=150) c = Button(root, text="Yes", font=o, width=10, height=1, bg='white') c.place(x=70, y=220) d = Button(root, text="No", font=o, width=10, height=1, bg='white') d.place(x=70, y=290) Our program should now look something like this: Our program looks great! But if you notice, the buttons won't do anything. To solve that, underneath d.place(), type/copy and paste: d.config(command=da) and underneath c.place(), type/copy and paste: c.config(command=ca) After that, create and fill in functions da and ca, like so: def da(): root.destroy() #This deletes the GUI when someone clicks the button 'No' def ca(): l.destroy() b.destroy() d.destroy() c.destroy() global vas global av global ab vas = Label(root, text="Which one would\nyou like to use?", font=o, bg='lightblue') vas.place(x=30, y=30) av = Button(root, text="X", font=o, bg='white', width=5, height=2) av.place(x=30, y=150) ab = Button(root, text="O", font=o, bg='white', width=5, height=2) ab.place(x=170, y=150) This will delete the contents from the welcome page and replace them with new text (that being 'Which one would you like to use?') and new buttons (that being X or O). Now, once we click 'Yes', or GUI should like this: Step 2: Create the Tic-Tac-Toe board We've now created a welcome page, and given the user the ability to choose either X or O. But sadly, the buttons don't do anything, and we still haven't made the actual tic-tac-toe board. To fix that, underneath av.place() type/copy and paste: av.config(command=x) and underneath ab.place() type/copy and paste: ab.config(command=ask) Once finished, create and fill in functions x and ask, like so: def x(): global vas, av, ab, ba, bb, bc, bd, be, bf, bg, bh, bi, label, i, buttons vas.destroy() av.destroy() ab.destroy() # Row 1 ba = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(ba, buttons)) ba.place(x=30, y=70) bb = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bb, buttons)) bb.place(x=110, y=70) bc = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bc, buttons)) bc.place(x=190, y=70) # Row 2 bd = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bd, buttons)) bd.place(x=30, y=145) be = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(be, buttons)) be.place(x=110, y=145) bf = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bf, buttons)) bf.place(x=190, y=145) # Row 3 bg = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bg, buttons)) bg.place(x=30, y=220) bh = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bh, buttons)) bh.place(x=110, y=220) bi = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bi, buttons)) bi.place(x=190, y=220) label = Label(root, text=f'TIMER:\nO:15', font=o, bg='lightblue') label.place(x=100, y=10) buttons = [ba, bb, bc, bd, be, bf, bg, bh, bi] start_time = Time.time() for i in range(15, -1, -1): label.config(text=f'TIMER:\nO:{i}') root.update() #force the label to update. Time.sleep(0.8) #add a one second delay, so the user can see the change. end = Time.time() r = end - start_time result = 12 - r if result < 1: for button in buttons: button.config(state=DISABLED) label.destroy() d = Label(root, text='No Winner: Tie!!!!', font=o, bg='l

Mar 23, 2025 - 12:10
 0
Just Made a Tic-Tac-Toe Game in Python (How You Can Too)

It's easy to think that making a tic-tac-toe game using Python is complicated, time-consuming, and just for really advanced developers. In reality it isn't, and in just a few steps, I'll show you how to make a simple tic-tac-toe game.

Step 1: Create the Layout

We'll start by creating a Python file. Afterwards, we'll import Tkinter (module for creating GUIs in Python) then define things such as fonts, the GUI's size and background, and so on:

from tkinter import *
from tkinter import font

root = Tk()
root.title("Python-Tic-Tac-Toe Game")
root.configure(bg='lightblue') # Sets GUI background to lightblue
root.geometry("300x400") # Defines size of GUI
root.resizable(False, False) # Says GUI can't be resized

f = font.Font(family='Courier', size=24) 
o = font.Font(family='Courier', size=18) 

Great! Now, wouldn't it be good to have a welcome page w/ a 'Yes' or 'No' button? I bet you said yes (and if you didn't, go back to Ohio!). To do that, simply type/copy and paste:

l = Label(root, text="Python Tic\nTac Toe Game", font=f, bg='lightblue')
l.place(x=30, y=30)

b = Label(root, text="Wish to continue?", font=o, bg='lightblue')
b.place(x=30, y=150)

c = Button(root, text="Yes", font=o, width=10, height=1, bg='white')
c.place(x=70, y=220)

d = Button(root, text="No", font=o, width=10, height=1, bg='white')
d.place(x=70, y=290)

Our program should now look something like this:

Image description

Our program looks great! But if you notice, the buttons won't do anything. To solve that, underneath d.place(), type/copy and paste:

d.config(command=da) 

and underneath c.place(), type/copy and paste:

c.config(command=ca) 

After that, create and fill in functions da and ca, like so:

def da():
    root.destroy() #This deletes the GUI when someone clicks the button 'No'

def ca():
   l.destroy()
   b.destroy()
   d.destroy()
   c.destroy()

    global vas
    global av
    global ab

    vas = Label(root, text="Which one would\nyou like to use?", font=o, bg='lightblue')
    vas.place(x=30, y=30)

    av = Button(root, text="X", font=o, bg='white', width=5, height=2)
    av.place(x=30, y=150)

    ab = Button(root, text="O", font=o, bg='white', width=5, height=2)
    ab.place(x=170, y=150)


This will delete the contents from the welcome page and replace them with new text (that being 'Which one would you like to use?') and new buttons (that being X or O). Now, once we click 'Yes', or GUI should like this:

Image description

Step 2: Create the Tic-Tac-Toe board

We've now created a welcome page, and given the user the ability to choose either X or O. But sadly, the buttons don't do anything, and we still haven't made the actual tic-tac-toe board. To fix that, underneath av.place() type/copy and paste:

av.config(command=x)

and underneath ab.place() type/copy and paste:

ab.config(command=ask)

Once finished, create and fill in functions x and ask, like so:

def x():
    global vas, av, ab, ba, bb, bc, bd, be, bf, bg, bh, bi, label, i, buttons
    vas.destroy()
    av.destroy()
    ab.destroy()



    # Row 1
    ba = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(ba, buttons))
    ba.place(x=30, y=70)


    bb = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bb, buttons))
    bb.place(x=110, y=70)


    bc = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bc, buttons))
    bc.place(x=190, y=70)

    # Row 2
    bd = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bd, buttons))
    bd.place(x=30, y=145)


    be = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(be, buttons))
    be.place(x=110, y=145)

    bf = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bf, buttons))
    bf.place(x=190, y=145)


    # Row 3
    bg = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bg, buttons))
    bg.place(x=30, y=220)

    bh = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bh, buttons))
    bh.place(x=110, y=220)

    bi = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action(bi, buttons))
    bi.place(x=190, y=220)

    label = Label(root, text=f'TIMER:\nO:15', font=o, bg='lightblue')
    label.place(x=100, y=10)

    buttons = [ba, bb, bc, bd, be, bf, bg, bh, bi]

    start_time = Time.time()

    for i in range(15, -1, -1):
        label.config(text=f'TIMER:\nO:{i}')
        root.update() #force the label to update.
        Time.sleep(0.8) #add a one second delay, so the user can see the change.

    end = Time.time()

    r = end - start_time
    result = 12 - r



    if result < 1:
        for button in buttons:
            button.config(state=DISABLED)
        label.destroy()
        d = Label(root, text='No Winner: Tie!!!!', font=o, bg='lightblue')
        d.place(x=20, y=30)


def ask():
    global vas, av, ab, b1, b2, b3, b4, b5, b6, b7, b8, b9, label, buttons1 
    vas.destroy()
    av.destroy()
    ab.destroy()



    # Row 1
    b1 = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action1(b1, buttons1))
    b1.place(x=30, y=70)


    b2 = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action1(b2, buttons1))
    b2.place(x=110, y=70)


    b3 = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action1(b3, buttons1))
    b3.place(x=190, y=70)

    # Row 2
    b4 = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action1(b4, buttons1))
    b4.place(x=30, y=145)


    b5 = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action1(b5, buttons1))
    b5.place(x=110, y=145)

    b6 = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action1(b6, buttons1))
    b6.place(x=190, y=145)


    # Row 3
    b7 = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action1(b7, buttons1))
    b7.place(x=30, y=220)

    b8 = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action1(b8, buttons1))
    b8.place(x=110, y=220)

    b9 = Button(root, font=o, bg='white', width=5, height=2, text=' ', command=lambda: u_action1(b9, buttons1))
    b9.place(x=190, y=220)


    label = Label(root, text=f'TIMER:\nO:15', font=o, bg='lightblue')
    label.place(x=100, y=10)

    buttons1 = [b1, b2, b3, b4, b5, b6, b7, b8, b9]

    start_time = Time.time()

    for i in range(15, -1, -1):
            label.config(text=f'TIMER:\nO:{i}')
            root.update() #force the label to update.
            Time.sleep(0.8) #add a one second delay, so the user can see the change.

    end = Time.time()

    r = end - start_time
    result = 12 - r


    if result < 1:
        for button in buttons1:
            button.config(state=DISABLED)
        label.destroy()
        d = Label(root, text='No Winner: Tie!!!!', font=o, bg='lightblue')
        d.place(x=20, y=30)

With these functions, you should have the tic-tac-toe board as well as a 15 second timer:

Image description

(P.S: If the timer isn't what you wish it to be, you can always change it in text=f'TIMER:\nO:15' and for i in range(15, -1, -1) )

Again, we're faced with the issue of the buttons not doing anything. To solve that, we'll have to...

Step 3: Allow User to Play

Start by creating functions, u_action and u_action1, and fill them in with the code below:

def u_action(button, buttons):
    button.config(text='X')
    button.config(state=DISABLED)

def u_action1(button, buttons1):
    button.config(text='O')
    button.config(state=DISABLED)

Now, once we click a button, it becomes filled with X or O and is then disabled (as in it can't be clicked again). It's progress, but tic-tac-toe wasn't made for just one player. To fix that, we'll need to create a bot, and since I said this tutorial would be easy, simply download the Logic module (link: https://drive.google.com/file/d/1aWYqNuVPvVdRNNtXuo670ULCxJMiJ8Bq/view?usp=sharing), then import it into your file:

import Logic #make sure 'l' is uppercase to avoid errors.

Afterwards, add the line below to u_action():

Logic.b_action(root, buttons, label, o)

and add this code to u_action1():

Logic.b_action1(root, buttons1, label, o)

Congrats