Search code examples
pythonpython-3.xturtle-graphicstic-tac-toe

Make function return boolean and implement AI in tic/tac/toe game


I have made a tic/tac/toe game but I want to create a computer player that controls "O" while the user controls "X".

For the clicked function in my code, I am trying to return a boolean but I am not sure if I am doing it right. The function returns True if the user's click operations were successful. If the user clicks on a position that is already outside of the board area, the move is invalid and the function should then return False. My code:

import turtle
import time
import random 

pieces = ["_", "_", "_", "_", "_", "_", "_", "_", "_"]
turn = "X"
def drawgame(brd):
    # draw board
    turtle.setup(600, 600)
    turtle.bgcolor("silver")
    turtle.color("white")
    turtle.hideturtle()
    turtle.speed('fastest')
    turtle.width(10)
    turtle.up()

    # Horizontal bars
    turtle.goto(-300, 100)
    turtle.down()
    turtle.forward(600)
    turtle.up()
    turtle.goto(-300, -100)
    turtle.down()
    turtle.forward(600)
    turtle.up()

    # Vertical bars
    turtle.goto(-100, 300)
    turtle.setheading(-90)
    turtle.down()
    turtle.forward(600)
    turtle.up()
    turtle.goto(100, 300)
    turtle.down()
    turtle.forward(600)
    turtle.up()
    turtle.color("blue")
    x, y = -300, 300
    for pos in pieces:
        if pos == "X":
            # Draw X
            turtle.up()
            turtle.goto(x + 20, y - 20)
            turtle.setheading(-45)
            turtle.down()
            turtle.forward(226)
            turtle.up()
            turtle.goto(x + 180, y - 20)
            turtle.setheading(-135)
            turtle.down()
            turtle.forward(226)
            turtle.up()

        elif pos == "O":
            #Draw O
            turtle.up()
            turtle.goto(x + 100, y - 180)
            turtle.setheading(0)
            turtle.down()
            turtle.circle(80)
            turtle.up()
        x += 200
        if x > 100:
            x = -300
            y -= 200


def clicked(board, x, y):
    #sig: list(str), int, int -> NoneType
    global turn, pieces
    turtle.onscreenclick(None)  # disabling handler when inside handler
    column = (x + 300) // 200
    row = (y - 300) // -200
    square = int(row * 3 + column)
    print("User clicked ", x, ",", y, " at square ", square)

    if pieces[square] == "_":
        pieces[square] = turn
        if turn == "X":
            turn = "O"
        else:
            turn = "X"
        drawgame(pieces)
    else:
        print("That square is already taken")
    turtle.onscreenclick(clicked)

def computer_AI(board):
    #sig: list(str) -> NoneType

def gameover(board):
    #sig: list(str) -> bool
    #checks gameover on board if there is a three in a row pattern or not

def handler(x, y):
    #sig: int, int -> NoneType
    if clicked(the_board, x, y):
        drawgame(the_board)
        if not gameover(pieces):
            computer_AI(pieces)
            drawgame(pieces)
            gameover(pieces)


def main():
    #Runs the game 
    turtle.tracer(0,0)
    turtle.hideturtle()
    turtle.onscreenclick(handler)
    drawgame(pieces)
    turtle.mainloop()

main()


I am trying to achieve this output: enter image description here

Any help is appreciated.


Solution

  • For my clicked function in my code, I am trying to return a bool but I am not sure if I am doing it right.

    The clicked() function is an event hander, it doesn't return anything to anyone. We have to design based on that. I've reworked your code below, combining the hander() and clicked() functions, such that it now "plays". That is, the user goes first, and is "X", and then the computer responds as player "O":

    import turtle
    import random
    
    board = ["_", "_", "_", "_", "_", "_", "_", "_", "_"]
    
    def drawgame(board):
        # draw board
        turtle.setup(600, 600)
        turtle.bgcolor("silver")
        turtle.color("white")
        turtle.hideturtle()
        turtle.speed('fastest')
        turtle.width(10)
        turtle.up()
    
        # Horizontal bars
        turtle.goto(-300, 100)
        turtle.down()
        turtle.forward(600)
        turtle.up()
        turtle.goto(-300, -100)
        turtle.down()
        turtle.forward(600)
        turtle.up()
    
        # Vertical bars
        turtle.setheading(-90)
        turtle.goto(-100, 300)
        turtle.down()
        turtle.forward(600)
        turtle.up()
        turtle.goto(100, 300)
        turtle.down()
        turtle.forward(600)
        turtle.up()
    
        turtle.color("blue")
        x, y = -300, 300
    
        for pos in board:
            if pos == "X":
                # Draw X
                turtle.up()
                turtle.goto(x + 20, y - 20)
                turtle.setheading(-45)
                turtle.down()
                turtle.forward(226)
                turtle.up()
                turtle.goto(x + 180, y - 20)
                turtle.setheading(-135)
                turtle.down()
                turtle.forward(226)
                turtle.up()
            elif pos == "O":
                # Draw O
                turtle.up()
                turtle.goto(x + 100, y - 180)
                turtle.setheading(0)
                turtle.down()
                turtle.circle(80)
                turtle.up()
    
            x += 200
            if x > 100:
                x = -300
                y -= 200
    
    def handler(x, y):
        # sig: list(str), int, int -> NoneType
    
        turtle.onscreenclick(None)  # disabling handler when inside handler
    
        column = (x + 300) // 200
        row = (y - 300) // -200
        square = int(row * 3 + column)
    
        if board[square] == "_":
            board[square] = "X"
            drawgame(board)
            if not gameover(board):
                computer_AI(board)
                drawgame(board)
    
                if not gameover(board):
                    turtle.onscreenclick(handler)  # allow player to take a turn
        else:
            print("That square is already taken!")
            turtle.onscreenclick(handler)  # allow player to retake turn
    
    def computer_AI(board): # sig: list(str) -> NoneType
    
        """ stupid robot player, just picks randomly from what's available """
    
        available = [index for index, character in enumerate(board) if character == "_"]
    
        if available:
            index = random.choice(available)
            board[index] = "O"
    
    def gameover(board):
        # sig: list(str) -> bool
        # checks game over on board if there is a three in a row pattern or not
    
        pass  # to be implemented!
    
        return False
    
    def main():
        # Runs the game
        turtle.hideturtle()
        turtle.onscreenclick(handler)
        drawgame(board)
        turtle.mainloop()
    
    main()
    

    To do: the logic to determine if the game is over, and who won, still doesn't exist, so you need to write that. Currently it just returns False to indicate the game isn't over. The computer_AI() code has no smarts at all, it simply notes all the open squares and picks one at random. You need to improve on this.