这个井字游戏的Python代码有什么问题

我最近报名参加了cs50的Python人工智能在线课程,第一项项目是使用极小极大算法创建一个井字游戏,我已经尝试过了。但是当我运行从他们网站下载的zip文件中提供的runner.py文件时,出现了错误,例如对于这个语句:i = action[0],提示”‘NoneType’ object is not subscriptable”,你能帮我修正代码或者至少告诉我问题到底是什么吗?谢谢

import mathimport numpy as npyimport sysimport copyX = "X"O = "O"EMPTY = Nonedef 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 Xdef 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 Resultdef 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_boarddef 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 Nonedef 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 Falsedef 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 0def 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:中的条件永远不会为真(仔细想想)。此外,这个条件留下了可能不进入任何一个块的情况,返回None。要么是X的回合,要么不是。如果不是X的回合,那么总是O的回合。你不需要第二个测试。所以将这一行改为else:
  • Result.add(board[k][l])添加的不是坐标对,而是方格的内容。这不是你想要的。你想要存储坐标。所以这应该改为Result.add((k, l))。注意:不要对这样的名称使用Pascal大小写,而应使用驼峰命名法。
  • winner函数中,for循环总是会在第一次迭代时退出。它从不执行其他迭代。在第一次迭代中,你无法知道足够的信息来返回None。所以删除那个else: return None:在这种情况下,循环应该继续进行。注意:对角线的测试最好移到循环之外,因为它没有必要重复三次测试。它不依赖于循环变量。

如果进行这些修正,应该可以正常工作。

其他一些建议:

  • 如果你打算从列表中创建一个numpy数组,那么为什么不从一开始就只创建numpy数组,并只使用它而不使用列表?在playerterminal中每次进行转换会影响性能。
  • 此外,计算X的数量然后计算O的数量需要两次迭代,而你可以在一次扫描中计算空单元格的数量,并从中推断出非空单元格的数量。更快的方法是只维护一个计数器,在玩一招时增加它,在回溯时减少它。
  • 上述提到的计数器可以用来快速确定当前玩家。如果玩的回合数是偶数,那么是X的回合,否则是O的回合。
  • deepcopy有性能成本。考虑使用相同的列表/数组而不复制它。你只需要在递归调用后添加一个“撤销”操作。
  • 不要重新创建可能的移动集,也考虑增量维护一个集合:在玩一招时从集合中移除一个动作,在回溯时将其放回。这将提高性能。
  • 不要使用这种模式:

    if (empty_no == 0):   return Trueelse:   return False

    首先,括号不是必需的,但更重要的是:当你已经有一个布尔表达式(empty_no == 0)时,直接返回它。不要使用这种if..else结构:

    return empty_no == 0
  • 极小极大算法只返回-1、0或1的值,这意味着它不偏好快速胜利而不是慢速胜利。这可能导致令人惊讶的移动,直接胜利不会被执行。为了改进这一点,考虑使用更动态的值。一个想法是更改utility函数,使X获胜时返回空单元格的数量加1。对于O,它将是该值的负数。这样可以偏好快速胜利。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注