Search code examples
pythontkinterartificial-intelligencetic-tac-toeminimax

What is wrong in my minimax algorithm in Tic Tac Toe using Tkinter in python?


I am making a Tic Tac Toe game. In this, there are 2 modes. One is two player mode and other one is with AI. Two player mode is working all good. But AI mode is not working. The AI mode will be applied when there is a tick in the given checkbox, and one of the turns is played by AI. I have used minimax algorithm for AI execution. I am not getting what is wrong with the code as this is my first game using GUI (Tkinter). Please someone give me insights about what is wrong in it, or please correct my code. Thanks in advance.

from tkinter import *
from copy import deepcopy
import random


game = Tk()
game.title("TIC TAC TOE")
game.geometry("450x555")
#game.configure(bg = '#b3b3b3')



with_AI = Label(text = "Want to play with AI:", font = ("Helvatica", 15), fg = "#000000")
with_AI.grid(row = 0, column = 0, columnspan = 2, sticky = 'e')

yes_AI = IntVar()
Checkbutton(game, variable = yes_AI).grid(row = 0, column = 2, sticky = 'w')

textPlay = Label(text = "Start the Game!!!", font = ("Helvatica", 15), fg = "red")
textPlay.grid(row = 1, column = 0, columnspan = 3)




temp = random.choice([0, 1])
if temp == 0:
    player = 'O'
else:
    player = 'X'
stop_game = False




def minimax(states, depth, isMax, playerAI, scores):

    result = checkWinner1()
    if result != None:
        return scores[result]

    if isMax:
        bestVal = -1000
        for i in range(3):
            for j in range(3):
                if states[i][j] == 0:
                    states[i][j] = playerAI
                    moveVal = minimax(states, depth + 1, False, playerAI, scores)
                    states[i][j] = 0
                    bestVal = max(moveVal, bestVal)
        return bestVal
    else:
        bestVal = 1000
        for i in range(3):
            for j in range(3):
                if states[i][j] == 0:
                    if playerAI == 'X':
                        states[i][j] = 'O'
                    else:
                        states[i][j] = 'X'
                    moveVal = minimax(states, depth + 1, True, playerAI, scores)
                    states[i][j] = 0
                    bestVal = min(moveVal, bestVal)
        return bestVal


def bestMove(states, playerAI, scores):
    checkWinner1()

    bestVal = -1000

    for i in range(3):
        for j in range(3):
            if states[i][j] == 0:
                moveVal = playerAI
                moveVal = minimax(states, 0, False, playerAI, scores)
                states[i][j] = 0
                if moveVal > bestVal:
                    bestRow = i
                    bestColumn = j
                    bestVal = moveVal

    return bestRow, bestColumn


def callback(r, c):
    global player
    global textPlay
    global states

    if player == 'X' and states[r][c] == 0 and stop_game == False:
        board[r][c].configure(text = 'X', fg = '#f64c72')
        states[r][c] = 'X'
        player = 'O'
        textPlay.config(text = "O's turn")
        if yes_AI.get() == 1:
            scores = {'X':-1, 'O':1, 'tie':0}
            bestRow, bestColumn = bestMove(states, player, scores)
            board[bestRow, bestColumn].configure(text = 'O', fg = '#f64c72')
            states[bestRow, bestColumn] = 'O'
            textPlay.config(text = "X's turn")

    if player == 'O' and states[r][c] == 0 and stop_game == False:
        board[r][c].configure(text = 'O', fg = '#f64c72')
        states[r][c] = 'O'
        player = 'X'
        textPlay.config(text = "X's turn")
        if yes_AI.get() == 1:
            scores = {'X':1, 'O':-1, 'tie':0}
            bestRow, bestColumn = bestMove(states, player, scores)
            board[bestRow, bestColumn].configure(text = 'X', fg = '#f64c72')
            states[bestRow, bestColumn] = 'X'
            textPlay.config(text = "O's turn")

    checkWinner()



def checkWinner1():
    global stop_game
    global states

    win = None

    win_color = '#85d139'
    for i in range(3):
        if states[i][0] == states[i][1] == states[i][2] != 0:
            if states[i][0] == 'X':
                win = 'X'
            else:
                win = 'O'

    for i in range(3):
        if states[0][i] == states[1][i] == states[2][i] != 0:
            if states[0][i] == 'X':
                win = 'X'
            else:
                win = 'O'

    if states[0][0] == states[1][1] == states[2][2] != 0:
        if states[1][1] == 'X':
            win = 'X'
        else:
            win = 'O'

    if states[2][0] == states[1][1] == states[0][2] != 0:
        if states[1][1] == 'X':
            win = 'X'
        else:
            win = 'O'

    temp1 = 0
    for i in range(3):
        for j in range(3):
            if states[i][j] == 0:
                temp1 = 1
    if temp1 == 0:
        textPlay.configure(text = "It's a tie! Click on 'Reset' to play again.")
        win = 'tie'

    return win



