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.4k stars 521 forks source link

[[ Feature / Script Request ]] #586

Closed glennsamuel32 closed 4 years ago

glennsamuel32 commented 4 years ago

Hello, I was wondering if there was a script that can detect the number of pieces on the board (user defined), and then delete all the remaining moves after that.

For example, this is the original pgn...

[Event "5 min, rated"]
[Site "Riom Engine"]
[Date "2018.12.31"]
[Round "?"]
[White "Leönidas, Stockfish 291218 64"]
[Black "Sarissa"]
[Result "1/2-1/2"]
[ECO "A28"]
[WhiteElo "2594"]
[BlackElo "2547"]
[Annotator "EPD 863 found after 164...Kc4"]
[PlyCount "328"]
[EventDate "2018.12.31"]
[EventType "blitz"]

1. c4 e5 2. Nc3 Nf6 3. Nf3 Nc6 4. e4 Bb4 5. d3 d6 6. a3 Bc5 7. b4 Bb6 8. Be3
Bxe3 9. fxe3 Ne7 10. Be2 O-O 11. O-O Ng6 12. Qe1 c6 13. a4 Be6 14. Rb1 Rc8 15.
Ng5 Bd7 16. Qg3 Qe7 17. Nf3 Nh5 18. Qg5 Nf6 19. Nh4 Nxh4 20. Qxh4 b5 21. axb5
cxb5 22. Qe1 Rc7 23. Qd2 Rfc8 24. Ra1 bxc4 25. dxc4 Be6 26. Nd5 Bxd5 27. exd5
Rb7 28. Rfc1 Ne4 29. Qe1 Rcb8 30. Ra4 Qd7 31. Bd1 h6 32. h3 Rc7 33. Ra5 Qd8 34.
Ra4 Qd7 35. Raa1 Rcb7 36. Bc2 Nf6 37. Ra4 Qc7 38. Bf5 g6 39. Bd3 Kg7 40. Rca1
Nd7 41. Be2 Nf6 42. Qd2 h5 43. Kh2 Qe7 44. Qc3 h4 45. Qe1 Nd7 46. Bf1 a6 47.
Qc3 Nf6 48. Qe1 Nh5 49. Rc1 Qd7 50. Rxa6 Rxb4 51. Qxh4 Nf6 52. Bd3 Qe7 53. e4
Rb3 54. Rd1 Rh8 55. Qe1 Qb7 56. Ra5 Qc7 57. Rb5 Ra3 58. Qe2 Nh5 59. Rf1 Rha8
60. Qd2 Nf4 61. Be2 Qe7 62. Rb2 Qh4 63. Bf3 R8a7 64. Rc2 R7a4 65. Qe1 Qxe1 66.
Rxe1 Rb3 67. h4 Raa3 68. Rf1 Nd3 69. h5 Nc5 70. hxg6 fxg6 71. Rh1 Kf6 72. Re2
Kg5 73. Kg3 Rb4 74. Rh8 Rxc4 75. Kh2 Rca4 76. Rd8 Ra6 77. Kg3 Rb3 78. Kf2 Nd3+
79. Kg3 Nf4 80. Rc2 Nd3 81. Re2 Nc5 82. Kh3 Nb7 83. Rf8 Nc5 84. Rd8 Rab6 85.
Kg3 R3b4 86. Re3 Nb3 87. Kh2 Nc5 88. Re2 Kf6 89. Re3 Kg5 90. Kg3 Rb2 91. Rf8
Ra6 92. Bd1 Raa2 93. Bf3 Rb4 94. Rd8 Rb6 95. Rf8 Rb4 96. Rd8 Rb6 97. Bd1 Rbb2
98. Bf3 Ra6 99. Bd1 Rb4 100. Bf3 Nb3 101. Be2 Rab6 102. Rf8 Nc5 103. Bf3 Nb3
104. Re1 Nc5 105. Rh8 Rb3 106. Rd8 R6b4 107. Kh2 Rb6 108. Kg3 Rd3 109. Kf2 Ra3
110. Re2 Ra1 111. Kg3 Nd3 112. Re8 Nc5 113. Rd8 Rab1 114. Re3 Rh1 115. Rg8 Rhb1
116. Rh8 R1b2 117. Re1 R6b4 118. Rd8 Nb7 119. Rg8 Nc5 120. Rd8 Nb7 121. Rf8
R2b3 122. Rh8 Nc5 123. Kh2 Rb1 124. Rxb1 Rxb1 125. Rd8 Nb7 126. Rb8 Kf4 127.
Rg8 Nc5 128. Rxg6 Rb6 129. Rg8 Nb3 130. Rf8+ Ke3 131. Ra8 Nc5 132. Ra3+ Kd4
133. g4 Rb7 134. Kh3 Rh7+ 135. Kg2 Rg7 136. Kf1 Rh7 137. Kg2 Rh8 138. Kf1 Rb8
139. Kg2 Rg8 140. Kf1 Rh8 141. Kg2 Rg8 142. Kh3 Rh8+ 143. Kg2 Rh7 144. Ra8 Rf7
145. Kg3 Nxe4+ 146. Bxe4 Kxe4 147. g5 Rf1 148. Kg4 Kxd5 149. g6 e4 150. Re8 e3
151. Rxe3 Rf8 152. g7 Rg8 153. Re1 Rxg7+ 154. Kf4 Rf7+ 155. Kg3 Kc6 156. Rc1+
Kb6 157. Rb1+ Kc5 158. Rc1+ Kb4 159. Rd1 Rf6 160. Kg4 Kc5 161. Rc1+ Kd4 162.
Rd1+ Kc5 163. Rc1+ Kb4 164. Rb1+ Kc4 1/2-1/2

