Search code examples
pythontic-tac-toe

Why does my check_if_tie() function not working in TicTacToe?


So I have got this Python code for my TicTacToe. Everything is working normal except when there is no winner the program must return 'Tie' and instead it just continues asking for X and O even if the board is already filled. I'm assuming the problem is in the check_if_tie() function but I can't figure it out.

# -------Global variables--------

# If game is still going
game_still_going = True


# Who won? Or tie
winner = None
# Whose turn it is
current_player = 'X'

# The board displaying function
board = [' '] * 10


def display_board():
    print(' ' + board[6] + ' | ' + board[7] + ' | ' + board[8])
    print('---+-'   '--+---  ')
    print(' ' + board[3] + ' | ' + board[4] + ' | ' + board[5])
    print('---+-'   '--+---  ')
    print(' ' + board[0] + ' | ' + board[1] + ' | ' + board[2])

# Checks if game is over


def check_if_game_over():
    check_if_tie()
    check_if_win()


# Checks if there is a winner


def check_if_win():
    global winner

    if check_row():
        winner = check_row()
    elif check_columns():
        winner = check_columns()
    elif check_diagonals():
        winner = check_columns()
    else:
        winner = None
    return


def check_row():
    global game_still_going
    row1 = board[6] == board[7] == board[8] != " "
    row2 = board[3] == board[4] == board[5] != " "
    row3 = board[0] == board[1] == board[2] != " "
    if row1 or row2 or row3:
        game_still_going = False
    if row1:
        return board[6]
    elif row2:
        return board[3]
    elif row3:
        return board[0]

    return


def check_columns():
    global game_still_going
    column1 = board[6] == board[3] == board[0] != " "
    column2 = board[7] == board[4] == board[1] != " "
    column3 = board[8] == board[5] == board[2] != " "
    if column1 or column2 or column3:
        game_still_going = False
    if column1:
        return board[6]
    elif column2:
        return board[7]
    elif column3:
        return board[8]
    return


def check_diagonals():
    global game_still_going
    diagonal1 = board[6] == board[4] == board[2] != " "
    diagonal2 = board[0] == board[4] == board[8] != " "
    if diagonal1 or diagonal2:
        game_still_going = False
    elif diagonal1:
        return board[6]
    elif diagonal2:
        return board[0]
    return


def check_if_tie():
    global game_still_going
    if ' ' not in board:
        game_still_going = False
    return


def flip_player():
    global current_player
    if current_player == 'X':
        current_player = 'O'
    elif current_player == 'O':
        current_player = 'X'
    return


# Whose turn it is to play


def handle_turn(player):
    print(player + "'s turn")
    position = int(input('Please write your position from 1 - 9: ')) - 1
    if position not in [0,1,2,3,4,5,6,7,8,9]:
        return input('Invalid position. Please write 1-9: ')
    board[position] = player
    display_board()


# Main gameplay function
def play_game():
    global winner
    # Displays initial board
    display_board()
    # Loop running the game
    while game_still_going:

        handle_turn(current_player)
        flip_player()
        check_if_game_over()

    if winner == 'X' or winner == 'O':
        print(winner + ' won.')
    elif winner:
        print('Tie')


play_game()


Solution

  • You're having quite a lot of issues in this code and most of the problem could be avoided by avoiding the use of globals.

    When you use globals you change a global state that makes it difficult to understand what's going on. You have functions that return nothing but change state that will make the game end or not.

    One simple change would be to have you check_ method return an actual boolean and use that value in the loop to check if you have a tie or a win.

    If you're not in a tie or a win, it means the game isn't finished. So you don't need to store a global value and certainly do not have to modify the state of the is a tie or a win anywhere else.

    Keep you functions as simple as possible. I often say that but think how you actually play a game.

    In a game you have 2 players and 1 board, in which you set values anywhere.

    Each turn adds a piece until the game ends in a tie or a win.

    Everytime you add a piece you can check the state of the game. If it's not finished then you can swith the current player and enter a new piece and repeat.

    None of this requires a global state and you can always pass the game board to your methods...

    In your case it would be as simple as doing this:

    def is_tie():
        return ' ' not in board
    
    def is_win():
        ... is_win logic
        return result
    
    
    while not is_tie() or not is_win():
        ... game logic
    

    To go a step further, instead of having globals you'd have to pass the game state as arugment like this:

    def is_tie(game):
        return ' ' not in game.board
    

    And to go a step further, entering a new piece in the board would return a new state. So instead of modifying the current state you'd have a main loop that looks like this:

    game = Game()
    
    while True:
        position = game.prompt_choice()
        if not game.position_available(position)
            # loop again to select a new position
            continue
    
        game = game.execute_turn(position)
    
        if game.is_done()
            break
        else:
            # if game not done switch player and move to next turn
            game = game.switch_player()
    
    # current game player is winner
    game.print_winner()
    

    The cool thing is that if you wanted to "replay" the game, you'd just have to save the state of the game just before looping again. each "game" being returned is a modified version of the previous game object so you're never modifying the actual object.