Моя непобедимая программа Tic Tac Toe терпит неудачу - PullRequest
0 голосов
/ 17 октября 2018

Я пытался использовать минимаксный алгоритм, чтобы создать программу, которая не может проиграть в крестики-нолики.Но в некоторых случаях это терпит неудачу.Например, когда на плате крестики-нолики остаются два пятна (в некоторых случаях), программа останавливает воспроизведение и запрашивает у пользователя два последовательных ввода.Кроме того, в некоторых случаях, когда компьютер явно выигрывает, он не делает правильный выбор ходов.

Это для задания, и любая помощь сегодня будет очень признательна.

Большое спасибо!

Редактировать: Обратите внимание, что код позволяет пользователю перезаписывать предыдущие шаги.Я исправлю это, как только смогу заработать.Однако, даже если я не перезаписываю предыдущие шансы, я не получаю результаты.Я проверил код, и проблема, кажется, заключается в функции минимакса, но я сохранил весь код на случай, если я ошибаюсь, и настоящая проблема заключается в другом месте.

Редактировать 2: Извините за неполноесообщение!Тестовый пример для воспроизведения проблемы ниже.После того, как я ввел свой ход (позиция 5), программа перестает играть и просит меня сыграть все шансы.

Would you like to go first (Y/N)?: n  
. . .  
. . .   
. . .   

x . .   
. . .   
. . .   

Enter your choice (1-9): 5  
x . .   
. o .   
. . .   

x x .   
. o .   
. . .   

Enter your choice (1-9): 7  
x x .   
. o .   
o . .   

x x .   
. o .   
o . .   

Enter your choice (1-9): 

Кроме того, я знаю, что мой код грязный и любительский - но, несмотря на использование глобальных переменных, ядолжен быть в состоянии заставить это работать.Если вы можете помочь мне с этим, я все уберу.Еще раз спасибо!

Редактировать 3: Еще один тестовый пример: Хотите ли вы идти первым (Y / N) ?: y

. . . 
. . . 
. . . 

Enter your choice (1-9): 5
. . . 
. o . 
. . . 

x . . 
. o . 
. . . 

Enter your choice (1-9): 3
x . o 
. o . 
. . . 

x . o 
. o . 
x . . 

Enter your choice (1-9): 2
x o o 
. o . 
x . . 

x o o 
. o . 
x . . 

Enter your choice (1-9): 6
x o o 
. o o 
x . . 

x o o 
. o o 
x . . 

Enter your choice (1-9): 9
You win!

Мой код на Python 3.6 и ниже:

move = -1
n = 0
def evaluateBoard(board):
    global n
    #Checking for rows
    cnt = 0
    for i in range(n):
        res = 0
        for j in range(n):
           res += board[cnt * n + j] 
        cnt += 1
        if res == n:
            return 1
        elif res == -n:
            return -1

    #Checking for columns
    for i in range(n):
        res = 0
        for j in range(n):
            res += board[i + n * j]
        if res == n:
            return 1
        elif res == -n:
            return -1

    #Checking for diagonals
    res = res2 = 0
    for i in range(n):
        res += board[i * (n + 1)]   
        res2 += board[(i + 1) * (n - 1)]
    if n in [res, res2]:
        return 1
    elif -n in [res, res2]:
        return -1

    return 0

def checkNonTerminal(board):
   for pos in board:
       if pos == 0:
           return 1
   return 0

def getScore(board, depth):
    if evaluateBoard(board) == 1:
        return 10 - depth
    elif evaluateBoard(board) == -1:
        return depth - 10
    else:
        return 0

def minimax(board, turn, depth):
    if evaluateBoard(board) == 0 and checkNonTerminal(board) == 0:
        return getScore(board, depth)
    global move
    moves = list()
    scores = list()

    for square, pos in enumerate(board):
        if pos == 0:
            #print(board)
            new_board = board.copy()
            new_board[square] = turn
            moves.append(square)
            #print("Moves:", moves, "depth:", depth, "turn:", turn, checkNonTerminal(new_board) == 0)
            if evaluateBoard(new_board) in [1, -1] or checkNonTerminal(new_board) == 0:
                return getScore(new_board, depth)
            scores.append(minimax(new_board, turn * -1, depth + 1))
        #print("scores", scores) 

    if turn == 1:
        move = moves[scores.index(max(scores))]
        return max(scores)
    elif turn == -1:
        move = moves[scores.index(min(scores))]
        return min(scores)

def displayBoard(board):
    global n
    for i in range(n):
        for j in range(n):
            if board[n*i+j] == 1:
                print("x", end = " ")
            elif board[n*i+j] == -1:
                print("o", end = " ")
            else:
                print(".", end = " ")
        print()

def main():      
    global n 
    global move
    n = 3
    first_turn = input("Would you like to go first (Y/N)?: ")
    if first_turn in ['Y', 'y']:
        first_turn = -1
        cnt = 1
    else:
        first_turn = 1
        cnt = 2
    board = [0] * 9

    while evaluateBoard(board) == 0 and checkNonTerminal(board) == 1:
        displayBoard(board)
        if cnt % 2 == 0:
            score = minimax(board, 1, 0)
            print(score)
            board[move] = 1
        else:
            choice = eval(input("Enter your choice (1-9): "))
            board[choice - 1] = -1
        cnt += 1

    if evaluateBoard(board) == 1:
        print("You lose!")
    elif evaluateBoard(board) == -1:
        print("You win!")
    else:
        print("It's a draw!")

main()

1 Ответ

0 голосов
/ 17 октября 2018

Если ваш первый проверенный ход - это конец игры, вы возвращаетесь, не задав ход.Вероятно, причина вашей неудачной win-логики, а также пропущенного хода.

Проще говоря:
Ваше условие завершения рекурсии срабатывает преждевременно, и вы должны также обработать этот случай!

for square, pos in enumerate(board):
    if pos == 0:
        #print(board)
        new_board = board.copy()
        new_board[square] = turn
        moves.append(square)
        #print("Moves:", moves, "depth:", depth, "turn:", turn, checkNonTerminal(new_board) == 0)
        if evaluateBoard(new_board) in [1, -1] or checkNonTerminal(new_board) == 0:
            return getScore(new_board, depth) <----here
        scores.append(minimax(new_board, turn * -1, depth + 1))
    #print("scores", scores) 

Слишком занят, чтобы проверить, но я полагаю, что вы можете просто установить там свою переменную перемещения - если вы просто выталкиваете свой стек рекурсии, он будет перезаписан позже.

PS, это еще одна причина, чтобы использовать правильные возвращаемые переменные вместо просто установки глобальной переменной;)

...