niklasf / python-chess

A chess library for Python, with move generation and validation, PGN parsing and writing, Polyglot opening book reading, Gaviota tablebase probing, Syzygy tablebase probing, and UCI/XBoard engine communication
https://python-chess.readthedocs.io/en/latest/
GNU General Public License v3.0
2.42k stars 529 forks source link

parse the variations #453

Closed cyrenaique closed 4 years ago

cyrenaique commented 4 years ago

Hello, I am trying to go through all the variations in one game in order to build a graph, but I have to admit that I didn't understand the philosophy of the code and I can't go though. Any advices would be appreciated. Thanks for this nice toolbox otherwise!

niklasf commented 4 years ago

Here's an example that recursively enumerates all variations:

import sys
import chess.pgn

def traverse(node, depth):
    print("  " * depth, node.move or "<root>")
    for child in node.variations:
        traverse(child, depth + 1)

game = chess.pgn.read_game(open(sys.argv[1]))
traverse(game, 0)

See https://python-chess.readthedocs.io/en/latest/pgn.html#chess.pgn.GameNode for the data available on each node.

cyrenaique commented 4 years ago

Big Thanks!! I would never figure out the recursive function... Thanks again...

cyrenaique commented 4 years ago

still struggling, trying to use anytree but really don't know how I can create the tree dynamically with it based on your code. Either I have algorithms lack of knowledges (clearly possible) or this lib doesn't fit this purpose, any advice would be appreciated....thanks again.

niklasf commented 4 years ago

An example:

import sys
import chess.pgn

from anytree import Node, RenderTree

def traverse(game_node, tree_parent=None):
    name = game_node.move.uci() if game_node.move else "<root>"
    tree_node = Node(name, parent=tree_parent)

    for game_node in game_node.variations:
        traverse(game_node, tree_node)

    return tree_node

game = chess.pgn.read_game(open(sys.argv[1]))
tree = traverse(game)

for pre, fill, node in RenderTree(tree):
    print("{}{}".format(pre, node.name))

Sample output:

<root>
└── e2e4
    └── c7c5
        └── c2c4
            └── b8c6
                └── g1e2
                    └── g8f6
                        └── b1c3
                            └── c6b4
                                └── g2g3
                                    └── b4d3

(It is also possible to do this more efficiently by using visitors to read the game directly into a tree. But the algorithm above might be easier to follow.)

cyrenaique commented 4 years ago

Oh man!!! I was not far considering the astronomical unit...Thanks again. Might be interesting to add this function in the lib :-)

cyrenaique commented 4 years ago

I ended up added: if depth%2!=0: name = str(int((depth/2)+.5))+' '+str(game_node.move) if game_node.move else ""
if depth%2==0: name = str(int((depth)/2))+'...'+str(game_node.move) if game_node.move else "" otherwise the dotexporter to image render a cyclic graph.... Checking now if I can add evaluation present in comments next to the node