I have the following variables X, Y, EMPTY, board, action
and function result(board, action)
in my tictactoe.py
file. board
is a 2D array and action
is a 2-tuple.
I pass this into my function and try to reference an index in it but some how my board
and/or action
is being interpreted as an integer and I cant figure out why that is. When I add breakpoints to the function and inspect the running of the file it works well but when I run the program as a whole, I get the following traceback:
board = ttt.result(board, move)
File "c:\Users\ACER\Desktop\Projects\artificial intelligence with python\tictactoe\tictactoe\tictactoe.py", line 66, in result
cell = new_board[action[0]][action[1]]
TypeError: 'int' object is not subscriptable
The code to the function is as follows:
X="X"
O="O"
EMPTY= None
initial_board = [[EMPTY,EMPTY,EMPTY],[EMPTY,EMPTY,EMPTY],[EMPTY,EMPTY,EMPTY]]
def result(board, action):
"""
Returns the board that results from making move (i, j) on the board.
"""
memo = {}
new_board = copy.deepcopy(board, memo)
#The line below raises the exception
cell = new_board[action[0]][action[1]]
move = player(board)
if cell is not EMPTY:
raise Exception("That move is not allowed")
else:
# cell = move
new_board[action[0]][action[1]] = move
return new_board
I am convinced the values of board
and action
I am passing to the function are indeed the correct types.
Where am I getting it wrong here? Let me know if I should add more detail to my question.
Thank you in advance for the help!
EDIT
The function is being called in a separate file runner.py
that implements the GUI for the game, I am not sure what exactly that file is doing but from what I've checked, my function is being called by the following lines of code:
# Check for AI move
if user != player and not game_over:
if ai_turn:
time.sleep(0.5)
move = ttt.minimax(board)
board = ttt.result(board, move)
ai_turn = False
else:
ai_turn = True
# Check for a user move
click, _, _ = pygame.mouse.get_pressed()
if click == 1 and user == player and not game_over:
mouse = pygame.mouse.get_pos()
for i in range(3):
for j in range(3):
if (board[i][j] == ttt.EMPTY and tiles[i][j].collidepoint(mouse)):
board = ttt.result(board, (i, j))
I hope that helps
FURTHER EDITS Below is the code for the functions that allow the AI to look for the optimal move:
def max_value(board):
"""
Given the AI is "X"(Maximizer), returns the value of the utility if the
terminal state has been reached,
otherwise return the value of the utility that will result
in optimal play.
"""
if terminal(board):
return utility(board)
else:
v = -(math.inf)
for action in actions(board):
v = max(v, min_value(result(board, action)))
return v
def min_value(board):
"""
Given the AI is "O"(Minimizer), returns the value of the utility if the
terminal state has been reached,
otherwise return the value of the utility that will result
in optimal play.
"""
if terminal(board):
return utility(board)
else:
v = math.inf
for action in actions(board):
v = min(v, max_value(result(board, action)))
return v
def minimax(board):
"""
Returns the optimal action for the current player on the board.
"""
if player(board) == X:
return max_value(board)
elif player(board) == O:
return min_value(board)
player(board)
Returns player who has the next turn on a board. In terms of X and O.
terminal(board
Returns True if game is over, False otherwise.
utility(board)
returns 1 if X has won the game, -1 if O has won, 0 otherwise.
max_value
and min_value
are alternatively being called as part of the recursion.
N.BI have tested the player, terminal and utility function and they are working fine