So if define 8 pieces left, all remaining moves will be deleted and the new pgn will be...

[Event "5 min, rated"]
[Site "Riom Engine"]
[Date "2018.12.31"]
[Round "?"]
[White "Leönidas, Stockfish 291218 64"]
[Black "Sarissa"]
[Result "1/2-1/2"]
[ECO "A28"]
[WhiteElo "2594"]
[BlackElo "2547"]
[Annotator "EPD 863 found after 164...Kc4"]
[PlyCount "292"]
[EventDate "2018.12.31"]
[EventType "blitz"]

1. c4 e5 2. Nc3 Nf6 3. Nf3 Nc6 4. e4 Bb4 5. d3 d6 6. a3 Bc5 7. b4 Bb6 8. Be3
Bxe3 9. fxe3 Ne7 10. Be2 O-O 11. O-O Ng6 12. Qe1 c6 13. a4 Be6 14. Rb1 Rc8 15.
Ng5 Bd7 16. Qg3 Qe7 17. Nf3 Nh5 18. Qg5 Nf6 19. Nh4 Nxh4 20. Qxh4 b5 21. axb5
cxb5 22. Qe1 Rc7 23. Qd2 Rfc8 24. Ra1 bxc4 25. dxc4 Be6 26. Nd5 Bxd5 27. exd5
Rb7 28. Rfc1 Ne4 29. Qe1 Rcb8 30. Ra4 Qd7 31. Bd1 h6 32. h3 Rc7 33. Ra5 Qd8 34.
Ra4 Qd7 35. Raa1 Rcb7 36. Bc2 Nf6 37. Ra4 Qc7 38. Bf5 g6 39. Bd3 Kg7 40. Rca1
Nd7 41. Be2 Nf6 42. Qd2 h5 43. Kh2 Qe7 44. Qc3 h4 45. Qe1 Nd7 46. Bf1 a6 47.
Qc3 Nf6 48. Qe1 Nh5 49. Rc1 Qd7 50. Rxa6 Rxb4 51. Qxh4 Nf6 52. Bd3 Qe7 53. e4
Rb3 54. Rd1 Rh8 55. Qe1 Qb7 56. Ra5 Qc7 57. Rb5 Ra3 58. Qe2 Nh5 59. Rf1 Rha8
60. Qd2 Nf4 61. Be2 Qe7 62. Rb2 Qh4 63. Bf3 R8a7 64. Rc2 R7a4 65. Qe1 Qxe1 66.
Rxe1 Rb3 67. h4 Raa3 68. Rf1 Nd3 69. h5 Nc5 70. hxg6 fxg6 71. Rh1 Kf6 72. Re2
Kg5 73. Kg3 Rb4 74. Rh8 Rxc4 75. Kh2 Rca4 76. Rd8 Ra6 77. Kg3 Rb3 78. Kf2 Nd3+
79. Kg3 Nf4 80. Rc2 Nd3 81. Re2 Nc5 82. Kh3 Nb7 83. Rf8 Nc5 84. Rd8 Rab6 85.
Kg3 R3b4 86. Re3 Nb3 87. Kh2 Nc5 88. Re2 Kf6 89. Re3 Kg5 90. Kg3 Rb2 91. Rf8
Ra6 92. Bd1 Raa2 93. Bf3 Rb4 94. Rd8 Rb6 95. Rf8 Rb4 96. Rd8 Rb6 97. Bd1 Rbb2
98. Bf3 Ra6 99. Bd1 Rb4 100. Bf3 Nb3 101. Be2 Rab6 102. Rf8 Nc5 103. Bf3 Nb3
104. Re1 Nc5 105. Rh8 Rb3 106. Rd8 R6b4 107. Kh2 Rb6 108. Kg3 Rd3 109. Kf2 Ra3
110. Re2 Ra1 111. Kg3 Nd3 112. Re8 Nc5 113. Rd8 Rab1 114. Re3 Rh1 115. Rg8 Rhb1
116. Rh8 R1b2 117. Re1 R6b4 118. Rd8 Nb7 119. Rg8 Nc5 120. Rd8 Nb7 121. Rf8
R2b3 122. Rh8 Nc5 123. Kh2 Rb1 124. Rxb1 Rxb1 125. Rd8 Nb7 126. Rb8 Kf4 127.
Rg8 Nc5 128. Rxg6 Rb6 129. Rg8 Nb3 130. Rf8+ Ke3 131. Ra8 Nc5 132. Ra3+ Kd4
133. g4 Rb7 134. Kh3 Rh7+ 135. Kg2 Rg7 136. Kf1 Rh7 137. Kg2 Rh8 138. Kf1 Rb8
139. Kg2 Rg8 140. Kf1 Rh8 141. Kg2 Rg8 142. Kh3 Rh8+ 143. Kg2 Rh7 144. Ra8 Rf7
145. Kg3 Nxe4+ 146. Bxe4 Kxe4 1/2-1/2

