这个井字游戏有什么问题 python
What's wrong with this tic tac toe python game
我最近通过 python 在线课程注册了 cs50 人工智能,第一个项目是使用 minimax 算法创建一个井字游戏,我已经尝试过了。但是当我 运行 runner.py 文件与他们网站上的 zip 文件一起提供时,它给了我一些错误,比如这个声明:
我 = 行动 [0] ,
说“'NoneType' 对象不可订阅”
你能更正代码吗,或者至少告诉我问题到底是什么
谢谢
import math
import numpy as npy
import sys
import copy
X = "X"
O = "O"
EMPTY = None
def initial_state():
"""
Returns starting state of the board.
"""
return [[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]]
def player(board):
"""
Returns player who has the next turn on a board.
"""
if board == initial_state():
return X
numpy_board = npy.array(board)
Xno = npy.count_nonzero(numpy_board = X)
Ono = npy.count_nonzero(numpy_board = O)
if Xno > Ono:
return O
elif Ono > Xno:
return X
def actions(board):
"""
Returns set of all possible actions (i, j) available on the board.
"""
Result = set()
for k in range(3):
for l in range(3):
if board[k][l] == EMPTY:
Result.add(board[k][l])
return Result
def result(board, action):
"""
Returns the board that results from making move (i, j) on the board.
"""
i = action[0]
j = action[1]
if board[i][j] != EMPTY:
raise Exception("Invalid Action")
new_player = player(board)
new_board = copy.deepcopy(board)
new_board[i][j] = new_player
return new_board
def winner(board):
"""
Returns the winner of the game, if there is one.
"""
for i in range(3):
if (board[i][0] == board[i][1] == board[i][2] and board[i][0] != EMPTY):
return board[i][0]
if (board[0][0] == board[1][1] == board[2][2] or (board[0][2] == board[1][1] == board[2][0]) and board[1][1] != EMPTY):
return board[1][1]
if (board[0][i] == board[1][i] == board[2][i] and board[0][i] != EMPTY):
return board[1][i]
else:
return None
def terminal(board):
"""
Returns True if game is over, False otherwise.
"""
if winner(board) != None:
return True;
numpy_board = npy.array(board)
empty_no = npy.count_nonzero(numpy_board == EMPTY)
if (empty_no == 0):
return True
else:
return False
def utility(board):
"""
Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
"""
win_player = winner(board)
if (win_player == X):
return 1
elif (win_player == O):
return -1
else:
return 0
def minimax(board):
"""
Returns the optimal action for the current player on the board.
"""
if terminal(board):
return None
currentPlayer = player(board)
if currentPlayer == X:
return max_value(board)[1]
else:
return min_value(board)[1]
def max_value(board):
if terminal(board):
return (utility(board), None)
value = -sys.maxsize-1
optimalAction = None
for action in actions(board):
possibleResult = min_value(result(board, action))
if possibleResult[0] > value:
value = possibleResult[0]
optimalAction = action
if value == 1:
break
return (value, optimalAction)
def min_value(board):
if terminal(board):
return (utility(board), None)
value = sys.maxsize
optimalAction = None
for action in actions(board):
possibleResult = max_value(result(board, action))
if possibleResult[0] < value:
value = possibleResult[0]
optimalAction = action
if value == -1:
break
return (value, optimalAction)
几个问题:
Xno = npy.count_nonzero(numpy_board = X)
中的语法错误。你在那里错过了一个等号。应该是==
。下一个类似语句中出现相同错误
elif Ono > Xno:
中的条件永远不会成立(想想看)。更重要的是,这种情况有可能在不进入任何一个块的情况下通过这个 if..elif
,给出 None
return 值。要么轮到 X,要么不轮到。在后一种情况下,总是轮到 O。你不应该需要第二次测试。所以将此行更正为 else:
Result.add(board[k][l])
加的不是坐标对,而是加方块的内容。这不是你想要的。你想存储坐标。所以这应该是Result.add((k, l))
。注意:不要对这样的名称使用 Pascal 大小写,而是使用驼峰大小写。
- 在函数
winner
中,for
循环将在其第一次迭代时 aways 退出。它从不执行其他迭代。在 return None
的第一次迭代中,您的了解还不够。所以删除 else: return None
: 在这种情况下,循环必须继续。注意:对角线的测试最好移到循环之外,因为重复该测试 3 次是没有意义的。它不依赖于循环变量。
如果您进行了这些更正,它应该会起作用。
一些其他备注:
- 如果您要从列表中创建一个 numpy 数组,那么为什么不从一开始就只创建一个 numpy 数组,并且只使用它而不是列表?每次在
player
和 terminal
中进行转换都会影响性能。
- 另外,先计算X的个数,然后计算O的个数需要两次迭代,而你可以一次清空单元格,然后从中扣除有多少不是空的。更快的方法是只维护一个计数器,并在移动时增加它,并在回溯时减少它。
- 上面提到的计数器可以用来快速判断当前玩家。如果走的步数是偶数,则轮到X,否则轮到O。
deepcopy
有性能成本。考虑使用相同的 list/array 而不复制它。你只需要在递归调用后添加一个"undo"操作。
- 与其重新创建一组可能的着法,不如考虑增量地维护一组:当你下这着法时从该组中删除一个动作,并在回溯时将其放回原位。这将提高性能。
不要使用这种模式:
if (empty_no == 0):
return True
else:
return False
首先,括号不是必需的,但更重要的是:当你已经有一个布尔表达式(empty_no == 0
),那么只需return吧。不要这样做 if..else
东西:
return empty_no == 0
- minimax 算法只有 returns 值 -1、0 或 1,这意味着它不支持快速获胜而不是慢速获胜。这可能会导致出人意料的举动,即不会直接获胜。要改进这一点,请考虑使用更动态的值。一个想法是更改
utility
函数,以便 X 获胜时,return 是空闲单元格的数量加 1。对于 O,它将是该值的负数。这样速赢才是王道。
我最近通过 python 在线课程注册了 cs50 人工智能,第一个项目是使用 minimax 算法创建一个井字游戏,我已经尝试过了。但是当我 运行 runner.py 文件与他们网站上的 zip 文件一起提供时,它给了我一些错误,比如这个声明: 我 = 行动 [0] , 说“'NoneType' 对象不可订阅” 你能更正代码吗,或者至少告诉我问题到底是什么 谢谢
import math
import numpy as npy
import sys
import copy
X = "X"
O = "O"
EMPTY = None
def initial_state():
"""
Returns starting state of the board.
"""
return [[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]]
def player(board):
"""
Returns player who has the next turn on a board.
"""
if board == initial_state():
return X
numpy_board = npy.array(board)
Xno = npy.count_nonzero(numpy_board = X)
Ono = npy.count_nonzero(numpy_board = O)
if Xno > Ono:
return O
elif Ono > Xno:
return X
def actions(board):
"""
Returns set of all possible actions (i, j) available on the board.
"""
Result = set()
for k in range(3):
for l in range(3):
if board[k][l] == EMPTY:
Result.add(board[k][l])
return Result
def result(board, action):
"""
Returns the board that results from making move (i, j) on the board.
"""
i = action[0]
j = action[1]
if board[i][j] != EMPTY:
raise Exception("Invalid Action")
new_player = player(board)
new_board = copy.deepcopy(board)
new_board[i][j] = new_player
return new_board
def winner(board):
"""
Returns the winner of the game, if there is one.
"""
for i in range(3):
if (board[i][0] == board[i][1] == board[i][2] and board[i][0] != EMPTY):
return board[i][0]
if (board[0][0] == board[1][1] == board[2][2] or (board[0][2] == board[1][1] == board[2][0]) and board[1][1] != EMPTY):
return board[1][1]
if (board[0][i] == board[1][i] == board[2][i] and board[0][i] != EMPTY):
return board[1][i]
else:
return None
def terminal(board):
"""
Returns True if game is over, False otherwise.
"""
if winner(board) != None:
return True;
numpy_board = npy.array(board)
empty_no = npy.count_nonzero(numpy_board == EMPTY)
if (empty_no == 0):
return True
else:
return False
def utility(board):
"""
Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
"""
win_player = winner(board)
if (win_player == X):
return 1
elif (win_player == O):
return -1
else:
return 0
def minimax(board):
"""
Returns the optimal action for the current player on the board.
"""
if terminal(board):
return None
currentPlayer = player(board)
if currentPlayer == X:
return max_value(board)[1]
else:
return min_value(board)[1]
def max_value(board):
if terminal(board):
return (utility(board), None)
value = -sys.maxsize-1
optimalAction = None
for action in actions(board):
possibleResult = min_value(result(board, action))
if possibleResult[0] > value:
value = possibleResult[0]
optimalAction = action
if value == 1:
break
return (value, optimalAction)
def min_value(board):
if terminal(board):
return (utility(board), None)
value = sys.maxsize
optimalAction = None
for action in actions(board):
possibleResult = max_value(result(board, action))
if possibleResult[0] < value:
value = possibleResult[0]
optimalAction = action
if value == -1:
break
return (value, optimalAction)
几个问题:
Xno = npy.count_nonzero(numpy_board = X)
中的语法错误。你在那里错过了一个等号。应该是==
。下一个类似语句中出现相同错误elif Ono > Xno:
中的条件永远不会成立(想想看)。更重要的是,这种情况有可能在不进入任何一个块的情况下通过这个if..elif
,给出None
return 值。要么轮到 X,要么不轮到。在后一种情况下,总是轮到 O。你不应该需要第二次测试。所以将此行更正为else:
Result.add(board[k][l])
加的不是坐标对,而是加方块的内容。这不是你想要的。你想存储坐标。所以这应该是Result.add((k, l))
。注意:不要对这样的名称使用 Pascal 大小写,而是使用驼峰大小写。- 在函数
winner
中,for
循环将在其第一次迭代时 aways 退出。它从不执行其他迭代。在 returnNone
的第一次迭代中,您的了解还不够。所以删除else: return None
: 在这种情况下,循环必须继续。注意:对角线的测试最好移到循环之外,因为重复该测试 3 次是没有意义的。它不依赖于循环变量。
如果您进行了这些更正,它应该会起作用。
一些其他备注:
- 如果您要从列表中创建一个 numpy 数组,那么为什么不从一开始就只创建一个 numpy 数组,并且只使用它而不是列表?每次在
player
和terminal
中进行转换都会影响性能。 - 另外,先计算X的个数,然后计算O的个数需要两次迭代,而你可以一次清空单元格,然后从中扣除有多少不是空的。更快的方法是只维护一个计数器,并在移动时增加它,并在回溯时减少它。
- 上面提到的计数器可以用来快速判断当前玩家。如果走的步数是偶数,则轮到X,否则轮到O。
deepcopy
有性能成本。考虑使用相同的 list/array 而不复制它。你只需要在递归调用后添加一个"undo"操作。- 与其重新创建一组可能的着法,不如考虑增量地维护一组:当你下这着法时从该组中删除一个动作,并在回溯时将其放回原位。这将提高性能。
不要使用这种模式:
if (empty_no == 0): return True else: return False
首先,括号不是必需的,但更重要的是:当你已经有一个布尔表达式(
empty_no == 0
),那么只需return吧。不要这样做if..else
东西:return empty_no == 0
- minimax 算法只有 returns 值 -1、0 或 1,这意味着它不支持快速获胜而不是慢速获胜。这可能会导致出人意料的举动,即不会直接获胜。要改进这一点,请考虑使用更动态的值。一个想法是更改
utility
函数,以便 X 获胜时,return 是空闲单元格的数量加 1。对于 O,它将是该值的负数。这样速赢才是王道。