Search code examples
pythontic-tac-toeminmax

Tic Tac Toe in Python with Minmax


As a project I was implementing MinMax Tic Tac Toe AI in Python. I further implemented Alpha - Beta Pruning and came up with this huge code.

Code:-

class TicTacToe:
    game_state = [[' ',' ',' '],
                [' ',' ',' '],
                [' ',' ',' ']]
    players = ['X','O']
    def __init__(self,player_idx=0):
        if(player_idx>1 or player_idx<0):
        raise Exception("player index can only be 0 or 1")
        self.current_player=player_idx
    def check_current_state(self):
        # Check if draw
        draw_flag = 0
        for i in range(3):
            for j in range(3):
                if self.game_state[i][j] is ' ':
                    draw_flag = 1
        if draw_flag is 0:
            return None, "Draw"
        
        # Check horizontals
        if (self.game_state[0][0] == self.game_state[0][1] and self.game_state[0][1] == self.game_state[0][2] and self.game_state[0][0] is not ' '):
            return self.game_state[0][0], "Done"
        if (self.game_state[1][0] == self.game_state[1][1] and self.game_state[1][1] == self.game_state[1][2] and self.game_state[1][0] is not ' '):
            return self.game_state[1][0], "Done"
        if (self.game_state[2][0] == self.game_state[2][1] and self.game_state[2][1] == self.game_state[2][2] and self.game_state[2][0] is not ' '):
            return self.game_state[2][0], "Done"
        
        # Check verticals
        if (self.game_state[0][0] == self.game_state[1][0] and self.game_state[1][0] == self.game_state[2][0] and self.game_state[0][0] is not ' '):
            return self.game_state[0][0], "Done"
        if (self.game_state[0][1] == self.game_state[1][1] and self.game_state[1][1] == self.game_state[2][1] and self.game_state[0][1] is not ' '):
            return self.game_state[0][1], "Done"
        if (self.game_state[0][2] == self.game_state[1][2] and self.game_state[1][2] == self.game_state[2][2] and self.game_state[0][2] is not ' '):
            return self.game_state[0][2], "Done"
        
        # Check diagonals
        if (self.game_state[0][0] == self.game_state[1][1] and self.game_state[1][1] == self.game_state[2][2] and self.game_state[0][0] is not ' '):
            return self.game_state[1][1], "Done"
        if (self.game_state[2][0] == self.game_state[1][1] and self.game_state[1][1] == self.game_state[0][2] and self.game_state[2][0] is not ' '):
            return self.game_state[1][1], "Done"
        
        return None, "Not Done"
    def evaluate(self):
        a,b=self.check_current_state()
        if(a != None and b == "Done" and a == 'X'):
        return 1
        elif(a != None and b == "Done" and a == 'O'):
        return -1
        elif(a == None and b == "Draw"):
        return 0
        else:
        return 6
    def MiniMax(self,depth,isMax):
        score=self.evaluate()
        if(score==1):
        return score;
        if(score==-1):
        return score;
        if(score==0):
        return score
        if(isMax==True):
        best=-100
        for i in range(3):
            for j in range(3):
            if(self.game_state[i][j]==' '):
                self.game_state[i][j]='X'
                best=max(best,self.MiniMax(depth+1,False))
                self.game_state[i][j]=' '
        return best
        elif(isMax==False):
        best=100
        for i in range(3):
            for j in range(3):
            if(self.game_state[i][j]==' '):
                self.game_state[i][j]='O'
                best=min(best,self.MiniMax(depth+1,True))
                self.game_state[i][j]=' '
        return best
    
    def best_move(self):
        best=-100
        r=-1
        c=-1
        for i in range(3):
        for j in range(3):
            if(self.game_state[i][j]==' '):
            self.game_state[i][j]='X'
            move = self.MiniMax(0, False)
            self.game_state[i][j]=' '
            if(move>best):
                r=i
                c=j
                best=move
        return r,c
    def play_move(self, block_num):
        if self.game_state[int((block_num-1)/3)][(block_num-1)%3] is ' ' and self.current_player==1:
            self.game_state[int((block_num-1)/3)][(block_num-1)%3] = self.players[self.current_player]
            self.current_player=1-self.current_player
        elif self.current_player==0:
            i,j=self.best_move()
            self.game_state[i][j] = self.players[self.current_player]
            self.current_player=1-self.current_player
        elif self.game_state[int((block_num-1)/3)][(block_num-1)%3] != ' ' and self.current_player==1:
            block_num = int(input("Block is not empty, ya blockhead! Choose again: "))
            self.play_move(block_num)
        
    def print_board(self):
        print('----------------')
        print('| ' + str(self.game_state[0][0]) + ' || ' + str(self.game_state[0][1]) + ' || ' + str(self.game_state[0][2]) + ' |')
        print('----------------')
        print('| ' + str(self.game_state[1][0]) + ' || ' + str(self.game_state[1][1]) + ' || ' + str(self.game_state[1][2]) + ' |')
        print('----------------')
        print('| ' + str(self.game_state[2][0]) + ' || ' + str(self.game_state[2][1]) + ' || ' + str(self.game_state[2][2]) + ' |')
        print('----------------')

    def play(self):
        current_state = "Not Done"
        print("New Game!")
        self.print_board()
        winner = None
        while current_state == "Not Done":
            if(self.current_player==1):
            block_choice = int(input(str(self.players[self.current_player]) + "s Turn! Choose where to place (1 to 9): "))
            self.play_move(block_choice)
            elif(self.current_player==0):
            self.play_move(1)
            self.print_board()
            winner, current_state = self.check_current_state()
            if winner is not None:
                print(str(winner) + " won!")

            if current_state is "Draw":
                print("Draw!")

    game=TicTacToe()
    game.play()

