【问题标题】:Tic Tac Toe AI recursing infinitely and not following rulesTic Tac Toe AI 无限递归且不遵循规则
【发布时间】:2019-09-22 21:02:33
【问题描述】:

我对编程并不陌生,但我之前没有按照教程尝试过制作太多东西。这是我第一次尝试真正的项目,而不遵循视频或复制粘贴代码。我创建了一个简单的井字游戏,如果两个玩家都手动选择位置,它可以正常工作,但是当我尝试使用 minimax 算法实现一个简单的 AI 时,它会一次播放所有 AI 动作,我不知道为什么。

我多次查看代码,但我不明白为什么当玩家只能移动 1 步时 AI 可以继续放置棋子/移动。根据代码,一次只允许 1 次移动。到底是怎么回事?当轮到人工智能时,人工智能会放置棋子直到它获胜,而不考虑轮到什么的。玩家棋子也被放置,这很奇怪,因为需要提示玩家进行选择输入。我认为问题出在 minimax 函数本身,但我无法确定在哪里。任何帮助将不胜感激!非常感谢。完整文件:

import random
from time import sleep
from math import inf

PLAYER = 0
AI = 1
BOARD = [[' ', ' ', ' '],
         [' ', ' ', ' '],
         [' ', ' ', ' ']]
#AVAILABLE = [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2]]
AVAILABLE = [1,2,3,4,5,6,7,8,9]
SELECTION = 0
TURN = random.randint(0, 1)

def display_board(board):
    print(f'\t[{board[0][0]}] [{board[0][1]}] [{board[0][2]}]\n\t[{board[1][0]}] [{board[1][1]}] [{board[1][2]}]\n\t[{board[2][0]}] [{board[2][1]}] [{board[2][2]}]')

