Python中的深拷贝在极小化函数中。

huangapple go评论76阅读模式
英文:

Python Deep copy in minimax function

问题

I am creating a chess engine in Python by using a minimax algortihm with alpha-beta pruning. It is however very slow at the moment, and I found that doing deepcopy each iteration in minimax is as slow as all my other functions combined.

Is there any way to get around the deepcopy, or to make it faster? Below is my minimax function as of today. It can only think 3-4 moves ahead or so, which doesn't make a very good engine... Any suggestions on speeding the algorithm up is very appreciated.

def minimax(board, depth, alpha, beta, maximizing_player):

    board.is_human_turn = not maximizing_player 

    children = board.get_all_possible_moves()

    if depth == 0 or board.is_draw or board.is_check_mate:
        return None, evaluate(board)

    best_move = random.choice(children)

    if maximizing_player:
        max_eval = -math.inf
        for child in children:
            board_copy = copy.deepcopy(board)
            board_copy.move(child[0][0], child[0][1], child[1][0], child[1][1])
            current_eval = minimax(board_copy, depth - 1, alpha, beta, False)[1]
            if current_eval > max_eval:
                max_eval = current_eval
                best_move = child
            alpha = max(alpha, current_eval)
            if beta <= alpha:
                break
        return best_move, max_eval

    else:
        min_eval = math.inf
        for child in children:
            board_copy = copy.deepcopy(board)
            board_copy.move(child[0][0], child[0][1], child[1][0], child[1][1])
            current_eval = minimax(board_copy, depth - 1, alpha, beta, True)[1]
            if current_eval < min_eval:
                min_eval = current_eval
                best_move = child
            beta = min(beta, current_eval)
            if beta <= alpha:
                break
        return best_move, min_eval
英文:

I am creating a chess engine in Python by using a minimax algortihm with alpha-beta pruning. It is however very slow at the moment, and I found that doing deepcopy each iteration in minimax is as slow as all my other functions combined.

Is there any way to get around the deepcopy, or to make it faster? Below is my minimax function as of today. It can only think 3-4 moves ahead or so, which doesn't make a very good engine... Any suggestions on speeding the algorithm up is very appreciated.

def minimax(board, depth, alpha, beta, maximizing_player):

    board.is_human_turn = not maximizing_player 

    children = board.get_all_possible_moves()

    if depth == 0 or board.is_draw or board.is_check_mate:
        return None, evaluate(board)

    best_move = random.choice(children)

    if maximizing_player:
        max_eval = -math.inf
        for child in children:
            board_copy = copy.deepcopy(board)
            board_copy.move(child[0][0], child[0][1], child[1][0], child[1][1])
            current_eval = minimax(board_copy, depth - 1, alpha, beta, False)[1]
            if current_eval &gt; max_eval:
                max_eval = current_eval
                best_move = child
            alpha = max(alpha, current_eval)
            if beta &lt;= alpha:
                break
        return best_move, max_eval

    else:
        min_eval = math.inf
        for child in children:
            board_copy = copy.deepcopy(board)
            board_copy.move(child[0][0], child[0][1], child[1][0], child[1][1])
            current_eval = minimax(board_copy, depth - 1, alpha, beta, True)[1]
            if current_eval &lt; min_eval:
                min_eval = current_eval
                best_move = child
            beta = min(beta, current_eval)
            if beta &lt;= alpha:
                break
        return best_move, min_eval

答案1

得分: 2

以下是翻译好的内容:

  1. minimax函数中,首先执行检查if depth == 0 or board.is_draw or board.is_check_mate。目前你调用了board.get_all_possible_moves(),这可能是多余的(例如,在depth == 0的情况下)。

  2. 我不清楚get_all_possible_moves()方法是如何实现的,我假设它不进行任何排序。对于极小化算法,有一个好的实践是对移动进行排序,这样你可以从最好的移动开始循环,直到最差的(在这种情况下,你可能会修剪更多的节点并加速程序)。

  3. for child in children循环中的child变量是一个二维矩阵。我猜想board也是一个多维数组。多维数组可能比一维数组慢,因为它们的内存布局(例如,如果你按列迭代它们)。如果可能的话,使用一维数组(例如,你可以将二维数组表示为一维数组的“串联”)。

  4. 使用生成器进行惰性评估。例如,你可以将get_all_possible_moves()变成一个生成器,并在不创建列表和消耗额外内存的情况下进行迭代。如果alpha/beta修剪条件早早触发,你就不需要扩展子节点列表。

  5. 通过制作和取消当前移动来避免深度复制棋盘。在这种情况下,你不会创建棋盘的副本,而是重复使用原始棋盘,这可能会更快:

current_move_info = ... # 收集和存储执行/取消当前移动所需的信息

board.move(current_move_info)

current_eval = minimax(board, ...)[1]

board.unmake_move(current_move_info) # 通过撤销上一步移动来还原棋盘位置
  1. 添加更多经典的优化国际象棋引擎功能,如迭代加深、主要变异搜索、置换表、位棋盘等。
英文:

Some ideas on how to optimize your program (in no particular order):

  1. Make the check if depth == 0 or board.is_draw or board.is_check_mate first thing you do in the minimax function. Right now you call board.get_all_possible_moves() which might be redundant (e.g. in the case depth == 0).

  2. I don't see how the get_all_possible_moves() method is implemented and assume that it doesn't do any kind of sorting. It's a good practice to order moves for minimax algorithm so that you loop over them starting from the best one to the worst (in this case you are likely to prune more nodes and speed up the program).

  3. The child variable in the for child in children loop is a two-dimensional matrix. I also guess that board is a multi-dimensional array as well. Multi-dimensional arrays can be slower than one-dimensional because of their memory layout (e.g. if you iterate over them column-wise). Use one-dimensional arrays if possible (e.g. you can represent a two-dim array as "concatenation" of one-dim arrays).

  4. Use generators for lazy evaluation. For instance, you can turn your get_all_possible_moves() into a generator and iterate over it without creating lists and consuming extra memory. If the alpha/beta pruning condition triggers early, you won't need to expand the whole list of children in the position.

  5. Avoid deepcopying of the board by making and unmaking the current move. In this case you don't create copies of the board but reuse the original board which might be faster:

current_move_info = ... # collect and store info necessary to make/unmake the current move

board.move(current_move_info)

current_eval = minimax(board, ...)[1]

board.unmake_move(current_move_info) # restore the board position by undoing the last move

  1. Add more classical optimizing chess-engine features like iterative deepening, principal variation search, transposition tables, bitboards etc.

huangapple
  • 本文由 发表于 2020年1月6日 15:45:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/59608390.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定