使用BFS算法和字典数据结构自行构建的国际象棋残局引擎

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

我正在使用 ChatGPT4 为下面所述的问题创建国际象棋代码, 但大多数情况下,我没有获得任何超出“深度 0 到深度 19”的搜索位置数量的结果。问题绝对是内存方面可以管理的。简短的描述是这样的:我的 ASCII 板上有很少的几块,D=3,4 或 5。我想写一个代码 它将所有可到达的位置放入字典中,称之为 A。现在我想为 A 中的每个键分配一个(小)自然数,0 表示黑色或白色的将死,以及第二个参数父级,所以我应该结束像这样的东西:[initial_fen,None,None]。 这里第一个 None 表示移动次数尚未确定。 第二个 None 意味着initial_fen 字符串的父项不存在(并且在其余的计算中不会存在,这是一个独特的例外,A 中的所有其他键都将有一个父项)然后,向上一点,我想为 A 中的所有键分配 1,这样有问题的玩家对具有标签的其他玩家有一个唯一的合法移动,即第二个条目,等于 0。然后,继续向上,我想应用 any()all() 递归到所有其他位置的逻辑,最终产生initial_fen 的标签,以及 导致将死或平局的路径,理论上对双方来说都是尽可能最佳的。有人能发现其中的错误吗?

import chess

def simplify_fen(fen):
    parts = fen.split(' ')
    return ' '.join(parts[:4])


def evaluate_position(board):
    if board.is_checkmate():
        return 1 if board.turn else -1  # 1 for White win, -1 for Black win
    elif board.is_stalemate() or board.is_insufficient_material():
        return 0  # Draw
    return None  # Game not ended

def assign_labels_and_parents(reachable_positions, board, depth):
    for fen in reachable_positions:
        board.set_fen(fen)
        result = evaluate_position(board)

        if result is not None:
            reachable_positions[fen]['label'] = result
        elif depth > 0:
            child_positions = generate_reachable_positions(board, depth - 1)
            if board.turn:  # White's turn, use 'any' logic
                reachable_positions[fen]['label'] = any(
                    child_positions[child_fen]['label'] == 1 for child_fen in child_positions)
            else:  # Black's turn, use 'all' logic
                reachable_positions[fen]['label'] = all(
                    child_positions[child_fen]['label'] == -1 for child_fen in child_positions)

def generate_reachable_positions(board, depth):
    reachable_positions = {}
    for move in board.legal_moves:
        board.push(move)
        fen = board.fen()
        reachable_positions[fen] = {'label': None, 'parent': board.fen() if board.move_stack else None}
        board.pop()
    assign_labels_and_parents(reachable_positions, board, depth)
    return reachable_positions

def main():
    initial_fen = "startpos"  # Replace with your specific starting position
    initial_fen = "8/8/8/8/8/7Q/3K4/k7 w - - 0 1"

    board = chess.Board(fen=initial_fen)
    depth = 5  # Modify as needed

    reachable_positions = generate_reachable_positions(board, depth)

    # Display the results
    for fen, data in reachable_positions.items():
        print(fen, data)

if __name__ == "__main__":
    main()

但是我明白了

