Hexapawn游戏Minimax算法Python实现:AI无法执行计算出的移动
Hexapawn Minimax算法问题:AI移动无法生效的原因及修复
你遇到的核心问题是AI计算出的移动无法正确作用到棋盘上,同时Minimax算法的实现存在几个关键逻辑错误,导致整个流程异常。以下是具体问题和修复方案:
关键问题分析
Minimax递归时未切换玩家身份
在Minimax的递归过程中,始终使用实例的self.player生成可能的移动,但模拟不同玩家回合时,没有动态切换玩家身份,导致生成的移动始终属于同一玩家,递归逻辑完全错误。评估函数使用错误的棋盘状态
evaluate_board直接读取self.board进行评估,但Minimax中模拟移动的是复制出来的board实例,这导致评估的是原始棋盘而非当前模拟的棋盘状态,完全失去了Minimax的意义。AI回合的Minimax角色搞反
评估函数返回player1_pawns - player2_pawns,人类玩家是1,AI是2。AI需要最小化这个评估值(值越小代表AI的棋子数量相对更多),但ai_turn中调用Minimax时传入了True(maximizing_player),导致AI尝试最大化对自己不利的评估值,逻辑完全颠倒。未基于模拟棋盘生成移动
所有的get_possible_moves、is_game_over都依赖self.board,但模拟过程中应该基于复制的棋盘判断,而非实例的原始棋盘。
修复后的完整代码
import copy class Hexapawn: def __init__(self): self.board = [[2,2,2], [0,0,0], [1,1,1]] self.player = 1 def display_board(self): for row in self.board: print(row) def ai_turn(self): # AI是玩家2,属于minimizing player,对应评估值越小越好 move = self.minimax(3, float('-inf'), float('inf'), False)[1] if move: self.make_move(move, self.board) print("AI moves:", move) else: print("AI has no valid moves.") def player_turn(self): player_move = self.get_player_move() self.make_move(player_move, self.board) if self.is_game_over(): self.display_board() print("Player wins!") return self.display_board() def play_game(self): while True: self.player_turn() self.player = 1 if self.player == 2 else 2 if self.is_game_over(): break self.ai_turn() self.player = 1 if self.player == 2 else 2 if self.is_game_over(): self.display_board() print("AI wins!") break # 修改评估函数,接受board参数,基于传入的棋盘评估 def evaluate_board(self, board): player1_pawns = sum(row.count(1) for row in board) player2_pawns = sum(row.count(2) for row in board) return player1_pawns - player2_pawns def get_player_move(self): while True: try: orow, ocol = map(int, input("Enter row and column of the pawn you want to move (e.g., 0 1): ").split()) nrow, ncol = map(int, input("Enter row and column of the destination (e.g., 1 1): ").split()) move = ((orow, ocol), (nrow, ncol)) if move in self.get_possible_moves(self.board, self.player): return move else: print("Invalid move. Try again.") except ValueError: print("Invalid input. Please enter row and column numbers separated by a space.") def make_move(self, move, board): orow, ocol = move[0] nrow, ncol = move[1] board[nrow][ncol] = board[orow][ocol] board[orow][ocol] = 0 def undo_move(self, move, board): orow, ocol = move[0] nrow, ncol = move[1] board[orow][ocol] = board[nrow][ncol] board[nrow][ncol] = 0 # 修改is_game_over,接受board和player参数 def is_game_over(self, board=None, player=None): if board is None: board = self.board if player is None: player = self.player # 检查当前玩家是否有合法移动 if not self.get_possible_moves(board, player): return True # 检查是否有玩家到达对方底线 for col in range(len(board[0])): if board[0][col] == 1 or board[2][col] == 2: return True return False # 修改get_possible_moves,接受board和player参数,基于传入的棋盘和玩家生成移动 def get_possible_moves(self, board, player): possible = [] opponent = 2 if player == 1 else 1 for row in range(len(board)): for col in range(len(board[row])): if board[row][col] == player: if player == 1: # 玩家1(人类)的移动:向上(row-1) # 右斜吃子 if row - 1 >= 0 and col + 1 <= 2 and board[row-1][col+1] == opponent: possible.append(((row, col), (row-1, col+1))) # 左斜吃子 if row - 1 >= 0 and col - 1 >= 0 and board[row-1][col-1] == opponent: possible.append(((row, col), (row-1, col-1))) # 直进 if row - 1 >= 0 and board[row-1][col] == 0: possible.append(((row, col), (row-1, col))) elif player == 2: # 玩家2(AI)的移动:向下(row+1) # 右斜吃子 if row + 1 <= 2 and col + 1 <= 2 and board[row+1][col+1] == opponent: possible.append(((row, col), (row+1, col+1))) # 左斜吃子 if row + 1 <= 2 and col - 1 >= 0 and board[row+1][col-1] == opponent: possible.append(((row, col), (row+1, col-1))) # 直进 if row + 1 <= 2 and board[row+1][col] == 0: possible.append(((row, col), (row+1, col))) return possible def minimax(self, depth, alpha, beta, maximizing_player): # 基于当前玩家判断是否游戏结束 current_player = self.player if maximizing_player else (2 if self.player ==1 else 1) if depth == 0 or self.is_game_over(self.board, current_player): return self.evaluate_board(self.board), None if maximizing_player: max_eval = float('-inf') best_move = None # 生成当前玩家(maximizing)的所有可能移动 for move in self.get_possible_moves(self.board, self.player): # 复制棋盘并模拟移动 temp_board = copy.deepcopy(self.board) self.make_move(move, temp_board) # 切换玩家身份 self.player = 2 if self.player ==1 else 1 # 递归调用minimax,此时是minimizing玩家回合 eval_val = self.minimax(depth-1, alpha, beta, False)[0] # 恢复玩家身份和棋盘 self.player = 2 if self.player ==1 else 1 # 比较评估值 if eval_val > max_eval: max_eval = eval_val best_move = move alpha = max(alpha, eval_val) if beta <= alpha: break return max_eval, best_move else: min_eval = float('inf') best_move = None # 生成当前玩家(minimizing)的所有可能移动 for move in self.get_possible_moves(self.board, self.player): # 复制棋盘并模拟移动 temp_board = copy.deepcopy(self.board) self.make_move(move, temp_board) # 切换玩家身份 self.player = 2 if self.player ==1 else 1 # 递归调用minimax,此时是maximizing玩家回合 eval_val = self.minimax(depth-1, alpha, beta, True)[0] # 恢复玩家身份和棋盘 self.player = 2 if self.player ==1 else 1 # 比较评估值 if eval_val < min_eval: min_eval = eval_val best_move = move beta = min(beta, eval_val) if beta <= alpha: break return min_eval, best_move if __name__ == "__main__": game = Hexapawn() game.display_board() game.play_game()
核心修改点说明
- 将
get_possible_moves、is_game_over、evaluate_board改为接受board和player参数,不再依赖实例的self.board和self.player,确保模拟过程基于当前棋盘状态。 - 在Minimax递归过程中,手动切换
self.player并在递归后恢复,保证每一层递归对应正确的玩家回合。 - 修正
ai_turn中Minimax的maximizing_player参数为False,因为AI是玩家2,需要最小化评估值。 - 在Minimax中使用临时复制的棋盘模拟移动,避免修改实例的原始棋盘。
内容的提问来源于stack exchange,提问作者adam5459
相关产品推荐
相关产品推荐

