Python国际象棋极小极大算法如何玩黑色棋子(机器人有白色)

2024-05-29 10:19:47 发布

您现在位置:Python中文网/ 问答频道 /正文

动机:

我正在尝试做一个基本的人工智能代理,可以与对手下棋。我们的目标是看看以后通过使用机器学习,它能变得多好,同时也能学习到象棋中隐藏的细节,比如评价参数。在


代码:

以下是我目前所掌握的情况:

import chess, chess.pgn, time, math, io
import numpy as np 

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import Select

piece_values = {'P': 10, 'N': 30, 'B': 30, 'R': 50, 'Q': 90, 'K': 100, 'p': -10, 'n': -30, 'b': -30, 'r': -50, 'q': -90, 'k': -100}

# These are all flipped
position_values = {
        'P' : np.array([ [0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0],
                        [5.0,  5.0,  5.0,  5.0,  5.0,  5.0,  5.0,  5.0],
                        [1.0,  1.0,  2.0,  3.0,  3.0,  2.0,  1.0,  1.0],
                        [0.5,  0.5,  1.0,  2.5,  2.5,  1.0,  0.5,  0.5],
                        [0.0,  0.0,  0.0,  2.0,  2.0,  0.0,  0.0,  0.0],
                        [0.5, -0.5, -1.0,  0.0,  0.0, -1.0, -0.5,  0.5],
                        [0.5,  1.0, 1.0,  -2.0, -2.0,  1.0,  1.0,  0.5],
                        [0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0] ]),

        'N' : np.array([[-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0],
                       [-4.0, -2.0,  0.0,  0.0,  0.0,  0.0, -2.0, -4.0],
                       [-3.0,  0.0,  1.0,  1.5,  1.5,  1.0,  0.0, -3.0],
                       [-3.0,  0.5,  1.5,  2.0,  2.0,  1.5,  0.5, -3.0],
                       [-3.0,  0.0,  1.5,  2.0,  2.0,  1.5,  0.0, -3.0],
                       [-3.0,  0.5,  1.0,  1.5,  1.5,  1.0,  0.5, -3.0],
                       [-4.0, -2.0,  0.0,  0.5,  0.5,  0.0, -2.0, -4.0],
                       [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0] ]),

        'B' : np.array([[-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0],
                       [-1.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0, -1.0],
                       [-1.0,  0.0,  0.5,  1.0,  1.0,  0.5,  0.0, -1.0],
                       [-1.0,  0.5,  0.5,  1.0,  1.0,  0.5,  0.5, -1.0],
                       [-1.0,  0.0,  1.0,  1.0,  1.0,  1.0,  0.0, -1.0],
                       [-1.0,  1.0,  1.0,  1.0,  1.0,  1.0,  1.0, -1.0],
                       [-1.0,  0.5,  0.0,  0.0,  0.0,  0.0,  0.5, -1.0],
                       [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0] ]),

        'R' : np.array([[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,  0.0],
                       [ 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,  0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [ 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0,  0.0]]),

        'Q' : np.array([[-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0],
                       [-1.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0, -1.0],
                       [-1.0,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -1.0],
                       [-0.5,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -0.5],
                       [-0.5,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -0.5],
                       [-1.0,  0.5,  0.5,  0.5,  0.5,  0.5,  0.0, -1.0],
                       [-1.0,  0.0,  0.5,  0.0,  0.0,  0.0,  0.0, -1.0],
                       [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0]]),

        'K' : np.array([[ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                       [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                       [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                       [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                       [ -2.0, -3.0, -3.0, -4.0, -4.0, -3.0, -3.0, -2.0],
                       [ -1.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -1.0],
                       [  2.0,  2.0,  0.0,  0.0,  0.0,  0.0,  2.0,  2.0 ],
                       [  2.0,  3.0,  1.0,  0.0,  0.0,  1.0,  3.0,  2.0 ]])}

class LichessBot:
    def __init__(self, fen):
        self.fen = fen
        self.bot = webdriver.Firefox(executable_path=r'geckodriver.exe')

    def initialize(self):
        bot = self.bot
        bot.get('https://lichess.org/editor/rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR_w_KQkq_-')
        time.sleep(3)
        analysis = bot.find_element_by_css_selector(".actions > a:nth-child(2)").click()
        time.sleep(1)

    def gameSelect(self, fen):
        bot = self.bot

        fen_area = bot.find_element_by_class_name("analyse__underboard__fen")
        bot.execute_script('arguments[0].setAttribute("value", arguments[1]);', fen_area, fen)

        # Refresh the page to enter new fen number properly every time
        fen_new = bot.find_element_by_class_name("analyse__underboard__fen").get_attribute('value').replace(' ', '_')
        bot.get('https://lichess.org/analysis/standard/{}'.format(fen_new))

    def gameReturn(self):
        bot = self.bot

        fen_return = bot.find_element_by_class_name("analyse__underboard__fen").get_attribute('value')
        time.sleep(1)
        return fen_return

def positionEvaluation(position, piece_values=piece_values, position_values=position_values):
    # Position of pieces is not taken into account for their strength
    if position_values == 'None':
        total_eval = 0
        pieces = list(position.piece_map().values())

        for piece in pieces:
            total_eval += piece_values[str(piece)]

        return total_eval

    else:
        positionTotalEval = 0
        pieces = position.piece_map()

        for j in pieces:
            file = chess.square_file(j)
            rank = chess.square_rank(j)

            piece_type = str(pieces[j])
            positionArray = position_values[piece_type.upper()]

            if piece_type.isupper():
                flippedPositionArray = np.flip(positionArray, axis=0)
                positionTotalEval += piece_values[piece_type] + flippedPositionArray[rank, file]

            else:
                positionTotalEval += piece_values[piece_type] - positionArray[rank, file]

        return positionTotalEval

def minimax(position, depth, alpha, beta, maximizingPlayer, bestMove = 'h1h3'):
    if depth == 0 or position.is_game_over():
        return positionEvaluation(position, piece_values, position_values), bestMove

    if maximizingPlayer:
        maxEval = -np.inf
        for child in [str(i).replace("Move.from_uci(\'", '').replace('\')', '') for i in list(position.legal_moves)]:
            position.push(chess.Move.from_uci(child))
            eval_position = minimax(position, depth-1, alpha, beta, False)[0]
            position.pop()
            maxEval = np.maximum(maxEval, eval_position)
            alpha = np.maximum(alpha, eval_position)
            if beta <= alpha:
                break
        return maxEval

    else:
        minEval = np.inf
        minMove = np.inf
        for child in [str(i).replace("Move.from_uci(\'", '').replace('\')', '') for i in list(position.legal_moves)]:
            position.push(chess.Move.from_uci(child))
            eval_position = minimax(position, depth-1, alpha, beta, True)
            position.pop()
            minEval = np.minimum(minEval, eval_position)
            if minEval < minMove:
                minMove = minEval
                bestMin = child

            beta = np.minimum(beta, eval_position)
            if beta <= alpha:
                break

        return minEval, bestMin

# # To check evaluation
# board = chess.Board()
# print(positionEvaluation(board))
# quit()

# Initialize and set up position
lichess = LichessBot('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -')
lichess.initialize()

board = chess.Board()
fen = board.fen()
lichess.gameSelect(fen)

while not board.is_game_over():
    if board.turn == True:
        print('\n[INFO] Your Turn\n=========================')
        fen_new = fen
        while fen_new == fen:
            fen_new = lichess.gameReturn()
        board = chess.Board(fen_new)

    else:
        print('[INFO] AI\'s Turn\n')
        minimaxEval, bestMove = minimax(board, 4, -np.inf, np.inf, False)
        print("AI Evaluation: {}\nAI Best Move: {}".format(minimaxEval, bestMove))
        board.push(chess.Move.from_uci(bestMove))
        print("{}\n=========================".format(board))
        fen = board.fen()
        lichess.gameSelect(fen)

这就是代码的作用:

  • 打开firefox终端并转到lichess.org网站

  • 进入开始象棋位置的分析模式

  • 等待人类玩家移动

  • 将FEN发送到python程序进行移动

  • 应用具有相应深度和位置值的minimax算法评估位置并确定最佳移动

  • 在python程序中执行此操作

  • 获取当前位置的分

  • 在《苔藓分析》中粘贴芬,在棋盘上打出最佳招式


问题:

现在这只允许我玩白色的棋子(计算机算法对黑色棋子有效)。我的问题,虽然这看起来很基本,但我的问题是如何使之在一开始就可以选择哪一方?看起来,minimax算法是针对计算机玩黑色棋子而设计的,我试图调整它的任何尝试都失败了。在


输出:

以下是游戏进行时控制台上的典型输出。 当游戏结束时没有什么特别的事情发生,我计划在以后包括游戏的总结和结果。在

Console output during a game

如图所示,每次移动后,我都要通过打印控制台输出中的电路板设置位置来再次检查移动是否正确注册。在


最后一点:

我知道评估指标,甚至算法的效率可能不是最好的,但是一旦所有的细节都得到了回答,比如问题中的细节,这些都会被调整。在


Tags: fromselfboardnewpiecereturnifbot
1条回答
网友
1楼 · 发布于 2024-05-29 10:19:47

我发现以下方法有效:

def minimax(position, depth, alpha, beta, maximizingPlayer, bestMove = 'h1h3'):
    if depth == 0 or position.is_game_over():
        if (computer == "BLACK"):
            return positionEvaluation(position, piece_values, position_values), bestMove
        else:
            return -1*positionEvaluation(position, piece_values, position_values), bestMove

相关问题 更多 >

    热门问题