六方游戏的Minimax

问题描述 投票:0回答:1

我正在为 Hexapawn 开发一个 python minimax 实现,但遇到了一个问题:它打印出 ai 的正确移动,但它不会执行该移动。

这是我的代码:

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):
            move = self.minimax(3, float('-inf'), float('inf'), True)[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

    def evaluate_board(self):
        player1_pawns = sum(row.count(1) for row in self.board)
        player2_pawns = sum(row.count(2) for row in self.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():
                    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   
    def is_game_over(self):
        if not self.get_possible_moves():
            return True
        for col in range(len(self.board[0])):
            if self.board[0][col] == 1 or self.board[2][col] == 2:
                return True

        return False
    
        

    def get_possible_moves(self):
        possible = []
        opponent=2 if self.player == 1 else 1

        for row in range(len(self.board)):
            for col in range(len(self.board[row])):
                if self.board[row][col] == self.player:
                    if self.player == 1:
                        #diagonal
                        #r
                        if row -1 >= 0 and col +1 <= 2 and self.board[row-1][col+1] == opponent:
                            possible.append(((row,col),(row-1,col+1)))
                        #l
                        if row-1 >= 0 and col-1 >= 0 and self.board[row-1][col-1] == opponent:
                            possible.append(((row,col),(row-1,col-1)))
                        #vert
                        if row -1 >= 0 and self.board[row-1][col] == 0:
                            possible.append(((row,col),(row-1,col)))
                    elif self.player == 2:
                        #diag
                        #r
                        if row+1 <= 2 and col +1 <= 2 and self.board[row+1][col+1] == opponent:
                            possible.append(((row,col), (row+1, col+1)))
                        #l
                        if row+1 <= 2 and col -1 >= 0 and self.board[row+1][col-1] == opponent:
                            possible.append(((row,col),(row+1, col-1)))
                        #vert
                        if row+1 <= 2 and self.board[row+1][col] == 0:
                            possible.append(((row,col), (row+1, col)))
        return possible

                    
    def minimax(self, depth, alpha, beta, maximizing_player):
        if depth == 0 or self.is_game_over():
            return self.evaluate_board(), None
        board = copy.deepcopy(self.board)

        if maximizing_player:
            max_eval = float('-inf')
            best_move = None
            for move in self.get_possible_moves():
                self.make_move(move, board)
                evaluate = self.minimax(depth-1,alpha, beta, False)[0]
                self.undo_move(move, board)
                if evaluate > max_eval:
                    max_eval = evaluate
                    best_move = move
                alpha = max(alpha, evaluate)
                if beta <= alpha:
                    break
            return max_eval, best_move

        else:
            min_eval= float('inf')
            for move in self.get_possible_moves():
                self.make_move(move, board)
                evaluate = self.minimax(depth-1, alpha, beta, True)[0]
                self.undo_move(move, board)
                if evaluate<min_eval:
                    min_eval = evaluate
                beta = min(beta, evaluate)
                if beta <= alpha:
                    break
            return min_eval, None

if __name__ == "__main__":
    game = Hexapawn()
    game.display_board()
    game.play_game()

我试图找出为什么人工智能的举动没有反映在棋盘上,但没有成功。我错过了什么?

python artificial-intelligence minimax
1个回答
1
投票

它打印出人工智能的正确动作,但它不会执行该动作

实际上,AI 动作已经做出了。但要真正看到结果,您应该在移动完成后立即打印棋盘:

    def ai_turn(self):
        move = self.minimax(3, float('-inf'), float('inf'), True)[1]
        if move:
            self.make_move(move, self.board)
            print("AI moves:", move)
            self.display_board()  # <----------------- add this
        else:
            print("AI has no valid moves.")

其他备注

上面回答了您的直接问题,但您的实现有几个问题:

  • 在极小极大搜索期间,玩家的回合不会改变,即

    self.player
    不会改变,这意味着
    get_possible_moves
    在整个极小极大搜索过程中只会返回同一玩家的移动。为了解决这个问题,我建议在函数
    self.player
    make_move
    中切换
    undo_move
    ,并且仅在那里。一旦你做到了这一点,你就不需要
    maximizing_player
    参数,因为它是由
    self.player
    暗示的。

  • minimax
    self.board
    的副本放入局部变量
    board
    中,并在该副本上执行可能的动作,但递归调用不会对该副本有任何引用,并将从初始
    self.board
    ,忽略刚刚下的棋步。我建议不要复制棋盘,直接在
    self.board
    上玩。由于您总是对每个动作执行撤消操作,因此您最终会得到原始的棋盘,没有任何问题。这也意味着采用
    board
    参数(如
    make_move
    )的函数可以删除该参数并转而使用
    self.board

  • 当游戏结束时,

    evaluate
    函数用于求极小极大分数。但这个函数有两个问题:

    1. 该函数最大化玩家 1 的得分,但 minimax 函数是在 AI 玩家(玩家 2)作为最大化玩家的情况下调用的,因此这会影响良好的评估。

    2. 该功能不利于赢得未结束的游戏。例如,这里左边是一场获胜的比赛,右边是一场未获胜的比赛,两者的最后一步都是玩家 2 下的:

      . . .       . 2 .
      2 1 2       1 1 .
      . . .       . . 2
      

      修正第一个问题后,对于玩家 2 来说,评估函数会倾向于左侧的棋盘,因为它的棋盘比对手的棋子多,但右侧的棋盘显然更好,因为它是一个棋盘。赢。要么首先检测胜利并为此类棋盘提供优先分数,要么为棋盘上更先进的棋子提供更多分数。

© www.soinside.com 2019 - 2024. All rights reserved.