I’ve been trying to create a tictactoe game that has a GUI using tKinter and utilizes a minimax function to simulate a computer playing against the player. The player should be X and the computer should be O. I’ve implemented a minimax function and a best move function to use the score from the minimax function to call the button where the highest score lies click command, but it doesn’t work for several reasons.
I’ve tried changing where certain variables are created to not have to run into the issue of variables not being accessible, and I’ve looked at various other implementations and tried to mimic their approaches, but I’ve had no success.
The base game however still works as intended, when a player wins, the buttons turn red and a messagebox appears exclaiming their victory.
import numpy as np
import tkinter as tk
from tkinter import messagebox
from tkinter import *
root = Tk()
root.title("ARISE TIC-TAC-TOE")
#Building buttons
b1 = Button(root, text = " ", font = ("Helvetica", 20), height = 3, width = 6, bg ="SystemButtonFace", command = lambda: b_click(b1) )
b2 = Button(root, text = " ", font = ("Helvetica", 20), height = 3, width = 6, bg ="SystemButtonFace", command = lambda: b_click(b2) )
b3 = Button(root, text = " ", font = ("Helvetica", 20), height = 3, width = 6, bg ="SystemButtonFace", command = lambda: b_click(b3) )
b4 = Button(root, text = " ", font = ("Helvetica", 20), height = 3, width = 6, bg ="SystemButtonFace", command = lambda: b_click(b4) )
b5 = Button(root, text = " ", font = ("Helvetica", 20), height = 3, width = 6, bg ="SystemButtonFace", command = lambda: b_click(b5) )
b6 = Button(root, text = " ", font = ("Helvetica", 20), height = 3, width = 6, bg ="SystemButtonFace", command = lambda: b_click(b6) )
b7 = Button(root, text = " ", font = ("Helvetica", 20), height = 3, width = 6, bg ="SystemButtonFace", command = lambda: b_click(b7) )
b8 = Button(root, text = " ", font = ("Helvetica", 20), height = 3, width = 6, bg ="SystemButtonFace", command = lambda: b_click(b8) )
b9 = Button(root, text = " ", font = ("Helvetica", 20), height = 3, width = 6, bg ="SystemButtonFace", command = lambda: b_click(b9) )
gameboard = np.array([[b1, b2, b3],
[b4, b5, b6],
[b7, b8, b9]])
temp_gameboard = np.array([[b1["text"], b2["text"], b3["text"]],
[b4["text"], b5["text"], b6["text"]],
[b7["text"], b8["text"], b9["text"]]])
clicked = True
global depth
b_move = (-1, -1)
depth = 0
count = 0
score = 0
#Disable all buttons
def disable_all():
b1.config(state= DISABLED)
b2.config(state= DISABLED)
b3.config(state= DISABLED)
b4.config(state= DISABLED)
b5.config(state= DISABLED)
b6.config(state= DISABLED)
b7.config(state= DISABLED)
b8.config(state= DISABLED)
b9.config(state= DISABLED)
#Win Checker
def checkifwon():
global winner
winner = False
if b1["text"] == "X" and b2["text"] == "X" and b3["text"] == "X":
b1.config(bg = "red")
b2.config(bg = "red")
b3.config(bg = "red")
winner = True
score = 1
messagebox.showinfo("PLAYER WINS!")
disable_all()
return True
elif b4["text"] == "X" and b5["text"] == "X" and b6["text"] == "X":
b4.config(bg = "red")
b5.config(bg = "red")
b6.config(bg = "red")
winner = True
score = 1
messagebox.showinfo("PLAYER WINS!")
disable_all()
return True
elif b7["text"] == "X" and b8["text"] == "X" and b9["text"] == "X":
b7.config(bg = "red")
b8.config(bg = "red")
b9.config(bg = "red")
winner = True
score = 1
messagebox.showinfo("PLAYER WINS!")
disable_all()
return True
elif b1["text"] == "X" and b4["text"] == "X" and b7["text"] == "X":
b1.config(bg = "red")
b4.config(bg = "red")
b7.config(bg = "red")
winner = True
score = 1
messagebox.showinfo("PLAYER WINS!")
disable_all()
return True
elif b2["text"] == "X" and b5["text"] == "X" and b8["text"] == "X":
b2.config(bg = "red")
b5.config(bg = "red")
b8.config(bg = "red")
winner = True
score = 1
messagebox.showinfo("PLAYER WINS!")
disable_all()
return True
elif b3["text"] == "X" and b6["text"] == "X" and b9["text"] == "X":
b3.config(bg = "red")
b6.config(bg = "red")
b9.config(bg = "red")
winner = True
score = 1
messagebox.showinfo("PLAYER WINS!")
disable_all()
return True
elif b1["text"] == "X" and b5["text"] == "X" and b9["text"] == "X":
b1.config(bg = "red")
b5.config(bg = "red")
b9.config(bg = "red")
winner = True
score = 1
messagebox.showinfo("PLAYER WINS!")
disable_all()
return True
elif b3["text"] == "X" and b5["text"] == "X" and b7["text"] == "X":
b3.config(bg = "red")
b5.config(bg = "red")
b7.config(bg = "red")
winner = True
score = 1
messagebox.showinfo("PLAYER WINS!")
disable_all()
return True
## Check if O has won
if b1["text"] == "O" and b2["text"] == "O" and b3["text"] == "O":
b1.config(bg = "red")
b2.config(bg = "red")
b3.config(bg = "red")
winner = True
score = -1
messagebox.showinfo("O WINS", "CONGRATULATIONS")
disable_all()
return False
elif b4["text"] == "O" and b5["text"] == "O" and b6["text"] == "O":
b4.config(bg = "red")
b5.config(bg = "red")
b6.config(bg = "red")
winner = True
score = -1
messagebox.showinfo("O WINS", "CONGRATULATIONS")
disable_all()
return False
elif b7["text"] == "O" and b8["text"] == "O" and b9["text"] == "O":
b7.config(bg = "red")
b8.config(bg = "red")
b9.config(bg = "red")
winner = True
score = -1
messagebox.showinfo("O WINS", "CONGRATULATIONS")
disable_all()
return False
elif b1["text"] == "O" and b4["text"] == "O" and b7["text"] == "O":
b1.config(bg = "red")
b4.config(bg = "red")
b7.config(bg = "red")
winner = True
score = -1
messagebox.showinfo("O WINS", "CONGRATULATIONS")
disable_all()
return False
elif b2["text"] == "O" and b5["text"] == "O" and b8["text"] == "O":
b2.config(bg = "red")
b5.config(bg = "red")
b8.config(bg = "red")
winner = True
score = -1
messagebox.showinfo("O WINS", "CONGRATULATIONS")
disable_all()
return False
elif b3["text"] == "O" and b6["text"] == "O" and b9["text"] == "O":
b3.config(bg = "red")
b6.config(bg = "red")
b9.config(bg = "red")
winner = True
score = -1
messagebox.showinfo("O WINS", "CONGRATULATIONS")
disable_all()
return False
elif b1["text"] == "O" and b5["text"] == "O" and b9["text"] == "O":
b1.config(bg = "red")
b5.config(bg = "red")
b9.config(bg = "red")
winner = True
score = -1
messagebox.showinfo("O WINS", "CONGRATULATIONS")
disable_all()
return False
elif b3["text"] == "O" and b5["text"] == "O" and b7["text"] == "O":
b3.config(bg = "red")
b5.config(bg = "red")
b7.config(bg = "red")
winner = True
score = -1
messagebox.showinfo("O WINS", "CONGRATULATIONS")
disable_all()
return False
score = 0
#Saving all 8 combinations for winning a game
# Computer logic
# 1. Simulate a b click
# 2. It has to store it's last move
# 3. It has to know to only go once
# 4. It has to use minimax for the logic behind it's move
def b_click(b):
global clicked, count
if b["text"] == " " and clicked == True :
b["text"] = "X"
clicked = False
count+= 1
checkifwon()
bestMove()
#minimax(temp_gameboard, 0, True)
elif b["text"] == " " and clicked == False :
b["text"] = "O"
clicked = True
count+=1
checkifwon()
#minimax(temp_gameboard, 0, True)
else :
raise messagebox.showerror("That square has already been selected!")
return 0
# Computer defines depth, the amount of turns left in a game
def score():
if checkifwon() == True:
output = 10 - depth
return output
elif checkifwon() == False:
output = depth - 10
return output
else:
return 0
def minimax(t_gameboard, depth, is_max):
if (checkifwon() == True or checkifwon() == False):
return score()
possible_gameboard = gameboard
if is_max:
best_score = -float('inf')
for i in range(3):
for j in range(3):
if possible_gameboard[i][j] == '':
possible_gameboard[i][j] = 'O'
score = minimax(possible_gameboard, depth + 1, False)
possible_gameboard[i][j] = ''
best_score = max(score, best_score)
return best_score
else:
best_score = float('inf')
for i in range(3):
for j in range(3):
if possible_gameboard[i][j] == '':
possible_gameboard[i][j] = 'X'
score = minimax(possible_gameboard, depth + 1, True)
possible_gameboard[i][j] = ''
best_score = min(score, best_score)
return best_score
def bestMove(temp_gameboard):
b_move = (-1, -1)
best_Val = float('inf')
for i in range(3):
for j in range(3):
if(temp_gameboard[i][j] == " "):
temp_gameboard[i][j] = "O"
if(moveVal > best_Val):
b_move = (i, j)
best_Val = moveVal
moveVal = minimax(temp_gameboard, 0, False)
temp_gameboard[i][j] = " "
varX = b_move[0]
varY = b_move[1]
print(gameboard[varX][varY])
# Recursive Minimax function
b1.grid(row = 0, column = 0)
b2.grid(row = 0, column = 1)
b3.grid(row = 0, column = 2)
b4.grid(row = 1, column = 0)
b5.grid(row = 1, column = 1)
b6.grid(row = 1, column = 2)
b7.grid(row = 2, column = 0)
b8.grid(row = 2, column = 1)
b9.grid(row = 2, column = 2)
root.mainloop()
Sorry if some of my documentation is a bit over the place, I’ve been playing around with this project for around 48 hours total and I still can’t wrap my head around it. I’m a beginner so any help is highly appreciated.
Chinasa Nwosu is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.