Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slice of a game between two moves #1030

Open
kevinpolisano opened this issue Jul 28, 2023 · 0 comments
Open

Slice of a game between two moves #1030

kevinpolisano opened this issue Jul 28, 2023 · 0 comments

Comments

@kevinpolisano
Copy link

kevinpolisano commented Jul 28, 2023

Hello,

First of all, thank you for this great python library!

My problem is simple: I would like to extract a subgame between the $i$-th move and the $j$-th move.

For example, if the PGN is: 1. e4 e5 2. Nf3 (2. Nc3 d6) 2... d5 3. a4 a5 4. b4 b5, I want to extract a slice between the second move for White (2. Nf3) and the third move for Black (3... a5), so to obtain 2. Nf3 (2. Nc3 d6) 2... d5 3. a4 a5.

I was able to print all the moves in between with a subclass of Visitor (*) but I don't know how to preserve the right numbering? I'm trying to use visit_board and board.fullmove_number but I suppose I need to restore the board as mentioned in the doc:

The board state must be restored before the traversal continues.

How can I fix it?

(*) Actually the code below is not completely satisfying since we could have repeated moves... that's why I'm actually looking for all the moves between two nodes in the main line.

import io
import chess
import chess.pgn

class SubPartVisitor(chess.pgn.BaseVisitor):
    def __init__(self, start_move, end_move):
        self.start_move = start_move
        self.end_move = end_move
        self.moves = []
        self.in_subpart = False
        self.numbering = 0

    def visit_move(self, board, move):
        if not self.in_subpart and move == self.start_move:
            self.in_subpart = True
            self.numbering = 1

        if self.in_subpart:
            move_str = f"{self.numbering}. {board.san(move)}"
            self.moves.append(move_str)

            if move == self.end_move:
                self.in_subpart = False
                return False

    def begin_variation(self):
        self.moves.append("(")

    def end_variation(self):
        self.moves.append(")")

    def visit_board(self, board):
        self.numbering = board.fullmove_number

    def result(self):
        return self.moves

if __name__ == "__main__":
    pgn_text = "1. e4 e5 2. Nf3 (2. Nc3 d6) 2... d5 3. a4 a5 4. b4 b5"
    
    start_move = chess.Move.from_uci("g1f3")  # 2. Nf3
    end_move = chess.Move.from_uci("a7a5")    # 3. a5

    pgn = io.StringIO(pgn_text)
    game = chess.pgn.read_game(pgn)

    subpart_visitor = SubPartVisitor(start_move, end_move)
    game.accept(subpart_visitor)

    moves_in_subpart = subpart_visitor.result()
    print(" ".join(moves_in_subpart))

Output: 1. Nf3 ( 2. Nc3 2. d6 ) 3. d5 3. a4 3. a5

Otherwise I found a way to visit recursively the nodes as follows:

def traverse(node):
     while node:
      board = node.board()
      if len(node.variations)==1:
        if node.turn()==False:
          print(str(board.fullmove_number) + '.')
        print(node.san())
      else:
       print(node.san())
       for child in node.variations[1:]:
        node = node.next()
        board = node.board()
        print(board.fullmove_number)
        print(node.san())
        print("(")
        traverse(child)
        print(")")
      node = node.next()

The structure is OK but I have also a problem with the numbering...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant