井字游戏python游戏有什么问题

时间:2020-04-21 14:59:26

标签: python algorithm artificial-intelligence minimax

我最近通过python在线课程注册了CS50人工智能,第一个项目是使用minimax算法创建tic tac toe游戏,我已经尝试过。但是,当我从他们的网站运行与zip文件一起提供的Runner.py文件时,它给了我一些类似于该语句的错误: i =动作[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)

1 个答案:

答案 0 :(得分:1)

几个问题:

  • Xno = npy.count_nonzero(numpy_board = X)中的语法错误。您错过了一个等号。它应该是==。在下一个类似的语句中出现相同的错误
  • elif Ono > Xno:中的条件永远不会成立(考虑一下)。更重要的是,这种情况使得不经过任何if..elif返回值而进入此None的可能性成为可能。轮到X了,还是不是。在后一种情况下,总是轮到O了。您不需要第二次测试。因此,将这一行更正为else:
  • Result.add(board[k][l])不会添加坐标对,而是会添加正方形的内容。这不是您想要的。您要存储坐标。因此,它应该是Result.add((k, l))。注意:不要使用Pascal大小写,而是驼色。
  • 在函数winner中,for循环将在其第一次迭代中 away 退出。它从不执行其他迭代。您在第一次迭代中所知不够多,无法返回None。因此,请删除else: return None:在这种情况下,循环必须继续进行。注意:对角线的测试最好移到循环之外,因为将测试重复3次是没有意义的。它不依赖于循环变量。

如果您进行了这些更正,应该可以。

其他一些评论:

  • 如果要从列表中创建一个numpy数组,那么为什么不从头开始仅创建一次numpy数组,并仅使用该列表而不是列表?每次在playerterminal中进行转换都会对性能产生影响。
  • 同样,先计算X的数量,再计算O的数量,需要两次迭代,而您可以一次扫描来计算空单元格,然后从中减去不为空的单元格。甚至更快的方法是仅维护一个计数器,并在进行移动时增加计数器,而在回溯时减小计数器。
  • 上述计数器可用于快速确定当前玩家。如果打的动作数是偶数,则为X圈,否则为O圈。
  • deepcopy会降低性能。考虑使用相同的列表/数组而不重复。您只需要在递归调用之后添加“撤消”操作即可。
  • 与其重新创建一组可能的动作,还考虑增量地维护一个动作:在播放动作时从该动作中删除一个动作,并在回溯时将其放回原处。这样可以提高性能。
  • 请勿使用此模式:

    if (empty_no == 0):
       return True
    else:
       return False
    

    首先,括号不是必需的,但更重要的是:当您已经拥有布尔表达式(empty_no == 0)时,只需返回 。不要做这些if..else的事情:

    return empty_no == 0
    
  • minimax算法仅返回值-1、0或1,这意味着它不赞成快速获胜而不是缓慢获胜。这可能会导致出乎意料的举动,其中没有直接赢球。要对此进行改进,请考虑使用更动态的值。一种想法是更改utility函数,以使X获胜时返回空闲单元格的数量加1。对于O,则等于该值的取反。这样有利于快速获胜。