# Simple Tic-Tac-Toe example # CS 1803 - Fall 2010 # Copyright, Jay Summet # #The code below plays TTT and will never make a mistake. from tkinter import * #We define a TTT class here: class TTT(): #It has an object variable called "board" that remembers #who has made what moves. We use a 9 element long 1D data structure # to make calculations easier. On-Screen, it's represented with a 3x3 grid. board = [ " ", " ", " ", " ", " ", " ", " ", " ", " "] #This function will use the "lookAhead" method to pick the best #move for the computer (highest score) def findMoveLA(self,board): pm = self.generateMoves(board) ourMove = None bestScore = -10000000 for move in pm: testBoard = board[:] testBoard[move] = "O" tryScore = self.lookAhead(testBoard,"X") if (tryScore > bestScore): ourMove = move bestScore = tryScore return ourMove pass #Helper function to swap the players... def opponent(self,player): if player == "X": return "O" else: return "X" #This function tries to maximize the score when it's the #computers turn, but minimize the score when it's the players # turn (emulating a perfect player). def lookAhead(self,board, player): #Terminating condition: result = self.checkWin(board) if result == "X": return(-100) #Player won, bad! if result == "O": return(100) #Computer won, good! if self.checkDraw(board): return(0) #It's a die, so-so #Recursive Part. We have at least one move left to play pm = self.generateMoves(board) otherPlayer = self.opponent(player) #If WE are the next player, maximize our score! if player == "O": score = -100 #start low, and try to get higher for move in pm: testBoard = board[:] testBoard[move] = "O" score = max( score, self.lookAhead(testBoard,otherPlayer)) else: #Player is human, try to minimize (the comptuers) score! score = 100 #start high, try to go lower... for move in pm: testBoard = board[:] testBoard[move] = "X" score = min( score, self.lookAhead(testBoard, otherPlayer)) #Now that we have calculated the best/worst score possible for this #board, we return it. Note that we do not tell which series of moves #resulted in this score, as we assume the computer will always choose # the best move based upon the score. return(score) #Given a board, this function returns a list of the positions # that are possible moves def generateMoves(self, board): possMoves = [] for index in range( len(board)): if board[index] == " ": possMoves.append(index) return(possMoves) #This function returns TRUE if the board has no open moves and #nobody has won. (i.e. it checks for a draw...) def checkDraw(self, board): if self.checkWin(board) == None and len( list(filter(lambda x: x==" ", board)) ) == 0: return True else: return False #This function accepts a "board" which is a list of 9 elements. #Each element is a character (X) (O) or (space). #It returns X, O or None if nobody has won. def checkWin(self, board): if board[0] == board[1] and board[1] == board[2] and board[0] != " ": return(board[0]) if board[3] == board[4] and board[4] == board[5] and board[3] != " ": return(board[3]) if board[6] == board[7] and board[7] == board[8] and board[6] != " ": return(board[6]) if board[0] == board[3] and board[3] == board[6] and board[0] != " ": return(board[0]) if board[1] == board[4] and board[4] == board[7] and board[1] != " ": return(board[1]) if board[2] == board[5] and board[5] == board[8] and board[2] != " ": return(board[2]) if board[0] == board[4] and board[4] == board[8] and board[0] != " ": return(board[0]) if board[2] == board[4] and board[4] == board[6] and board[2] != " ": return(board[2]) #If none of the above are true, nobody has won! return None #This is the constructor. It draws the window with the 9 buttons. def __init__(self, tkMainWin): frame = Frame(tkMainWin) frame.pack() self.B00 = Button(frame) self.B00.bind("", self.clicked) self.B00.grid(row=0,column=0) self.B01 = Button(frame) self.B01.bind("", self.clicked) self.B01.grid(row=0, column=1) self.B02 = Button(frame) self.B02.bind("", self.clicked) self.B02.grid(row=0, column=2) self.B10 = Button(frame) self.B10.bind("", self.clicked) self.B10.grid(row=1,column=0) self.B11 = Button(frame) self.B11.bind("", self.clicked) self.B11.grid(row=1, column=1) self.B12 = Button(frame) self.B12.bind("", self.clicked) self.B12.grid(row=1, column=2) self.B20 = Button(frame) self.B20.bind("", self.clicked) self.B20.grid(row=2,column=0) self.B21 = Button(frame) self.B21.bind("", self.clicked) self.B21.grid(row=2,column=1) self.B22 = Button(frame) self.B22.bind("", self.clicked) self.B22.grid(row=2,column=2) #Set the text for each of the 9 buttons. (Initially, to all Blanks!) self.redrawBoard() #This event handler (callback) will figure out which of the 9 buttons #were clicked, and call the "userMove" method with that move position. def clicked(self, event): if event.widget == self.B00: self.userMove(0) if event.widget == self.B01 : self.userMove(1) if event.widget == self.B02 : self.userMove(2) if event.widget == self.B10 : self.userMove(3) if event.widget == self.B11: self.userMove(4) if event.widget == self.B12 : self.userMove(5) if event.widget == self.B20 : self.userMove(6) if event.widget == self.B21 : self.userMove(7) if event.widget == self.B22 : self.userMove(8) #When a button signals that the user has tried to make a move by # clicking, we check to see if that move is valid. If it is, we # need to check to see if the user has won. If they have not, we # need to make our move, and check to see if the computer has won. # We also redraw the board after each move. def userMove(self, pos): #Is this a valid move? if self.board[pos] == " ": #Record the players move... self.board[pos] = "X" #Then redraw the board! self.redrawBoard() result = self.checkWin(self.board) if result == "X": print("You won!") elif result == "O": print ("The computer won!") elif self.checkDraw(self.board) : print("It's a Draw!") else: #Make our move! self.computerMove() #TODO: Make our move smarter! #Check to see if the computer won with this move! result = self.checkWin(self.board) if result == "O": print("Computer won!") #Then redraw the board! self.redrawBoard() else: #Move is NOT valid! Don't do anything! messagebox.showinfo("Invalid Move", "I'm sorry, that move is not valid!" ) # TODO: Make our move smarter! # This method will make a move for the computer. def computerMove(self): #Method one: just pick the first valid move from an # ordered list of preferred moves. #for move in [4, 0, 2, 6, 8, 1, 3, 5, 7]: # if self.board[move] == " ": # self.board[move] = "O" # return #Method two: Try to find the move that will result #in the most "possible" wins for us, or the least "possible" # wins for the player... #ourMove = self.findBestMove(self.board) #self.board[ourMove] = "O" #Method Three: Look ahead, and anticipate that the player will # try to act in their own best interest! ourMove = self.findMoveLA(self.board) self.board[ourMove] = "O" return #This method will update the text displayed by # each of the 9 buttons to reflect the "board" # object variable. def redrawBoard(self): self.B00.config( text = self.board[0]) self.B01.config( text = self.board[1]) self.B02.config( text = self.board[2]) self.B10.config( text = self.board[3]) self.B11.config( text = self.board[4]) self.B12.config( text = self.board[5]) self.B20.config( text = self.board[6]) self.B21.config( text = self.board[7]) self.B22.config( text = self.board[8]) #This code starts up TK and creates a main window. mainWin = Tk() #This code creates an instance of the TTT object. ttt = TTT( mainWin) #This line starts the main event handling loop and sets us on our way... mainWin.mainloop()