You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hexapawn游戏Minimax算法Python实现:AI无法执行计算出的移动

Hexapawn Minimax算法问题:AI移动无法生效的原因及修复

你遇到的核心问题是AI计算出的移动无法正确作用到棋盘上,同时Minimax算法的实现存在几个关键逻辑错误,导致整个流程异常。以下是具体问题和修复方案:

关键问题分析

  1. Minimax递归时未切换玩家身份
    在Minimax的递归过程中,始终使用实例的self.player生成可能的移动,但模拟不同玩家回合时,没有动态切换玩家身份,导致生成的移动始终属于同一玩家,递归逻辑完全错误。

  2. 评估函数使用错误的棋盘状态
    evaluate_board直接读取self.board进行评估,但Minimax中模拟移动的是复制出来的board实例,这导致评估的是原始棋盘而非当前模拟的棋盘状态,完全失去了Minimax的意义。

  3. AI回合的Minimax角色搞反
    评估函数返回player1_pawns - player2_pawns,人类玩家是1,AI是2。AI需要最小化这个评估值(值越小代表AI的棋子数量相对更多),但ai_turn中调用Minimax时传入了True(maximizing_player),导致AI尝试最大化对自己不利的评估值,逻辑完全颠倒。

  4. 未基于模拟棋盘生成移动
    所有的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 10:14:52