【问题标题】:Why my minimax algorithm doesn't make the perfect move - 1D Chess?为什么我的极小极大算法没有做出完美的一步 - 1D Chess?
【发布时间】:2021-02-11 13:08:11
【问题描述】:

我正在尝试开发一种极小极大算法来下一维国际象棋,我正在考虑以下内容:8 个方格对齐、1 个国王、1 个骑士和 1 个车。国王和车以通常的方式移动,而骑士一次移动 2 个方格。此外,如果相持或无法将死(只有 2 个国王或 2 个国王和一个额外的骑士)或进行 8 次移动而不减少棋子数,则为平局。

这些是游戏本身的功能。

from copy import deepcopy

# Board -> [list of pieces and non pieces, turn, plays without chagnge number of pieces]
# Piece -> 'K0', 'N1', 'R0', ...

def new_board():
    return [['K0','N0','R0','--','--','R1','N1','K1'], 0, 0]

# Return True if there is any piece on the position index and False otherwise
def pieceQ(board, index):
    return board[0][index][0] in ['K','N','R']

def available_piece(board, piece):
    return piece in board[0]

# Return position of the piece
def piece_index(board, piece):
    if piece in board[0]:
        return board[0].index(piece)
    else:
        return -1

# Return the piece on position index
def index_piece(board, index):
    if pieceQ(board, index):
        return board[0][index]
    else:
        return '--'
    
def print_board(board, code = -1):
    if code == -1:
        print(board[0]) 
    else:
        second_board = deepcopy(board[0])
        second_board[code] = '*'+second_board[code]+'*'
        print(second_board)
        

def available_pieces(board):
    a_pieces = []
    for x in board[0]:
        if x[1]==str(board[1]):
            a_pieces += [x]
    return a_pieces

# Returns the position of the pieces before and after the rook
def min_max_rook(board):
    rook_index = board[0].index('R'+str(board[1]))
    minim = 0
    for i in range(len(board[0][:rook_index])):
        if pieceQ(board, i):
            minim = i
            if index_piece(board, i)[1]==str(board[1]):
                minim+=1
            
    maxim = 7
    for i in range(7, rook_index, -1):
        if pieceQ(board, i):
            maxim = i
            if index_piece(board,i)[1]==str(board[1]):
                maxim-=1
    return (minim, maxim)

def available_moves(board):
    pieces = available_pieces(board)
    moves  = []
    for piece in pieces:
        piece_ind = piece_index(board, piece)
        # Move King
        if piece[0]=='K':
            # Move forward
            if piece_ind<7:
                second_board = deepcopy(board)
                if index_piece(board, piece_ind+1)[1]!=str(board[1]) and index_piece(board, piece_ind+1)[0]!='K':
                    second_board_2 = move(second_board, piece[0], piece_ind+1, free=True)
                    if not check((second_board_2[0],(second_board_2[1]+1)%2)):
                        moves += [(piece[0], piece_ind+1)]
            # Move backwards
            if piece_ind>0:
                second_board = deepcopy(board)
                if index_piece(board, piece_ind-1)[1]!=str(board[1]) and index_piece(board, piece_ind-1)[0]!='K':
                    second_board_2 = move(second_board, piece[0], piece_ind-1, free=True)
                    if not check((second_board_2[0],(second_board_2[1]+1)%2)):
                        moves += [(piece[0], piece_ind-1)]
        # Move Knight        
        elif piece[0]=='N':
            # Move forward
            if piece_ind<6:
                second_board = deepcopy(board)
                if index_piece(board, piece_ind+2)[1]!=str(board[1]) and index_piece(board, piece_ind+2)[0]!='K':
                    second_board_2 = move(second_board, piece[0], piece_ind+2, free=True)
                    if not check((second_board_2[0],(second_board_2[1]+1)%2)):
                        moves += [(piece[0], piece_ind+2)]
            # Move backwards
            if piece_ind>1:
                second_board = deepcopy(board)
                if index_piece(board, piece_ind-2)[1]!=str(board[1]) and index_piece(board, piece_ind-2)[0]!='K':                
                    second_board_2 = move(second_board, piece[0], piece_ind-2, free=True)
                    if not check((second_board_2[0],(second_board_2[1]+1)%2)):
                        moves += [(piece[0], piece_ind-2)]            
        # Move Rook    
        elif piece[0]=='R':
            minim, maxim = min_max_rook(board)
            for i in range(minim, maxim+1):
                second_board = deepcopy(board)
                if i!=piece_ind and index_piece(board, i)[0]!='K':
                    second_board_2 = move(second_board, piece[0], i, free=True)
                    if not check((second_board_2[0],(second_board_2[1]+1)%2)):
                        moves += [(piece[0], i)]
    return moves
                
