2

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.

Alecs21
  • 25
  • 3
  • 1
    Could you complete your code, in particular the binding? – tobias_k Jan 10 '20 at 09:41
  • Read about [Canvas.create_rectangle-method](http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.create_rectangle-method) and the [find_* methodes](http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.find_closest-method) – stovfl Jan 10 '20 at 09:45

3 Answers3

2

You can "discretize" the x and y values by integer-dividing and then multiplying with the width of the individual cells (and adding some offset for the center).

def drawo(event):
    x2, y2 = event.x, event.y
    x2 = x2 // 300 * 300 + 150
    y2 = y2 // 300 * 300 + 150
    ...

Same for drawx.

Alternatively, you could use different canvas elements (or buttons, labels or similar) for the different cells in the grid and get the clicked widget from the event.

tobias_k
  • 81,265
  • 12
  • 120
  • 179
1

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.

acw1668
  • 40,144
  • 5
  • 22
  • 34
  • Thanks for pretty much giving me most of the code! It's been a huge help. I'm now having a few minor problems. Firstly, I tried binding the reset to , however it's not working. For now, I'm using . Another problem would be stopping the game without closing the canvas so I could actually reset the game. I've also added a global variable k in the on_click for the tie conditions, however it tells me that k has not been defined. – Alecs21 Jan 18 '20 at 10:06
0

To add more detail to tobias_k's answer, the below code will draw an X when the right mouse button is clicked and an O when the left mouse button is clicked.

An improvement in the code could be to use a variable to define the size of the canvas and each X/O rather than hard coding 200 or 300.

from tkinter import *

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):
    print("drawx")
    x, y = event.x, event.y
    x0 = x // 300 * 300 + 50
    y0 = y // 300 * 300 + 50
    x1 = x0 + 200
    y1 = y0 + 200
    #return w.create_oval(x0, y0, x1, y1)
    w.create_line(x0,y0,x1,y1, fill = "black")
    w.create_line(x0,y0+200,x1,y1-200, fill = "black")

def drawo(event):
    print("drawo")
    x, y = event.x, event.y
    x0 = x // 300 * 300 + 50
    y0 = y // 300 * 300 + 50
    x1 = x0 + 200
    y1 = y0 + 200
    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")

w.bind('<Button-1>',drawo)
w.bind('<Button-3>',drawx)

ttt.mainloop()
scotty3785
  • 6,763
  • 1
  • 25
  • 35