Search code examples
pythontkintertic-tac-toe

Tkinter Tic Tac Toe Drawing a shape in a certain box


I'm a newbie in programming, trying to learn python and I decided to make Tic Tac Toe for my first project. I've made functions for drawing X and O, but I have trouble drawing them in the box I'm clicking. Here's the code so far:

ttt = Tk()
ttt.title("Tic Tac Toe")
w = Canvas(ttt, width = 902, height = 902)
w.configure (bg =  "white")
w.pack()

m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

def drawx(event):
    x, y = event.x, event.y
    w.create_line(49, 49, 249, 249, fill = "black")
    w.create_line(49, 249, 249, 49, fill = "black")

def drawo(event):
    x2, y2 = event.x, event.y
    x0 = x2 - 100
    y0 = y2 - 100
    x1 = x2 + 100
    y1 = y2 + 100
    return w.create_oval(x0, y0, x1, y1)

w.create_line(0, 300, 901, 300, fill = "black") 
w.create_line(0, 601, 901, 601, fill = "black")
w.create_line(300, 0, 300, 901, fill = "black")
w.create_line(601, 0, 601, 901, fill = "black")

Here, the shapes will be drawn based on my cursor coordinates, which I know should be modified. Any help or suggestion would be appreciated.


Solution

  • You need to bind the mouse click event to canvas:

    w.bind('<Button-1>', on_click)
    

    Then determine which cell is clicked in on_click handler:

    SIZE = 300
    player = 1  # 1 for O, 2 for X
    
    def on_click(event):
        global m
        global player
        row = event.y // SIZE
        col = event.x // SIZE
        # check whether the cell is not filled yet
        if m[row][col] == 0:
            # calculate the center of the cell
            cx = col * SIZE + SIZE // 2
            cy = row * SIZE + SIZE // 2
            # draw X or O based on current player
            if player == 1:
                draw_O(cx, cy)
            else:
                draw_X(cx, cy)
            # set cell is filled
            m[row][col] = player
            # now you need to check whether current player wins
            ...
            # if no one wins, toggle player
            player = 2 if player == 1 else 1
    
    def draw_O(x, y):
        radius = SIZE // 3
        w.create_oval(x-radius, y-radius, x+radius, y+radius, width=5, tag='cell')  # tag is used for resetting the game
    
    def draw_X(x, y):
        radius = SIZE // 3
        w.create_line(x-radius, y-radius, x+radius, y+radius, width=5, tag='cell')
        w.create_line(x+radius, y-radius, x-radius, y+radius, width=5, tag='cell')
    

    If you want to reset the game:

    def reset_game():
        global m
        global player
        # remove all 'O' and 'X'
        w.delete('cell')
        m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
        player = 1
    

    For better design, put all the above logic in a class (inherited from Canvas). Then you can use instance variables instead of global variables.