def move(board, piece, position, free=False):
    if not free and (piece, position) not in available_moves(board):
        print('It is impossible to move '+piece+' to',position)
        raise # IMPOSSIBLE MOVE
        
    elif not available_piece(board, piece+str(board[1])):
        raise #PIECE NOT AVAILABLE

    else:
        n_pieces_old           = len([i for i in range(len(board[0])) if pieceQ(board,i)])
        old_position           = piece_index(board, piece+str(board[1]))
        board[0][old_position] = '--'
        board[0][position]     = piece+str(board[1])
        board[1]               = (board[1]+1)%2
        n_pieces_new           = len([i for i in range(len(board[0])) if pieceQ(board,i)])
        if n_pieces_old==n_pieces_new:
            board[2] += 1
            return [board[0],board[1],board[2]+1]
        else:
            board[2] = 0
            return [board[0],board[1],0]

# Returns True if board is in a check position for player board[1]
def check(board):
    king_index = piece_index(board, 'K'+str(board[1]))    
    if king_index+2 <= 7 and index_piece(board, king_index+2) == 'N'+str((board[1]+1)%2):
        return True        
    elif king_index-2 >= 0 and index_piece(board, king_index-2) == 'N'+str((board[1]+1)%2):
        return True 
    
    else:
        # Check with Rook
        rook_index = piece_index(board,'R'+str((board[1]+1)%2))
        if rook_index != -1:
            if abs(king_index-rook_index)==1:
                return True
            found = False
            for i in range(min(king_index, rook_index)+1, max(king_index, rook_index)):
                found = found or (pieceQ(board, i))
            if not found:
                return True
            
        # Check with king
        king_index_2 = piece_index(board, 'K'+str((board[1]+1)%2))
        if abs(king_index-king_index_2)==1:
            return True   
        else:
            return False             
        
def stalemate(board):
    return (not check(board) and len(available_moves(board))==0) or len([board[0][i] for i in range(len(board[0])) if pieceQ(board, i)])==2 or (len([x for x in board[0] if x[0] in ['K', 'N']])==3 and len([i for i in range(len(board[0])) if pieceQ(board,i)])==3) or board[2]==8
        
def checkmate(board):
    return check(board) and len(available_moves(board))==0

def game_over(board):
    return checkmate(board) or stalemate(board)

def evaluate_board(board):
    if not board[1]:
        # Defeat
        if checkmate(board):
            return -1
        # Victory
        elif checkmate([board[0], (board[1]+1)%2]):
            return 1
        else:
            return 0
    else:
        # Defeat
        if checkmate(board):
            return 1
        # Victory
        elif checkmate([board[0], (board[1]+1)%2]):
            return -1
        else:
            return 0

这是我在 Codecademy tic tac toe 算法中启发的 minimax 算法。在这里我保证将永远发生。

