Что не так с этой игрой ti c ta c toe python - PullRequest
0 голосов
/ 21 апреля 2020

Я недавно зарегистрировался в cs50 Artificial Intelligence с онлайн-курсом python, и первый проект - создать игру ti c ta c toe, используя алгоритм минимакса, и я попробовал его. Но когда я запускаю файл runner.py, поставляемый вместе с zip-файлом с их веб-сайта, он выдает мне некоторые ошибки, например, для этого оператора: i = action [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 Ответ

1 голос
/ 22 апреля 2020

Несколько проблем:

  • Синтаксическая ошибка в Xno = npy.count_nonzero(numpy_board = X). Вы пропустили знак равенства там. Это должно быть ==. Та же ошибка в следующем аналогичном утверждении
  • Условие в elif Ono > Xno: никогда не будет верным (подумайте об этом). Более того, это условие оставляет возможность пройти через этот if..elif, не входя ни в один из блоков, давая возвращаемое значение None. Либо настала очередь Х, либо нет. В последнем случае всегда очередь О. Вам не нужно проходить второй тест. Так что исправьте эту строку так: else:
  • Result.add(board[k][l]) добавляет не пару координат, а содержимое квадрата. Это не то, что вы хотите. Вы хотите сохранить координаты. Так что это должно быть Result.add((k, l)). Примечание: не используйте Pascal кейс для такого имени, но верблюжий кейс.
  • В функции winner for l oop будет всегда выходить на его Первая итерация. Он никогда не выполняет другие итерации. Вы не можете знать достаточно в первой итерации, чтобы вернуть None. Поэтому удалите это else: return None: в этом случае l oop должен просто продолжаться. Примечание: тест на диагонали лучше перемещать за пределы l oop, так как нет смысла повторять этот тест 3 раза. Он не зависит от переменной l oop.

Если вы внесете эти исправления, он должен работать.

Некоторые другие замечания:

  • Если Вы собираетесь создать массив numpy из списка, тогда почему бы не создать только массив numpy с самого начала и работать только с этим, а не со списком? Выполнение преобразования каждый раз в player и terminal влияет на производительность.
  • Кроме того, подсчет числа X и затем числа O требует двух итераций, в то время как вы можете сосчитать пустые ячейки в одной подмети, и вычитай из того сколько не пусто. Еще быстрее было бы просто поддерживать счетчик и увеличивать его при воспроизведении хода, а уменьшать при возврате.
  • Вышеупомянутый счетчик можно использовать для быстрого определения текущего игрока. Если количество сыгранных ходов четное, то это ход Х, в противном случае ход О.
  • deepcopy имеет стоимость исполнения. Попробуйте использовать тот же список / массив, не дублируя его. Вам просто нужно добавить операцию «отменить» после рекурсивного вызова.
  • Вместо того, чтобы воссоздавать набор возможных ходов, также рассмотрите возможность сохранения одного набора постепенно: удалите действие из этого набора, когда вы играете ход, и положить его обратно при возврате. Это повысит производительность.
  • Не используйте этот шаблон:

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

    Прежде всего, скобки не нужны, но что более важно: когда вы уже иметь логическое выражение (empty_no == 0), затем просто вернуть его. Не делайте этого if..else stuff:

    return empty_no == 0
    
  • Минимаксный алгоритм возвращает только значения -1, 0 или 1, что означает, что он не одобряет быстрые выигрыши по сравнению с медленными выигрышами. Это может привести к неожиданным ходам, когда прямой выигрыш не разыгрывается. Чтобы улучшить это, рассмотрите возможность использования более динамического значения c. Одна идея состоит в том, чтобы изменить функцию utility так, чтобы при выигрыше с X он возвращал количество свободных ячеек плюс 1. Для O это было бы отрицанием этого значения. Таким образом, быстрые победы приветствуются.
...