momo momo - 5 months ago 19
Python Question

Simple Tictactoe program

I am trying to implement a simple tic tac toe program, but having difficulty implementing the gaming. This is the code I have so far:

from Tkinter import *
click = False

class Buttons(object):

def __init__(self,master):
frame = Frame(master)
frame.pack()

self.button1= Button(frame,text=" ",height=4,width=8,
command=self.moveYes)
self.button1.grid(row=0,column=0)

self.button2= Button(frame,text=" ",height=4,width=8,
command=self.moveYes)
self.button2.grid(row=0,column=1)

self.button3= Button(frame,text=" ",height=4,width=8,
command=self.moveYes)
self.button3.grid(row=0,column=2)

self.button4= Button(frame,text=" ",height=4,width=8,
command=self.moveYes)
self.button4.grid(row=1,column=0)

self.button5= Button(frame,text=" ",height=4,width=8,
command=self.moveYes)
self.button5.grid(row=1,column=1)

self.button6= Button(frame,text=" ",height=4,width=8,
command=self.moveYes)
self.button6.grid(row=1,column=2)

self.button7= Button(frame,text=" ",height=4,width=8,
command=self.moveYes)
self.button7.grid(row=2,column=0)

self.button8= Button(frame,text=" ",height=4,width=8,
command=self.moveYes)
self.button8.grid(row=2,column=1)

self.button9= Button(frame,text=" ",height=4,width=8,
command=self.moveYes)
self.button9.grid(row=2,column=2)

def moveYes(self):
global click
global buttons
if buttons["text"] == " " and click == False:
buttons["text"]="X"
click= True
elif buttons["text"] == " " and click == True:
buttons["text"]="O"

root = Tk()
b=Buttons(root)

root.mainloop()


When I run this, it says:

global name 'buttons' is not defined


I guess my second method isn't correct. I don't understand how to access the buttons and compare values so that I may change the text.

Should I, for now, start with a double player format and later on move to AI?

Answer

First of all, whenever you find yourself repeating almost the same line or lines of code, it's a clue that you need to write some kind of loop.

Secondly, you can avoid using a global variable by passing the data as an argument to your command function — which will make the error you're getting go away.

In this case it's just the button that was clicked, and can be done by creating a short lamdba function with the button as a default argument. This has to be after the Tkinter Button is created, and therefore requires doing a separate config() call after its creation. Passing extra data like this is sometimes called the extra arguments trick and can be extended to include other items of interest, like the row and column of the button (or even the whole list of them).

from Tkinter import *

class Buttons(object):
    def __init__(self, master):
        frame = Frame(master)
        frame.pack()

        self.buttons = []
        for row in xrange(3):
            for col in xrange(3):
                button = Button(frame, text=" ", height=4, width=8)
                button.config(command=lambda b=button: self.clicked(b))
                button.grid(row=row, column=col)
                self.buttons.append(button)

    def clicked(self, button):
        if button["text"] == " ":
            button["text"] = "X"
        else:
            button["text"] = "O"

root = Tk()
b = Buttons(root)
root.mainloop()

Now that the basic graphical user interface is running and you have a function being called whenever the user clicks a grid position, you can start implementing the "gaming". This could be done by adding code to the clicked() method that checks to see what positions are filled with what character ("X" or "O") and responding accordingly.

I think it would be simplest to start by implementing an "AI" version first, because doing that would require the least amount of effort.