Search code examples
pythontic-tac-toe

How to switch between players in tic tac toe in Python


The rest of my code does what I want it to do for now. I can't for the life of me figure out why I can't switch between players. The code I have makes sense to me. This is a two-part question: How can I fix this using the same sort of approach I've taken AND ALSO how would you have done it?

board_tiles = [0, 1, 2, 3, 4, 5, 6, 7, 8]
tiles_available = [0, 1, 2, 3, 4, 5, 6, 7, 8]

# create a function that draw the board
def draw_board():
    #call clear_screen()

    print(f'{board_tiles[0]} | {board_tiles[1]} | {board_tiles[2]}')
    print('------------------')
    print(f'{board_tiles[3]} | {board_tiles[4]} | {board_tiles[5]}')
    print('------------------')
    print(f'{board_tiles[6]} | {board_tiles[7]} | {board_tiles[8]}')

# create a function that prompts the user to choose a character
def player_input():
    mark = input('Would you like to be "X" or "O"? ').lower()
    if mark == 'x':
        player1 = 'x'
        player2 = 'o'
    else:
        player1 = 'o'
        player2 = 'x'
    print(f'Player1, you are {player1}.  That means Player2 is {player2}.  LETS PLAY!')

    return player1, player2

# create a function that defines a win
def winning_combo(player1, player2):
    players = [player1, player2]
    turn = 0
    player = players[turn]
    turn = (turn + 1) % len(players)
    while tiles_available:
        move = int(input('Where would you like to move? '))
        if move in tiles_available:
            tiles_available.remove(move)
            board_tiles.insert(board_tiles[move], player)
            board_tiles.remove(move)
        else:
            print('That move has already been taken.')

        return move, turn, player

# create a function to determine when a player wins
def win_combo():
    if board_tiles[0] == board_tiles[1] == board_tiles[2] or \
        board_tiles[3] == board_tiles[4] == board_tiles[5] or \
        board_tiles[6] == board_tiles[7] == board_tiles[8] or \
        board_tiles[0] == board_tiles[3] == board_tiles[6] or \
        board_tiles[1] == board_tiles[4] == board_tiles[7] or \
        board_tiles[2] == board_tiles[5] == board_tiles[8] or \
        board_tiles[0] == board_tiles[4] == board_tiles[8] or \
        board_tiles[2] == board_tiles[4] == board_tiles[6]:
        quit()
    else:
        True
# create a game function
def game():
    playing = True
    player1, player2 = player_input()
    while playing:
        draw_board()
        winning_combo(player1, player2)
        win_combo()

game()

Solution

  • There are actually multiple problems here, but let's get to the root of it:

    The winning_combo function resets turn = 0 at the start of each call. Sure, you update it to 1 with that turn = (turn + 1) % len(players), but you're never going to use the value again; you just return from the function, and then call it again, and then set turn = 0 again.

    The simplest fix here would be to use a global variable for turn, so it can be persistent across calls:

    def winning_combo(player1, player2):
        global turn
        players = [player1, player2]
        player = players[turn]
        turn = (turn + 1) % len(players)
        while tiles_available:
            # the rest of the code is the same
    
    def game():
        global turn
        turn = 0
        playing = True
        # the rest of the code is the same
    

    You may have heard that global variables are bad. Can you avoid one here? Sure. While you can't use a local variable in winning_combo, because that function keeps exiting and starting over again, you can use a local variable in game, and pass it in as a parameter to winning_combo:

    def winning_combo(player1, player2, turn):
        players = [player1, player2]
        player = players[turn]
        while tiles_available:
            move = int(input('Where would you like to move? '))
            # the rest of the code is the same
    
    def game():
        turn = 0
        playing = True
        player1, player2 = player_input()
        while playing:
            draw_board()
            winning_combo(player1, player2, turn)
            turn = (turn + 1) % 2
            win_combo()
    

    There are many other ways you could improve your design and simplify your code, and bugs to fix (e.g., what happens when the game is a tie?), but this should get you past your current bug, and hopefully give you a little insight into how local variables work.