0

I need to do a tic tac toe game, but I am stuck on how to change the value of a cell from "-" to "X" or "O",

I have been looking on internet since yesterday of how to tell the program "The user click on the cell [0][2]", I don't know how to tell the program which cell the user pressed.

This is the code that I have:

import tkinter as tk

buttons = []

window = tk.Tk()

frame_title = tk.Frame(master=window)
tk.Label(text="Tic tac toe", master=frame_title).pack()
frame_title.pack()

frame_body = tk.Frame(master=window)
frame_body.rowconfigure(0, minsize=50, weight=1)
frame_body.rowconfigure(1, minsize=50, weight=1)
frame_body.rowconfigure(2, minsize=50, weight=1)
frame_body.columnconfigure([0, 1, 2], minsize=50, weight=1)

for i in range(3):
    for j in range(3):
        new_button = tk.Button(master=frame_body, text="-", command=value_change)
        new_button.grid(row=i, column=j, sticky="nsew")


frame_body.pack()

window.mainloop()

This is what this code make:

tic tac toe

And this is the best try that I already did:

def value_change():
    turn = 0
    if turn == 0:
        new_button["text"] = "X"
        turn += 1
    else:
        new_button["text"] = "O"
        turn -= 1

But this one, if I clicked on any cell, only change the value of the cell[2][2].

If I press [0] [2] I need to change the value of this cell

I am using the module tkinter cause this is what the teacher used in the class to teach how to make a counter that increase and decrease, but that was a lot easier cause if I click on the "+" cell[0][0] or "-"[0][2] it only changed the value of the number on the cell [zero][one]

If you know where I can read an example about how to do this with tkinter, I will be very grateful.

martineau
  • 119,623
  • 25
  • 170
  • 301
Zethus
  • 27
  • 6
  • 1
    You are overwriting `new_button` on each iteration, so you're left with no way to reference any Button but the last. You need to save references to *all* of them - in a 9-element list perhaps, or a 3-element list of 3-element lists, or a dictionary keyed by (row, column) tuples, etc. – jasonharper May 25 '22 at 00:10
  • Does this answer your question? https://stackoverflow.com/questions/10865116/ – Bryan Oakley May 25 '22 at 00:38

1 Answers1

1

Here's how to do what you want. It's similar to the top rated answer to the question @Bryan Oakley mentioned in a comment, but does things in a more readable way and goes further by showing the modifications needed to your value_change() function to make it work properly.

The creation and configuration of each Button involves multiple steps.

  1. The basic Button widget is created.
  2. A function is then defined to be called whenever it's clicked. Note how the function parameter is given the default value of the new widget.
  3. Lastly the new button is configured to call the newly defined function.

Things have to be done this way because the callback function can't be written with the default argument until the Button exists, and the function can't be assigned to the Button until it exists.

Your value_change() function was changed to accept being passed the Button it is to change as an argument. In addition the variable turn has been declared global so it's not a local variable to preserve its value between calls to the function and enable it to properly keep track of who's move it is.

import tkinter as tk

buttons = []

window = tk.Tk()

frame_title = tk.Frame(master=window)
tk.Label(text="Tic tac toe", master=frame_title).pack()
frame_title.pack()

turn = 0  # Define global variable.

def value_change(btn):
    global turn  # Prevent creation of local variable and use global one.
    if turn == 0:
        btn["text"] = "X"
        turn += 1
    else:
        btn["text"] = "O"
        turn -= 1

frame_body = tk.Frame(master=window)
frame_body.rowconfigure(0, minsize=50, weight=1)
frame_body.rowconfigure(1, minsize=50, weight=1)
frame_body.rowconfigure(2, minsize=50, weight=1)
frame_body.columnconfigure([0, 1, 2], minsize=50, weight=1)
frame_body.pack()

for i in range(3):
    for j in range(3):
        new_button = tk.Button(master=frame_body, text="-")  # Create button.
        def handler(btn=new_button):  # Define function to be called when it's clicked.
            return value_change(btn)
        new_button['command'] = handler  # Associate the function with button.
        new_button.grid(row=i, column=j, sticky="nsew")

window.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301