Thanks a lot !!

Regards, Glenn

PedanticHacker commented 4 years ago

Your PGN format is very strange. The move numbers are (for example, in the original PGN and looking at the bottom of the first section of it) 59. Rf1 Rha8, and then a new section starts as 2. Qd2 Nf4. Strange.

Anyway, you might use the pieces() method of chess.Board() to somehow count the pieces on the chessboard. Just note that it takes 2 arguments: piece_type and color.

glennsamuel32 commented 4 years ago

This is actually a very common format...the pgn I posted is from Chessbase... I wish I could translate what you wrote into a py script but I'm not a developer :(:( I appreciate any help on this...

niklasf commented 4 years ago

Here you go. An example for using visitors, basically forwarding everything to FileExporter, unless the limit is hit:

import sys
import chess
import chess.pgn

MIN_PIECES = 8

class MinimumPieceCountExporter(chess.pgn.FileExporter):
    def begin_game(self):
        self.above = True

    def visit_board(self, board):
        self.above = chess.popcount(board.occupied) > MIN_PIECES
        if self.above:
            super().visit_board(board)

    def visit_move(self, board, move):
        if self.above:
            super().visit_move(board, move)

    def visit_comment(self, comment):
        if self.above:
            super().visit_comment(comment)

    def visit_nag(self, nag):
        if self.above:
            super().visit_nag(nag)

    def begin_variation(self):
        if self.above:
            super().begin_variation()

    def end_variation(self):
        if self.above:
            super().end_variation()

    def visit_result(self, result):
        super().visit_result(result if self.above else "*")

    def result(self):
        return True

def main(arg):
    visitor = MinimumPieceCountExporter(sys.stdout)

    with open(arg) as pgn:
        while chess.pgn.read_game(pgn, Visitor=lambda: visitor):
            pass

# Usage: python script.py in.pgn > out.pgn
if __name__ == "__main__":
    for arg in sys.argv[1:]:
        main(arg)