I don't know why but this code is giving Issues.

Please help me out.


Solution

  • You had indentation problems I have fixed it for you:

    class TicTacToe:
        game_state = [[' ',' ',' '],
                    [' ',' ',' '],
                    [' ',' ',' ']]
        players = ['X','O']
        def __init__(self,player_idx=0):
            if(player_idx>1 or player_idx<0):
                raise Exception("player index can only be 0 or 1")
            self.current_player=player_idx
        def check_current_state(self):
            # Check if draw
            draw_flag = 0
            for i in range(3):
                for j in range(3):
                    if self.game_state[i][j] is ' ':
                        draw_flag = 1
            if draw_flag is 0:
                return None, "Draw"
            
            # Check horizontals
            if (self.game_state[0][0] == self.game_state[0][1] and self.game_state[0][1] == self.game_state[0][2] and self.game_state[0][0] is not ' '):
                return self.game_state[0][0], "Done"
            if (self.game_state[1][0] == self.game_state[1][1] and self.game_state[1][1] == self.game_state[1][2] and self.game_state[1][0] is not ' '):
                return self.game_state[1][0], "Done"
            if (self.game_state[2][0] == self.game_state[2][1] and self.game_state[2][1] == self.game_state[2][2] and self.game_state[2][0] is not ' '):
                return self.game_state[2][0], "Done"
            
            # Check verticals
            if (self.game_state[0][0] == self.game_state[1][0] and self.game_state[1][0] == self.game_state[2][0] and self.game_state[0][0] is not ' '):
                return self.game_state[0][0], "Done"
            if (self.game_state[0][1] == self.game_state[1][1] and self.game_state[1][1] == self.game_state[2][1] and self.game_state[0][1] is not ' '):
                return self.game_state[0][1], "Done"
            if (self.game_state[0][2] == self.game_state[1][2] and self.game_state[1][2] == self.game_state[2][2] and self.game_state[0][2] is not ' '):
                return self.game_state[0][2], "Done"
            
            # Check diagonals
            if (self.game_state[0][0] == self.game_state[1][1] and self.game_state[1][1] == self.game_state[2][2] and self.game_state[0][0] is not ' '):
                return self.game_state[1][1], "Done"
            if (self.game_state[2][0] == self.game_state[1][1] and self.game_state[1][1] == self.game_state[0][2] and self.game_state[2][0] is not ' '):
                return self.game_state[1][1], "Done"
            
            return None, "Not Done"
        def evaluate(self):
            a,b=self.check_current_state()
            if(a != None and b == "Done" and a == 'X'):
                return 1
            elif(a != None and b == "Done" and a == 'O'):
                return -1
            elif(a == None and b == "Draw"):
                return 0
            else:
                return 6
        def MiniMax(self,depth,isMax):
            score=self.evaluate()
            if(score==1):
                return score;
            if(score==-1):
                return score;
            if(score==0):
                return score
            if(isMax==True):
                best=-100
                for i in range(3):
                    for j in range(3):
                        if(self.game_state[i][j]==' '):
                            self.game_state[i][j]='X'
                            best=max(best,self.MiniMax(depth+1,False))
                            self.game_state[i][j]=' '
                return best
            elif(isMax==False):
                best=100
                for i in range(3):
                    for j in range(3):
                        if(self.game_state[i][j]==' '):
                            self.game_state[i][j]='O'
                            best=min(best,self.MiniMax(depth+1,True))
                            self.game_state[i][j]=' '
                return best
        
        def best_move(self):
            best=-100
            r=-1
            c=-1
            for i in range(3):
                for j in range(3):
                    if(self.game_state[i][j]==' '):
                        self.game_state[i][j]='X'
                        move = self.MiniMax(0, False)
                        self.game_state[i][j]=' '
                        if(move>best):
                            r=i
                            c=j
                            best=move
            return r,c
        def play_move(self, block_num):
            if self.game_state[int((block_num-1)/3)][(block_num-1)%3] is ' ' and self.current_player==1:
                self.game_state[int((block_num-1)/3)][(block_num-1)%3] = self.players[self.current_player]
                self.current_player=1-self.current_player
            elif self.current_player==0:
                i,j=self.best_move()
                self.game_state[i][j] = self.players[self.current_player]
                self.current_player=1-self.current_player
            elif self.game_state[int((block_num-1)/3)][(block_num-1)%3] != ' ' and self.current_player==1:
                block_num = int(input("Block is not empty, ya blockhead! Choose again: "))
                self.play_move(block_num)
            
        def print_board(self):
            print('----------------')
            print('| ' + str(self.game_state[0][0]) + ' || ' + str(self.game_state[0][1]) + ' || ' + str(self.game_state[0][2]) + ' |')
            print('----------------')
            print('| ' + str(self.game_state[1][0]) + ' || ' + str(self.game_state[1][1]) + ' || ' + str(self.game_state[1][2]) + ' |')
            print('----------------')
            print('| ' + str(self.game_state[2][0]) + ' || ' + str(self.game_state[2][1]) + ' || ' + str(self.game_state[2][2]) + ' |')
            print('----------------')
    
        def play(self):
            current_state = "Not Done"
            print("New Game!")
            self.print_board()
            winner = None
            while current_state == "Not Done":
                if(self.current_player==1):
                    block_choice = int(input(str(self.players[self.current_player]) + "s Turn! Choose where to place (1 to 9): "))
                    self.play_move(block_choice)
                elif(self.current_player==0):
                    self.play_move(1)
                    self.print_board()
                winner, current_state = self.check_current_state()
                if winner is not None:
                    print(str(winner) + " won!")
    
                if current_state is "Draw":
                    print("Draw!")
    
    game=TicTacToe()
    game.play()