0

I've been learning python for the last couple days and in my book the challenge was to create a tic tac toe program. I think I have a general idea of how to do the game, but I ran into an issue in which insight would be helpful,

Heres the relevant part of my code

   board = []
for i in range(0,3):
    board.append([" "," "," "])   # Fill the board

def print_board():
    for i in range(0,3):
        print "| ",
        for k in range(0,3):
            print board[i][k],
        print "|\n"

print "The first player to start will be player one with X\n, the second player will be O\n"
player_one = "X"
player_two = "O"
current_player_name = "Player One"
current_player = player_one
filled_board = False
winner = False

def check_win_col(the_winner):
    for i in range(0,3):
        for j in range(0,3):
            if board[0][i] == board[j][0] and board[0][i] != " ":
                the_winner = current_player
    return the_winner




while(filled_board == False):
    get_row = int(raw_input("Please enter which row you would like to move to " + current_player))
    get_col = int(raw_input("Please enter the col you want to move to" + current_player))
    board[get_row][get_col] = current_player
    print_board()
    check_win_col()
    if current_player == player_one:
        current_player = player_two
    else:
        current_player = player_one

The Error

**UnboundLocalError: local variable 'the_winner' referenced before assignment**

At first, I did not understand why the line the_winner = current_player gave me an error, then after reading some SO questions like Unbound Local Error, I realized my issue.

On my own I thought of two solutions.

My attempt

1. Make the_winner global. This way I wouldn't have an issue with setting the winner for the winning column to the current player. The reason I don't want to do this, is because I recall people saying during my research on this error, that it is very bad practice to use the keyword global, and thus I do not really want to use it.

2. inside the function add a parameter for the_winner. But the problem with this idea, is that how I would access the_winner outside of the function itself. This would create the_winner inside the check_win_col() local scope, and I would not be able to manipulate this outside of the function, if I for some reason needed to. Plus the idea of adding a parameter to a function for checking the column winner seems odd. It seems like its one of those functions that should just be parameter-less if you will.

Is there a better solution I am missing? Sorry if this question seems trivial.

Community
  • 1
  • 1
bill
  • 378
  • 2
  • 9
  • if you have to access "the_winner" outside the function, you could just return it at the end of the function and store it when you call the function. – Tryph Sep 05 '16 at 15:17
  • 1
    Looks like you don't know the word `return`. – polku Sep 05 '16 at 15:17
  • Where else are you using the_winner? Your function is called `check_win_col` yet does two other things. Mutates the state outside of the function, and prints the winner. I would suggest making this function simply return the `current_player` who wins, and use it where you call the function – Joseph Young Sep 05 '16 at 15:17
  • I don't use `the_winner` anywhere else. I just was thinking ahead in the future, If I needed to do so. And yes I have been exposed to the word `return`, but how does this fix the current issue? (I don't mean to be rude, I genuinely don't know) – bill Sep 05 '16 at 15:19
  • Isn't the main issue here `the_winner = current_player`?, Are you guys saying to make `the_winner` a parameter of the `check_win_col` method, and then return `the_winner`? – bill Sep 05 '16 at 15:21
  • Can you edit the question to show the error you're getting? I wouldn't expect the function to raise anything (though it doesn't work right at detecting a winning position). Please give the full traceback, which will show what error you have and where it is being triggered. – Blckknght Sep 05 '16 at 15:22
  • by returning "the_winner", you just actually make its value available outside the function. You may want to read some doc about it: https://docs.python.org/2.7/tutorial/controlflow.html#defining-functions – Tryph Sep 05 '16 at 15:23
  • Oh, I see the error now. You'll only get it if the board has only spaces. If it has any value at any position it will report the current player as winning. – Blckknght Sep 05 '16 at 15:26
  • Added the full code and the error. I can struggle through the winning detection on my own, and toy with that I think I will learn more that way. I just wanted to know how I could set `the_winner` equal to the current player – bill Sep 05 '16 at 15:26
  • The problem is that it's unclear what you want check_win to do : give the winner (in case you don't need a parameter and return it) or check if the player that just played has won (in case you need a parameter and can just return a boolean). – polku Sep 05 '16 at 15:27
  • It looks like `board[0][i] != " "` was the issue, I guess this is a separate issue but may I ask why? I just wanted to add the and because, my program was reporting a win since all the spaces were equal to each other' – bill Sep 05 '16 at 15:30

2 Answers2

1

The issue with your function is not on the line you say gives an error, but rather on the line where you print or return the variable the_winner. That variable may not have been set in the loop, since the (buggy!) condition you're checking is not always be true.

There are a few options for how you can deal with that possibility. One might be to initialize the the_winner local variable in the function before starting the loop. None is often a reasonable default value for a variable that may or may not get changed.

def check_win_col():
    the_winner = None   # set a default value for the variable unconditionally
    for i in range(0,3):
        for j in range(0,3):
            if board[0][i] == board[j][0] and board[0][i] != " ":
                the_winner = current_player  # this *may* modify it, if the condition was met
    return the_winner   # this will work always, not only if the loop modified the variable

Putting the return statement in the loop (as suggested by Skirrebattie) is almost exactly the same as this, since None is the default return value for functions in Python. If the condition is never met in the code in the other answer, the function will return None after it reaches the end of its code.

A final note about the condition you're checking. It doesn't make any sense! Currently you're checking if any value in the first column matches any value in the first row and is not a space. That's not a very relevant condition for winning Tic-Tac-Toe. You probably want something different (though I'm not exactly sure what).

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • This is perfect! Thank you so much, I was banging my head on what to do! – bill Sep 05 '16 at 15:48
  • Yeah lol my condition makes no sense, I think I know what to do from here though I have some ideas :) – bill Sep 05 '16 at 15:51
0

How about just returning the winner? Something along the lines of this:

player_one = "X"
player_two = "O"
current_player_name = "Player One"
current_player = player_one
filled_board = False
winner = False

def check_win_col():
    for i in range(0,3):
        for j in range(0,3):
            if board[0][i] == board[j][0] and board[0][i] != " ":
                return current_player # Current player wins, so return it.

the_winner = check_win_col()
print the_winner
PdevG
  • 3,427
  • 15
  • 30