List not defined in a function in Python

Question:

I am working on one of my first codes (Tic Tac Toe), and I cannot figure out why I’m getting a name error.

First I have a list (board).

Then I have a function (possible_victory_for() ) that I define. It is supposed to to something with a list (temp_board) that will be defined later, within the next function.

The next function (computer_move() ) says temp_board = board and then calls the possible_victory_for().

In my understanding, seeing as "board" seems to work fine, the temp_board = board should be enough of a definition for temp_board. But Python disagrees.

I tried to recreate the error on a simple example, like this:

board = [1, 2, 3]
temp_board = board
if temp_board[0] == 1:
  print("it works")

But this works fine and doesn’t produce the error. So maybe it’s something related to communication between functions (or simply a typo somewhere)?

As I fail to shorten the code and get the same error, I am attaching everything I made for now. Sorry for a long post.

(if you decide to run the code, go for Medium difficulty, that’s where the error happens).

# I need these functions to make everything work
# clear_output was imported by Google Colab, not sure if it's standard
# all mentions of clear_output can be deleted if needed, the code will work
# but it will show every turn made by computer/human, not just the current board
from IPython.core.display import clear_output
from random import randrange

# Sets the board at the beginning
board = [1, 2, 3, 4, 5, 6, 7, 8, 9]

# Just displays the current board
def display_board():
  print(
      "n+-------+-------+-------+",
      "n|       |       |       |",
      "n|  ",board[0],"  |  ",board[1],"  |  ",board[2],"  |",
      "n|       |       |       |",
      "n+-------+-------+-------+",
      "n|       |       |       |",
      "n|  ",board[3],"  |  ",board[4],"  |  ",board[5],"  |",
      "n|       |       |       |",
      "n+-------+-------+-------+",
      "n|       |       |       |",
      "n|  ",board[6],"  |  ",board[7],"  |  ",board[8],"  |",
      "n|       |       |       |",
      "n+-------+-------+-------+"
      )

# Asks player to define who plays first (human or computer). Impacts the order of turns.
def who_plays_first():
  global first_player
  while True:
    first_player = int(input("Choose who plays first: 1 for human, 2 for computer"))
    if first_player == 1 or first_player == 2:
      break
    print("invalid input, read the instruction")
  if first_player == 1:
    first_player = "human"
  else:
    first_player = "computer"
  return(first_player)

# Asks player to set difficulty. Impacts how computer decides on its move.
def choose_difficulty():
  global difficulty
  while True:
    difficulty = int(input("Choose difficulty: 1 = easy, 2 = medium, 3 = hard"))
    if difficulty == 1 or difficulty == 2 or difficulty == 3:
      break
    print("invalid input, read the instruction")
  if difficulty == 1:
    difficulty = "easy"
  elif difficulty == 2:
    difficulty = "medium"
  else:
    difficulty = "hard"
  return(difficulty)

# Makes a list of free fields. Used in other functions.
def make_list_of_free_fields():
  list_of_free_fields = []
  for field in range(1,10):
    if field in board:
      list_of_free_fields.append(field)
  return(list_of_free_fields)

# Checks whether the player (defined by the sign) won.
def victory_for(sign):
  if (board[0] == sign and board[1] == sign and board[2] == sign or 
      board[3] == sign and board[4] == sign and board[5] == sign or 
      board[6] == sign and board[7] == sign and board[8] == sign or 
      board[0] == sign and board[3] == sign and board[6] == sign or 
      board[1] == sign and board[4] == sign and board[7] == sign or 
      board[2] == sign and board[5] == sign and board[8] == sign or 
      board[0] == sign and board[4] == sign and board[8] == sign or 
      board[2] == sign and board[4] == sign and board[6] == sign):
    return True
  else:
    return False

# Same as victory_for, but only used to help computer make its move on medium/hard.
def possible_victory_for(sign):
  if (temp_board[0] == sign and temp_board[1] == sign and temp_board[2] == sign or 
      temp_board[3] == sign and temp_board[4] == sign and temp_board[5] == sign or 
      temp_board[6] == sign and temp_board[7] == sign and temp_board[8] == sign or 
      temp_board[0] == sign and temp_board[3] == sign and temp_board[6] == sign or 
      temp_board[1] == sign and temp_board[4] == sign and temp_board[7] == sign or 
      temp_board[2] == sign and temp_board[5] == sign and temp_board[8] == sign or 
      temp_board[0] == sign and temp_board[4] == sign and temp_board[8] == sign or 
      temp_board[2] == sign and temp_board[4] == sign and temp_board[6] == sign):
    return True
  else:
    return False