def checkWinner():
    global stop_game
    global states
    global board

    win = None

    win_color = '#85d139'
    for i in range(3):
        if states[i][0] == states[i][1] == states[i][2] != 0:
            board[i][0].config(bg = win_color)
            board[i][1].config(bg = win_color)
            board[i][2].config(bg = win_color)
            stop_game = True
            if states[i][0] == 'X':
                textPlay.config(text = "X wins! Click on 'Reset' to play again.")
                win = 'X'
            else:
                textPlay.config(text = "O wins! Click on 'Reset' to play again.")
                win = 'O'

    for i in range(3):
        if states[0][i] == states[1][i] == states[2][i] != 0:
            board[0][i].config(bg = win_color)
            board[1][i].config(bg = win_color)
            board[2][i].config(bg = win_color)
            stop_game = True
            if states[0][i] == 'X':
                textPlay.config(text = "X wins! Click on 'Reset' to play again.")
                win = 'X'
            else:
                textPlay.config(text = "O wins! Click on 'Reset' to play again.")
                win = 'O'

    if states[0][0] == states[1][1] == states[2][2] != 0:
        board[0][0].configure(bg = win_color)
        board[1][1].configure(bg = win_color)
        board[2][2].configure(bg = win_color)
        stop_game = True
        if states[1][1] == 'X':
            textPlay.config(text = "X wins! Click on 'Reset' to play again.")
            win = 'X'
        else:
            textPlay.config(text = "O wins! Click on 'Reset' to play again.")
            win = 'O'

    if states[2][0] == states[1][1] == states[0][2] != 0:
        board[2][0].configure(bg = win_color)
        board[1][1].configure(bg = win_color)
        board[0][2].configure(bg = win_color)
        stop_game = True
        if states[1][1] == 'X':
            textPlay.config(text = "X wins! Click on 'Reset' to play again.")
            win = 'X'
        else:
            textPlay.config(text = "O wins! Click on 'Reset' to play again.")
            win = 'O'

    temp1 = 0
    for i in range(3):
        for j in range(3):
            if states[i][j] == 0:
                temp1 = 1
    if temp1 == 0:
        textPlay.configure(text = "It's a tie! Click on 'Reset' to play again.")
        win = 'tie'

    return win


f = [[0, 0, 0],
    [0, 0, 0],
    [0, 0, 0]]
board = [[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]]
states = [[0, 0, 0],
         [0, 0, 0],
         [0, 0, 0]]

def reset():
    global stop_game
    global player
    global board
    global states
    for i in range(3):
        for j in range(3):
            board[i][j].configure(text = ' ', fg = '#ffda30', bg = "#242582")
            states[i][j] = 0
    stop_game = False
    textPlay.configure(text = "Start the Game!!!")
    temp = random.choice([0, 1])
    if temp == 0:
        player = 'O'
    else:
        player = 'X'




for i in range(3):
    for j in range(3):
        f[i][j] = Frame(game, width = 150, height = 150)
        f[i][j].propagate(False)
        f[i][j].grid(row = i+2, column = j, sticky = "nsew", padx = 1, pady = 1)
        board[i][j] = Button(f[i][j], font = ("Helvatica", 70), bg = "#242582", fg = "#ffda30",
                        command = lambda    r = i, c = j: callback(r, c))
        board[i][j].pack(expand = True, fill = BOTH)


reset_game = Button(text = "Reset the game!", font = ("Helvatica", 15), bg = "#ffda30", fg = "#000000", 
               command = lambda    :reset())
reset_game.grid(row = 5, column = 0, columnspan = 2, sticky = 'nsew')


quit_game = Button(text = "Quit game!", font = ("Helvatica", 15), bg = "#ffda30", fg = "red", 
                   command = lambda    :game.destroy())
quit_game.grid(row = 5, column = 2, sticky = 'nsew')


game.resizable(False, False)
game.mainloop()

Solution

  • In callback, inside 2nd 'if condition', you should add:

    player = 'X'
    

    and in the next one as player 'O' so as to change the players.