7Q/8/8/8/8/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '7Q/8/8/8/8/8/3K4/k7 b - - 1 1'}
2Q5/8/8/8/8/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '2Q5/8/8/8/8/8/3K4/k7 b - - 1 1'}
8/7Q/8/8/8/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/7Q/8/8/8/8/3K4/k7 b - - 1 1'}
8/3Q4/8/8/8/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/3Q4/8/8/8/8/3K4/k7 b - - 1 1'}
8/8/7Q/8/8/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/7Q/8/8/8/3K4/k7 b - - 1 1'}
8/8/4Q3/8/8/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/4Q3/8/8/8/3K4/k7 b - - 1 1'}
8/8/8/7Q/8/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/7Q/8/8/3K4/k7 b - - 1 1'}
8/8/8/5Q2/8/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/5Q2/8/8/3K4/k7 b - - 1 1'}
8/8/8/8/7Q/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/7Q/8/3K4/k7 b - - 1 1'}
8/8/8/8/6Q1/8/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/6Q1/8/3K4/k7 b - - 1 1'}
8/8/8/8/8/6Q1/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/6Q1/3K4/k7 b - - 1 1'}
8/8/8/8/8/5Q2/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/5Q2/3K4/k7 b - - 1 1'}
8/8/8/8/8/4Q3/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/4Q3/3K4/k7 b - - 1 1'}
8/8/8/8/8/3Q4/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/3Q4/3K4/k7 b - - 1 1'}
8/8/8/8/8/2Q5/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/2Q5/3K4/k7 b - - 1 1'}
8/8/8/8/8/1Q6/3K4/k7 b - - 1 1 {'label': 0, 'parent': '8/8/8/8/8/1Q6/3K4/k7 b - - 1 1'}
8/8/8/8/8/Q7/3K4/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/Q7/3K4/k7 b - - 1 1'}
8/8/8/8/8/8/3K3Q/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/8/3K3Q/k7 b - - 1 1'}
8/8/8/8/8/8/3K2Q1/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/8/3K2Q1/k7 b - - 1 1'}
8/8/8/8/8/8/3K4/k6Q b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/8/3K4/k6Q b - - 1 1'}
8/8/8/8/8/8/3K4/k4Q2 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/8/3K4/k4Q2 b - - 1 1'}
8/8/8/8/8/4K2Q/8/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/4K2Q/8/k7 b - - 1 1'}
8/8/8/8/8/3K3Q/8/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/3K3Q/8/k7 b - - 1 1'}
8/8/8/8/8/2K4Q/8/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/2K4Q/8/k7 b - - 1 1'}
8/8/8/8/8/7Q/4K3/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/7Q/4K3/k7 b - - 1 1'}
8/8/8/8/8/7Q/2K5/k7 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/7Q/2K5/k7 b - - 1 1'}
8/8/8/8/8/7Q/8/k3K3 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/7Q/8/k3K3 b - - 1 1'}
8/8/8/8/8/7Q/8/k2K4 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/7Q/8/k2K4 b - - 1 1'}
8/8/8/8/8/7Q/8/k1K5 b - - 1 1 {'label': False, 'parent': '8/8/8/8/8/7Q/8/k1K5 b - - 1 1'}
python-3.x dictionary breadth-first-search python-chess
1个回答
0
投票

好的,但这只是部分答案,因为我不使用或不认识该编程语言,而且我无法确定我的语法是否正确,更不用说测试它了。这些改变可能会纠正一些错误:

def generate_reachable_positions(board, depth):
reachable_positions = {}
parent = board.fen()
for move in board.legal_moves:
    board.push(move)
    fen = board.fen()
    reachable_positions[fen] = {'label': None, 'parent': parent if board.move_stack else None}
    board.pop()
assign_labels_and_parents(reachable_positions, board, depth)
return reachable_positions

在进行每次移动之前保存父节点的 FEN 表示应该会导致表自一致。

这应该会导致父节点在其每个子节点中正确显示。然后你需要看看它如何执行黑棋(它应该更喜欢延迟交配时间最长的动作)。

我强烈建议您对棋子受阻的仓位进行一些手动测试,并且出于测试目的,非常很少有合法的动作。在解决更广泛的问题之前,您需要能够在心里检查它是否正常工作。您还需要验证它至少可以正常运行 3 或 4 层,然后才值得让它运行完成。即使对于 3^N 或 4^N 的情况,手动计算移动树也会在第 3 层达到 27 或 64 个终端节点。对于高于 ~5 的分支因子,手动检查功能是很困难的。

最终我认为您会希望该表由位置的快速散列索引,并且每个父级仅存储获胜或最大延迟损失移动(从终端节点返回)并可能移动到获胜/失败/平局。对于更困难的问题,您当前的数据结构将太大且缓慢。

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