# Asks the human player to make their move.
def human_move():
  while True:
    human_input = int(input("Choose a field"))
    if human_input in make_list_of_free_fields():
      break
    print("You must choose one of the free fields on the board by typing 1-9")
  board[human_input-1] = "O"

# This is how the computer makes its move.
# Depends on the difficulty.
# Easy is completely random.
# Medium checks whether:
# a) there's a (list of) move(s) that could guarantee computer's victory
# b) there's a (list of) move(s) that could guarantee human's victory
# - then play a random move out of that list (computer victory has priority)
# Hard is yet to be defined.
def computer_move():

  if difficulty == "easy":
    while True:
      computer_input = randrange(10)
      if computer_input in make_list_of_free_fields():
        break
    board[computer_input-1] = "X"
  
  # elif difficulty == "medium":
  else:
    brings_computer_victory = []
    brings_human_victory = []
    for field in make_list_of_free_fields():
      temp_board = board
      temp_move = field
      temp_board[temp_move-1] = "X"
      if possible_victory_for("X") == True:
        brings_computer_victory.append(temp_move)
    if brings_computer_victory != []:
      computer_input = randrange(1, len(brings_computer_victory) + 1)
      board[computer_input-1] = "X"
    for field in make_list_of_free_fields():
      temp_board = board
      temp_move = field
      temp_board[temp_move-1] = "O"
      if possible_victory_for("O") == True:
        brings_human_victory.append(temp_move)
    if brings_human_victory != []:
      computer_input = randrange(1, len(brings_human_victory) + 1)
      board[computer_input-1] = "X"

# This is the final piece of code that connects all the functions.
who_plays_first()
choose_difficulty()
clear_output()

if first_player == "human":

  display_board()

  while True:
    human_move()
    clear_output()
    display_board()
    if victory_for("O") == True:
      print("You won!")
      break
    if len(make_list_of_free_fields()) == 0:
      print("it's a tie")
      break
    computer_move()
    clear_output()
    display_board()
    if victory_for("X") == True:
      print("You lost!")
      break

else:

  while True:
    computer_move()
    clear_output()
    display_board()
    if victory_for("X") == True:
      print("You lost!")
      break
    if len(make_list_of_free_fields()) == 0:
      print("it's a tie")
      break
    human_move()
    clear_output()
    display_board()
    if victory_for("O") == True:
      print("You won!")
      break

Here’s the error traceback:

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-11-322daee905b6> in <module>
     28 
     29   while True:
---> 30     computer_move()
     31     clear_output()
     32     display_board()

1 frames
<ipython-input-10-6530342ed281> in computer_move()
     24       temp_move = field
     25       temp_board[temp_move-1] = "X"
---> 26       if possible_victory_for("X") == True:
     27         brings_computer_victory.append(temp_move)
     28     if brings_computer_victory != []:

<ipython-input-8-bd71fed33fb4> in possible_victory_for(sign)
      1 # Same as victory_for, but only used to help computer make its move on medium/hard.
      2 def possible_victory_for(sign):
----> 3   if (temp_board[0] == sign and temp_board[1] == sign and temp_board[2] == sign or 
      4       temp_board[3] == sign and temp_board[4] == sign and temp_board[5] == sign or
      5       temp_board[6] == sign and temp_board[7] == sign and temp_board[8] == sign or

NameError: name 'temp_board' is not defined
Asked By: orulo

||

Answers:

temp_board is local to computer_move, but you’re treating it as if it were a global. You should make it a parameter to possible_victory_for:

def possible_victory_for(sign, temp_board):
    # if (temp_board[0] ...

and then pass it from computer_move as an argument:

    if possible_victory_for("X", temp_board) == True:

In general I’d recommend passing variables to your functions as arguments rather than relying on pulling them implicitly from an outer scope; it makes the dependencies between different parts of your code more obvious (in this case possible_victory_for depends on not only sign but the current values of temp_board), which makes it easier to change and extend.

Answered By: Samwise