1
<line fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1">
    <move m="e4"/>
    <move m="e5"/>
    <move m="d4"/>
    <move m="exd4"/>
    <move m="Qxd4"/>
    <move m="Nc6"/>
    <move m="Qe3"/>

    <line fen="r1bqkbnr/pppp1ppp/2n5/8/4P3/4Q3/PPP2PPP/RNB1KBNR b KQkq - 0 4">
        <move m="Bb4+"/>

        <line fen="r1bqk1nr/pppp1ppp/2n5/8/1b2P3/4Q3/PPP2PPP/RNB1KBNR w KQkq - 0 5">
            <move m="Nc3"/>

            <line fen="r1bqk1nr/pppp1ppp/2n5/8/1b2P3/2N1Q3/PPP2PPP/R1B1KBNR b KQkq - 0 5">
                <move m="Nf6"/>
            </line>

            <move m="Nge7"/>
            <move m="Bd2"/>
            <move m="O-O"/>
            <move m="O-O-O"/>
            <move m="d6"/>
            <move m="Qg3"/>
            <move m="Kh8"/>
            <move m="f4"/>
            <move m="f5"/>
        </line>

        <move m="c3"/>
        <move m="Be7"/>
    </line>

    <line fen="r1bqkbnr/pppp1ppp/2n5/8/4P3/4Q3/PPP2PPP/RNB1KBNR b KQkq - 0 4">
        <move m="g6"/>
    </line>

    <move m="Nf6"/>
</line>
import xml.etree.ElementTree as ET
import chess
import chess.pgn

xml_data = """<line fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1">
    <move m="e4"/>
    <move m="e5"/>
    <move m="d4"/>
    <move m="exd4"/>
    <move m="Qxd4"/>
    <move m="Nc6"/>
    <move m="Qe3"/>

    <line fen="r1bqkbnr/pppp1ppp/2n5/8/4P3/4Q3/PPP2PPP/RNB1KBNR b KQkq - 0 4">
        <move m="Bb4+"/>

        <line fen="r1bqk1nr/pppp1ppp/2n5/8/1b2P3/4Q3/PPP2PPP/RNB1KBNR w KQkq - 0 5">
            <move m="Nc3"/>

            <line fen="r1bqk1nr/pppp1ppp/2n5/8/1b2P3/2N1Q3/PPP2PPP/R1B1KBNR b KQkq - 0 5">
                <move m="Nf6"/>
            </line>

            <move m="Nge7"/>
            <move m="Bd2"/>
            <move m="O-O"/>
            <move m="O-O-O"/>
            <move m="d6"/>
            <move m="Qg3"/>
            <move m="Kh8"/>
            <move m="f4"/>
            <move m="f5"/>
        </line>

        <move m="c3"/>
        <move m="Be7"/>
    </line>

    <line fen="r1bqkbnr/pppp1ppp/2n5/8/4P3/4Q3/PPP2PPP/RNB1KBNR b KQkq - 0 4">
        <move m="g6"/>
    </line>

    <move m="Nf6"/>
</line>
"""

def collect_main_line(xml_elem):
    """Собирает ходы только из высшего уровня <move> — без вложенных <line>."""
    moves = []
    for child in xml_elem:
        if child.tag.lower() == "move":
            m = child.get("m")
            if m:
                moves.append(m)
    return moves


def xml_to_mainline_pgn(root_line):
    # Создаём игру
    game = chess.pgn.Game()

    # Устанавливаем начальную позицию, если указан FEN
    fen = root_line.get("fen")
    if fen:
        board = chess.Board(fen)
        game.setup(board)
    else:
        board = chess.Board()
        game.setup(board)

    moves_san = collect_main_line(root_line)

    node = game

    for san in moves_san:
        move = board.parse_san(san)
        board.push(move)
        node = node.add_main_variation(move)

    return game


def main():
    root = ET.fromstring(xml_data.strip())
    game = xml_to_mainline_pgn(root)

    exporter = chess.pgn.StringExporter(headers=True, variations=False, comments=False)
    pgn_text = game.accept(exporter)

    print(pgn_text)

    with open("main_line_only.pgn", "w", encoding="utf-8") as f:
        f.write(pgn_text)

    print("PGN сохранён в main_line_only.pgn")


if __name__ == "__main__":
    main()

I have an XML-like structure that describes a chess line with nested <move> and <line> tags.

I’m trying to convert this structure into a PGN file using the python-chess library.

I already know how to add the mainline moves to a chess.pgn.Game(), but I can’t figure out how to correctly add variations and nested sub-variations to the PGN tree.

How do I recursively add moves and variations to python-chess so that each nested <line> becomes a proper PGN variation (i.e., (...))?

Should I manually walk the tree and use node.add_variation(move) for every branch? Or is there a more idiomatic way to create PGN with sub-variations using python-chess?

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.