def convert_position(available_locations, position):
    if position in available_locations:
        available_locations.remove(position)
    position -= 1
    converted = (position // 3, position % 3)
    return converted

def is_valid_location(board):
    pass

def place_piece(board, position, player):
    a, b = position
    if a in [0, 1, 2] and b in [0, 1, 2]:
        if board[a][b] == ' ' and player == PLAYER:
            board[a][b] = 'X'
        if board[a][b] == ' ' and player == AI:
            board[a][b] = 'O'

def clear_board(board):
    board = [[' ', ' ', ' '],
             [' ', ' ', ' '],
             [' ', ' ', ' ']]
    return board

def is_win_player(board):
    if board[0][0] != ' ' and board[0][1] == board[0][0] and board[0][2] == board[0][0]:
        if board[0][0] == 'X':
            return True
    if board[1][0] != ' ' and board[1][1] == board[1][0] and board[1][2] == board[1][0]:
        if board[1][0] == 'X':
            return True
    if board[2][0] != ' ' and board[2][1] == board[2][0] and board[2][2] == board[2][0]:
        if board[2][0] == 'X':
            return True
    if board[0][0] != ' ' and board[1][1] == board[0][0] and board[2][2] == board[0][0]:
        if board[0][0] == 'X':
            return True
    if board[2][0] != ' ' and board[1][1] == board[2][0] and board[0][2] == board[2][0]:
        if board[2][0] == 'X':
            return True
    if board[0][0] != ' ' and board[1][0] == board[0][0] and board[2][0] == board[0][0]:
        if board[0][0] == 'X':
            return True
    if board[0][1] != ' ' and board[1][1] == board[0][1] and board[2][1] == board[0][1]:
        if board[0][1] == 'X':
            return True
    if board[0][2] != ' ' and board[1][2] == board[0][2] and board[2][2] == board[0][2]:
        if board[0][2] == 'X':
            return True

def is_win_ai(board):
    if board[0][0] != ' ' and board[0][1] == board[0][0] and board[0][2] == board[0][0]:
        if board[0][0] == 'O':
            return True
    if board[1][0] != ' ' and board[1][1] == board[1][0] and board[1][2] == board[1][0]:
        if board[1][0] == 'O':
            return True
    if board[2][0] != ' ' and board[2][1] == board[2][0] and board[2][2] == board[2][0]:
        if board[2][0] == 'O':
            return True
    if board[0][0] != ' ' and board[1][1] == board[0][0] and board[2][2] == board[0][0]:
        if board[0][0] == 'O':
            return True
    if board[2][0] != ' ' and board[1][1] == board[2][0] and board[0][2] == board[2][0]:
        if board[2][0] == 'O':
            return True
    if board[0][0] != ' ' and board[1][0] == board[0][0] and board[2][0] == board[0][0]:
        if board[0][0] == 'O':
            return True
    if board[0][1] != ' ' and board[1][1] == board[0][1] and board[2][1] == board[0][1]:
        if board[0][1] == 'O':
            return True
    if board[0][2] != ' ' and board[1][2] == board[0][2] and board[2][2] == board[0][2]:
        if board[0][2] == 'O':
            return True

def score_eval(board):
    for row in range(0, 3):
        if board[row][0] == board[row][1] and board[row][1] == board[row][2]:
            if board[row][0] == 'O':
                return 10
            if board[row][0] == 'X':
                return -10
    for col in range(0, 3):
        if board[0][col] == board[1][col] and board[1][col] == board[2][col]:
            if board[row][0] == 'O':
                return 10
            if board[row][0] == 'X':
                return -10

    if board[0][0] == board[1][1] and board[1][1] == board[2][2]:
        if board[0][0] == 'O':
            return 10
        if board[0][0] == 'X':
            return -10
    if board[2][0] == board[1][1] and board[1][1] == board[0][2]:
        if board[2][0] == 'O':
            return 10
        if board[2][0] == 'X':
            return -10

def is_tie(board):
    counter = 0
    for array in board:
        for index in array:
            if index == ' ':
                counter += 1
    if counter == 0:
        return True

def is_terminal(board):
    return is_tie(board) or is_win_player(board) or is_win_ai(board)

def minimax(board, depth, maximizingPlayer, available_positions):
    valid_locations = available_positions
    print(valid_locations)
    terminal = is_terminal(board)
    if depth == 0 or terminal:
        if terminal:
            if is_win_ai(board):
                return None, 99999999
            if is_win_player(board):
                return None, -99999999
            if is_tie(board): 
                return None, 1
        else:
            return None, score_eval(board)
    if maximizingPlayer:
        value = -inf
        selection = random.choice(valid_locations)
        for num in valid_locations:
            board_copy = board.copy()
            position = convert_position(valid_locations, num)
            place_piece(board_copy, position, AI)
            new_score = minimax(board_copy, depth-1, False, valid_locations)[1]
            if new_score > value:
                value = new_score
                selection = num
        return selection, value
    else:
        value = inf
        selection = random.choice(valid_locations)
        for num in valid_locations:
            board_copy = board.copy()
            position = convert_position(valid_locations, num)
            place_piece(board_copy, position, PLAYER)
            new_score = minimax(board_copy, depth-1, True, valid_locations)[1]
            if new_score < value:
                value = new_score
                selection = num
        return selection, value



while SELECTION != 'QUIT':
    print('\t< Tic Tac Toe >\n')
    print('\tPlease make a selection:')
    print("\t'PLAY' to play a game")
    print("\t'QUIT' to exit application\n")
    SELECTION = input('> ').upper()
    if SELECTION != 'QUIT' and SELECTION != 'PLAY':
        print(SELECTION)
        print('Please enter a valid selection.')
    elif SELECTION == 'PLAY':
        game_over = False
        while not game_over:
            display_board(BOARD)
            if is_tie(BOARD):
                print('Tie game. No winner.')
                input('Press any key to continue...')
                game_over = True
            elif TURN == PLAYER:
                print("Player's turn!")
                position = int(input('Please enter a number 1 - 9 to make a mark: '))
                selection = convert_position(AVAILABLE, position)
                place_piece(BOARD, selection, PLAYER)
                if is_win_player(BOARD):
                    print('Player has won the game!')
                    display_board(BOARD)
                    game_over = True
            elif TURN == AI:
                print("AI's turn!")
                sleep(0.9)
                x, y = minimax(BOARD, 7, True, AVAILABLE)  #x = position, y = score
                selection = convert_position(AVAILABLE, x)
                place_piece(BOARD, selection, AI)
                if is_win_ai(BOARD):
                    print('AI has won the game!')
                    display_board(BOARD)
                    game_over = True
            TURN += 1
            TURN = TURN % 2
            print(AVAILABLE)
        BOARD = clear_board(BOARD)
        AVAILABLE = [1,2,3,4,5,6,7,8,9]

【问题讨论】:

  • 如果您打算从事一个真正的项目,我强烈建议您首先阅读并开始关注PEP 8 - Style Guide for Python Code,以使您的代码更易于阅读和遵循——无论是为您自己还是为他人。
  • 如果我运行游戏并选择5 作为我的第一个标记号码,则会出现TypeError: '&gt;' not supported between instances of 'NoneType' and 'float' — 因为显然这是score_eval() 在被调用8 次后返回的内容。这表明其中有一个代码路径超出了函数的末尾。

标签: python python-3.x minimax


【解决方案1】:

其中一个可能的罪魁祸首是试图在您的极小极大函数中“克隆”棋盘的位:

board_copy = board.copy()

在 Python 中,list.copy 方法返回一个列表的浅拷贝,它本质上是一个包含 3 个元素的新列表,这些元素是对原始板中“行”的引用。尝试使用copy.deepcopy()documentation

import copy
...
board_copy = copy.deepcopy(board)

【讨论】:

    猜你喜欢
    • 2013-09-23
    • 1970-01-01
    • 2020-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-03
    相关资源
    最近更新 更多