mattheww / sgfmill

Python library for Smart Game Format (SGF) files
http://mjw.woodcraft.me.uk/sgfmill/
68 stars 12 forks source link

Is it possible to read the comments in SGF files with Python? #6

Open phdsmhong opened 2 years ago

phdsmhong commented 2 years ago

Hello,

Thank you very much for creating this sgfmill module. This is extremely useful for researchers. I deeply appreciate it.

I have a quick question. I am trying to read the comments written in SGF files with Python, and I wonder whether sgfmill can help. Any advice would be greatly appreciated! Thank you for your attention.

gjask commented 2 years ago

I am not author of this library, but I'll try to answer the question at least to best of my knowledge anyway.

In short? Yes, you sure can.

Long answer kinda depends on what exactly you want to do. You probably know that SGF files store game records as a tree structure (it can contain multiple variants of single game or even multiple games). And any part of information (which move got played, comments or any other meta-data) is always stored in some node of this tree. Sgfmill library can parse SGF file and build object representation of game tree for you. What you do with this tree next is up to you.

So which comments do you want to read? All stored in file? Just those in the main branch of game tree? Or some particular nodes? I'll try write simple example which prints all comments in main branch.

from sgfmill.sgf import Sgf_game

# Read SGF file and parse it with Sgf_game class
with open("ff4_ex.sgf", "rb") as file:
    game = Sgf_game.from_bytes(file.read())

# We start searching for comments from root node.
node = game.root
# Also we remember number of move we are currently reading
move_number = 0

while True:
    # If current node has move stored in it we increase number of moves we passed by
    if node.get_move() != (None, None):
        move_number += 1

    # If current node has comment stored, we print it along with current move number
    if node.has_property("C"):
        comment = node.get("C")
        print(f"Move {move_number}: {comment}")

    # If current node has no children we reached end of main branch, so we stop looking
    if not len(node):
        break

    # Lets point our looks onto first child node of current node
    node = node[0]
phdsmhong commented 2 years ago

@gjask Your advice is extremely helpful. Thank you for your help. I want to read only the comments in the main branch (in fact, my sgf files have only one branch). So far, I managed to read the stone positions using the following snippet, but I am having a hard time combining stone positions with comments. Can you please advise? Any help would be greatly appreciated.

def data_point(board, move, color, COMMENTS WILL COME HERE):
    board_array = torch.zeros((1, BOARD_SIZE, BOARD_SIZE), dtype=torch.float32, device=device)
    for p in board.list_occupied_points():
        board_array[0, p[1][0], p[1][1]] = -1.0 + 2 * int(p[0] == color)
    return board_array, COMMENTS WILL COME HERE

def make_data_points(game_files):
    data_points = []
    for i, game_file in enumerate(game_files):
        with open(game_file) as f:
            contents = f.read().encode('ascii')
            game = sgf.Sgf_game.from_bytes(contents)
            board, plays = sgf_moves.get_setup_and_moves(game)       
            for color, move in plays:
                if move is None: continue
                row, col = move
                tp = data_point(board, move, color, COMMENTS WILL COME HERE)
                data_points.append(tp)
                board.play(row, col, color)
    return data_points
gjask commented 2 years ago

I still am not able to fully understand your use-case. But I think there may be an issue with your approach. The issue I see lays in sgf_moves.get_setup_stones_and_moves() function, which completely disregards any node properties that do not directly affect position and that also means the comments. As you probably need comments in relation to corresponding position I suggest to re-implement said function in a way that it returns moves and comments. If you look up its code, you can easily build on that.