Files
Stockfish/script/pgn_to_plain.py
Stéphane Nicolet 3963e3de55 Clean up previous patch
2021-06-17 16:05:12 +02:00

111 lines
3.3 KiB
Python

import chess.pgn
import argparse
import glob
import re
from typing import List
# todo close in c++ tools using pgn-extract
# https://www.cs.kent.ac.uk/people/staff/djb/pgn-extract/help.html#-w
commentRe = re.compile("([+-]*M*[0-9.]*)/([0-9]*)")
mateRe = re.compile("([+-])M([0-9]*)")
flip_black = False
def parse_result(result_str:str, board:chess.Board) -> int:
if result_str == "1/2-1/2":
return 0
if result_str == "0-1":
if board.turn == chess.WHITE:
return -1
else:
return 1
elif result_str == "1-0":
if board.turn == chess.WHITE:
return 1
else:
return -1
else:
print("illegal result", result_str)
raise ValueError
def game_sanity_check(game: chess.pgn.Game) -> bool:
if not game.headers["Result"] in ["1/2-1/2", "0-1", "1-0"]:
print("invalid result", game.headers["Result"])
return False
return True
def parse_comment_for_score(comment_str: str, board: chess.Board) -> int:
global commentRe
global mateRe
global flip_black
try:
m = commentRe.search(comment_str)
if m:
score = m.group(1)
# depth = int(m.group(2))
m = mateRe.search(score)
if m:
if m.group(1) == "+":
score = 32000 - int(m.group(2))
else:
score = -32000 + int(m.group(2))
else:
score = int(float(score) * 208) # pawn to SF PawnValueEg
if flip_black and board.turn == chess.BLACK:
score = -score
else:
score = 0
except:
score = 0
return score
def parse_game(game: chess.pgn.Game, writer, start_play: int=1)->None:
board: chess.Board = game.board()
if not game_sanity_check(game):
return
result: str = game.headers["Result"]
ply = 0
for node in game.mainline():
move = node.move
if ply >= start_play:
comment: str = node.comment
writer.write("fen " + board.fen() + "\n")
writer.write("move " + str(move) + "\n")
writer.write("score " + str(parse_comment_for_score(comment, board)) + "\n")
writer.write("ply " + str(ply)+"\n")
writer.write("result " + str(parse_result(result, board)) +"\n")
writer.write("e\n")
ply += 1
board.push(move)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--pgn", type=str, required=True)
parser.add_argument("--start_ply", type=int, default=1)
parser.add_argument("--output", type=str, default="plain.txt")
parser.add_argument("--flip_black_score", action='store_true', dest='flip_black_score', help="Flip black score. Default: False")
args = parser.parse_args()
global flip_black
flip_black = args.flip_black_score
pgn_files: List[str] = glob.glob(args.pgn)
pgn_files = sorted(pgn_files, key=lambda x:float(re.findall("-(\d+).pgn",x)[0] if re.findall("-(\d+).pgn",x) else 0.0))
f = open(args.output, 'w')
for pgn_file in pgn_files:
print("parse", pgn_file)
pgn_loader = open(pgn_file)
while True:
game = chess.pgn.read_game(pgn_loader)
if game is None:
break
parse_game(game, f, args.start_ply)
f.close()
if __name__=="__main__":
main()