我尝试使用极小化极大算法来创建一个在井字游戏中不会输的程序。但在一些情况下,程序出现了问题。例如,当井字游戏棋盘上只剩下两个位置时(在某些情况下),程序会停止运行并要求用户连续输入两次。此外,在某些情况下,当电脑有明显的获胜机会时,它却没有选择正确的移动方式。
这是我的作业,今天任何形式的帮助都将非常感激。
非常感谢!
编辑:请注意,代码允许用户覆盖之前的移动。我会尽快修复这个问题。然而,即使我不覆盖之前的机会,我也得不到结果。我已经测试了代码,问题似乎出在极小化极大函数中,但我保留了整个代码,以防我错了,真正的问题在其他地方。
编辑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:另一个测试案例:Would you like to go first (Y/N)?: y
. . . . . . . . . Enter your choice (1-9): 5. . . . o . . . . x . . . o . . . . Enter your choice (1-9): 3x . o . o . . . . x . o . o . x . . Enter your choice (1-9): 2x o o . o . x . . x o o . o . x . . Enter your choice (1-9): 6x o o . o o x . . x o o . o o x . . Enter your choice (1-9): 9You win!
我的代码是Python 3.6版本,代码如下:
move = -1n = 0def 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 0def checkNonTerminal(board): for pos in board: if pos == 0: return 1 return 0def getScore(board, depth): if evaluateBoard(board) == 1: return 10 - depth elif evaluateBoard(board) == -1: return depth - 10 else: return 0def 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()
回答:
如果您检查的第一个移动是结束游戏的移动,您在没有设置移动的情况下就返回了。这可能是您的获胜逻辑失败以及跳过回合的原因。
用更程序员的方式来说:
您的递归终止条件过早触发,您需要处理这种情况!
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)
太忙了,没时间检查,但我相信您也可以在那里设置您的移动变量——如果您只是弹出递归堆栈,它稍后会被覆盖。
附言,这也是使用适当的返回变量而不是仅仅设置全局变量的另一个原因;)