def minimax(input_board, is_maximizing, max_depth=8):
    depth = max_depth-1
    # Base case - the game is over, so we return the value of the board
    if game_over(input_board):
        return [evaluate_board(input_board), ('','')]
    
    # The maximizing player
    elif is_maximizing:
        # The best value starts at the lowest possible value
        best_value = -float("Inf")        
        best_move = ('','')
        
        av_moves = available_moves(input_board)
        if len(av_moves)==1:
            return [best_value, av_moves[0]]
        else:
            for m in av_moves:
                new_board = deepcopy(input_board)
                move(new_board, m[0], m[1])
                if checkmate(new_board):
                    return [best_value, m]
    
            # Loop through all the available moves
            for m in av_moves:
                # Make a copy of the board and apply the move to it
                new_board = deepcopy(input_board)
                move(new_board, m[0], m[1])

                # Recursively find your opponent's best move
                if depth >= 0:
                    hypothetical_value = minimax(new_board, False, depth)[0]
                else:
                    return [(best_value if best_move != ('','') else 0), (best_move if best_move != ('','') else m)]

                # Update best value if you found a better hypothetical value
                if hypothetical_value > best_value:
                    best_value = hypothetical_value
                    best_move = m
                    
            return [best_value, (best_move if best_move != ('','') else m)]
        
    # The minimizing player  
    else:
        # The best value starts at the highest possible value
        best_value = float("Inf")
        best_move = ('','')
        av_moves = available_moves(input_board)
        if len(av_moves)==1:
            return [best_value, av_moves[0]]
        else:
            for m in av_moves:
                new_board = deepcopy(input_board)
                move(new_board, m[0], m[1])
                if checkmate(new_board):
                    return [best_value, m]
            
            # Testing all potential moves
            for m in av_moves:
                # Copying the board and making the move
                new_board = deepcopy(input_board)
                move(new_board, m[0], m[1])
                # Passing the new board back to the maximizing player
                if depth >= 0:
                    hypothetical_value = minimax(new_board, True, depth)[0]
                else:
                    return [(best_value if best_move != ('','') else 0), (best_move if best_move != ('','') else m)]
                # Keeping track of the best value seen so far
                if hypothetical_value < best_value:
                    best_value = hypothetical_value
                    best_move = m      
            return [best_value, (best_move if best_move != ('','') else m)]

(我考虑了从 0 到 7 的棋盘)

播放器 0 是第一个播放的。问题是我可以作为玩家 0 击败算法,我的动作是 - N3、N5、R3、K1、N7 - 但我可以作为玩家 1 移动 - N4、K6、N2、K5 来绘制。这是我作为玩家 1 绘制的游戏。

['K0', 'N0', 'R0', '--', '--', 'R1', 'N1', 'K1']

['K0', '--', 'R0', 'N0', '--', 'R1', 'N1', 'K1']

件,位置:n4 ['K0', '--', 'R0', 'N0', 'N1', 'R1', '--', 'K1']

['K0', '--', 'R0', '--', 'N1', 'N0', '--', 'K1']

件,位置:k6 ['K0', '--', 'R0', '--', 'N1', 'N0', 'K1', '--']

['--', 'K0', 'R0', '--', 'N1', 'N0', 'K1', '--']

件,位置:n2 ['--', 'K0', 'N1', '--', '--', 'N0', 'K1', '--']

['--', '--', 'K0', '--', '--', 'N0', 'K1', '--']

我认为在我的 k6 之后它应该与车而不是与国王一起前进。

这样他最终可以平局而不是输球。 希望你能帮助我。

谢谢!

【问题讨论】:

  • 我想说一些玩游戏的常见错误是:没有为正确的玩家评估移动,没有记住返回到递归调用 minimax 的最大值需要乘以 -1表明在深度 n+1 的更好结果对于深度 n 的其他玩家来说是更差的结果。常见的协议是翻转每个要评估的状态,使得当前玩家是白色并且在棋盘的底部,评估,返回这个值,并在递归调用返回时乘以 -1
  • 粗略的一瞥告诉我,也许你忘了乘以 -1 来否定对手的值
  • 我制作了evaluate_board 函数,因此它会因播放器而异。我认为乘以 -1 并不能解决问题——我实际上尝试过,一切都变得更好了。无论如何,我很欣赏你的评论。

标签: python chess minimax


【解决方案1】:

问题是我使用深度的方式不起作用。它应该在开头(在基本情况下)。我想问题已经解决了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-16
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 2019-05-09
    相关资源
    最近更新 更多