我正在使用极小化极大算法和Python实现跳棋游戏。游戏中有两个玩家 – 都是电脑。我一直在寻找类似问题的解决方案,但没有找到任何结果,并且已经为此挣扎了几天。我的入口点是这个函数:
def run_game(board): players = board.players is_move_possible = True move = 0 while is_move_possible: is_move_possible = move_piece_minimax(board, players[move % 2]) move += 1
它启动游戏并调用下一个函数,该函数应该根据极小化极大算法为第一位玩家做出最佳移动。在第一次移动之后,它会为第二位玩家调用这个函数,这个循环将在其中一位玩家赢得游戏后结束。该函数如下所示:
def move_piece_minimax(board, player): best_move = minimax(copy.deepcopy(board), player, 0) if best_move.score == +infinity or best_move.score == -infinity: return False move_single_piece(board.fields, player, best_move) return True
第一行调用极小化极大算法,我将在后面描述,它应该为玩家找到最佳可能的移动。我在这里传递了整个棋盘的深拷贝,因为我不想在执行极小化极大算法时编辑原始棋盘。条件检查获胜条件,因此如果最大化玩家或最小化玩家获胜。如果没有一方获胜,则执行best_move。现在回到主要问题,我实现了极小化极大算法如下:
def minimax(board, player, depth): best_move = Move(-1, -1, -infinity if player.name == PLAYER_NAMES['P1'] else +infinity) if depth == MAX_SEARCH_DEPTH or game_over(board): score = evaluate(board) return Move(-1, -1, score) for correct_move in get_all_correct_moves(player, board.fields): x, y, piece = correct_move.x, correct_move.y, correct_move.piece move_single_piece(board.fields, player, correct_move) player_to_move = get_player_to_move(board, player) move = minimax(board, player_to_move, depth + 1) # <--- 这里是递归 move.x = x move.y = y move.piece = piece if player.name == PLAYER_NAMES['P1']: if move.score > best_move.score: best_move = move # 最大值 else: if move.score < best_move.score: best_move = move # 最小值 return best_move
我决定玩家‘P1’是最大化玩家,玩家‘P2’是最小化玩家。从第一行开始,best_move变量保存对Move对象的引用,该对象具有以下字段:
class Move: def __init__(self, x, y, score, piece=None): self.x = x self.y = y self.score = score self.piece = piece
我将best_move.score初始化为最大化玩家的-Infinity,对于其他情况则为+Infinity。
第一个条件检查深度是否达到了最大级别(为了测试目的,设置为2)或游戏是否结束。如果是,则评估当前棋盘的情况并返回一个包含当前棋盘得分的Move对象。否则,我的算法会寻找该玩家所有合法/正确的移动,并执行第一个移动。
执行后,该函数以递归方式调用,但深度增加并更换移动的玩家。该函数在更改参数后再次运行,直到第一个if条件执行。
一旦执行进入该分支,将返回棋盘的评估得分,之后,在递归调用后的for循环中,将移动的坐标x、y和棋子分配给Move对象。
最后的条件检查新得分是否是该特定玩家的更好得分。如果这是最大化玩家,在我的例子中是P1,它会检查新得分是否高于之前的得分。对于最小化玩家,算法寻找最低得分。
在为该玩家执行所有正确移动后,我的算法应该返回一个best_move。
预期结果
Move类的单个对象,包含坐标x和y,评估的棋盘得分,仅在其中一位玩家获胜的情况下为+Infinity/-Infinity,以及将移动到[x, y]坐标的Piece类对象。
实际结果
Move类的单个对象,包含坐标x和y,评估的棋盘得分在第一次调用极小化极大函数后等于+Infinity。没有棋子改变其位置,因此游戏尚未结束。然而,得分为+Infinity,因此函数move_piece_minimax()将返回False – 意味着没有更多可能的移动。因此,我的程序将在棋盘上没有任何变化的情况下停止执行。这是初始和最终棋盘状态的截图 – 在执行过程中没有任何变化,因为第一次调用返回了+Infinity。
我的问题是,在实现极小化极大算法时我遗漏了什么?我犯了什么错误?我也欢迎任何代码改进或建议。如果您需要了解我的实现的任何其他函数,我会提供它们。谢谢!
回答:
在极小化极大函数中,您应该执行以下操作之一
1. 在放置棋子之前制作棋盘的副本
2. 在递归调用极小化极大函数后移除放置的棋子
否则,您的棋盘将在递归过程中被棋子填满,您将收到一个错误,指出没有剩余的移动。极小化极大算法旨在通过放置棋子进行深入搜索,因此您应该实现一种方法,使其不修改原始棋盘。