Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

188 строки
6.1 KiB

  1. import argparse
  2. import os
  3. import sys
  4. from subprocess import check_call, DEVNULL, STDOUT, CalledProcessError
  5. from shutil import copyfile, rmtree
  6. parser = argparse.ArgumentParser(description='Convert sgf go records into a kifu format.')
  7. parser.add_argument('sgfFile')
  8. parser.add_argument('-se', "--splitevery", dest="step", type=int, default=50)
  9. parser.add_argument('-cn', "--continuousNumbers", action="store_true", dest="cn", default=False)
  10. parser.add_argument('-c', "--compile", action="store_true", dest="c", default=False)
  11. parser.add_argument('-o', "--open", action="store_true", dest="o", default=False)
  12. args = parser.parse_args(sys.argv[1:])
  13. filePath = args.sgfFile
  14. fileBase = ".".join(filePath.split(".")[:-1])
  15. splitBoardAt = [x for x in range(0, 400, args.step)]
  16. with open(filePath, 'r') as myfile:
  17. sgfData = myfile.read().replace("\n", "").split(";")[1:]
  18. sgfData[-1] = sgfData[-1][:-1]
  19. header = sgfData[0]
  20. moves = sgfData[1:]
  21. def get_tag_from_header(tag):
  22. eventIdxStart = header.lower().find(f"{tag.lower()}[")
  23. eventIdxEnd = -1
  24. if eventIdxStart != -1 :
  25. eventIdxEnd = header.find("]", eventIdxStart)
  26. return header[len(tag) + 1 + eventIdxStart:eventIdxEnd]
  27. return ""
  28. def extract_coordinates(move):
  29. try:
  30. firstCoordinate = move[2]
  31. secondCoordinate = ord(move[3])-96
  32. # mirror at x axis so it looks normal
  33. secondCoordinate = parsedHeader["boardSize"] - secondCoordinate + 1
  34. if ord(move[2]) >= ord("i"):
  35. firstCoordinate = chr(ord(firstCoordinate) + 1)
  36. return firstCoordinate, secondCoordinate
  37. except IndexError:
  38. # wierd move
  39. return -1,-1
  40. def generate_title():
  41. out = []
  42. if parsedHeader["event"] != "":
  43. out.extend([parsedHeader["event"], "\\\\"])
  44. out.append(parsedHeader["playerBlack"])
  45. if parsedHeader["rankBlack"] != "":
  46. out.extend(["[", parsedHeader["rankBlack"], "]"])
  47. out.extend([" - ", parsedHeader["playerWhite"]])
  48. if parsedHeader["rankWhite"] != "":
  49. out.extend(["[", parsedHeader["rankWhite"], "]"])
  50. return "".join(out)
  51. def generate_moves():
  52. finished = False
  53. outText = []
  54. for i in range(len(splitBoardAt)-1):
  55. currentSplit = splitBoardAt[i]
  56. nextSplit = splitBoardAt[i+1]
  57. outText.append("\\begin{psgoboard}\n\t")
  58. # old moves
  59. for j in range(currentSplit):
  60. firstCoordinate, secondCoordinate = extract_coordinates(moves[j])
  61. if not (firstCoordinate == -1 or secondCoordinate == -1):
  62. outText.append(f"\\move*{{{firstCoordinate}}}{{{secondCoordinate}}} ")
  63. if j % 5 == 4:
  64. outText.append("\n\t")
  65. elif secondCoordinate < 10: # nice spacing
  66. outText.append(" ")
  67. # new moves
  68. for j in range(nextSplit-currentSplit):
  69. firstCoordinate, secondCoordinate = extract_coordinates(moves[currentSplit+j])
  70. if not (firstCoordinate == -1 or secondCoordinate == -1):
  71. outText.append(f"\\move{{{firstCoordinate}}}{{{secondCoordinate}}} ")
  72. if j % 5 == 4:
  73. outText.append("\n\t")
  74. elif secondCoordinate < 10: # nice spacing
  75. outText.append(" ")
  76. # was it the last move?
  77. if currentSplit+j == len(moves)-1:
  78. finished = True
  79. break
  80. outText.append("\n\\end{psgoboard}\n")
  81. if finished:
  82. break
  83. elif not args.cn:
  84. outText.append("\n\\setcounter{gomove}{0}\n")
  85. return "".join(outText)
  86. parsedHeader = {
  87. "event" : get_tag_from_header("EV"),
  88. "gameName" : get_tag_from_header("GN"),
  89. "date" : get_tag_from_header("DT"),
  90. "boardSize" : int(get_tag_from_header("SZ")),
  91. "playerBlack" : get_tag_from_header("PB"),
  92. "playerWhite" : get_tag_from_header("PW"),
  93. "rankBlack" : get_tag_from_header("BR"),
  94. "rankWhite" : get_tag_from_header("WR"),
  95. "komi" : get_tag_from_header("KM"),
  96. "result" : get_tag_from_header("RE")
  97. }
  98. # title = generate_title()
  99. event = parsedHeader["event"]
  100. date = parsedHeader["date"]
  101. result = parsedHeader["result"]
  102. komi = parsedHeader["komi"]
  103. playerWhite = parsedHeader["playerWhite"]
  104. playerBlack = parsedHeader["playerBlack"]
  105. if parsedHeader["rankBlack"] != "":
  106. playerBlack += f" ({parsedHeader['rankBlack']})"
  107. if parsedHeader["rankWhite"] != "":
  108. playerWhite += f" ({parsedHeader['rankWhite']})"
  109. moves = generate_moves()
  110. outText = f"""
  111. \\documentclass[a4paper]{{article}}
  112. \\usepackage{{psgo}}
  113. \\usepackage[ngerman]{{babel}}
  114. \\usepackage[margin=2cm,nohead]{{geometry}}
  115. \\usepackage{{tabularx}}
  116. \\newcolumntype{{R}}{{>{{\\raggedleft\\arraybackslash}}X}}
  117. \\setgounit{{0.5cm}}
  118. \\setcounter{{gomove}}{{0}}
  119. \\begin{{document}}
  120. \\sffamily
  121. \\def\\arraystretch{{2}}
  122. \\begin{{center}}
  123. \\vspace*{{1cm}}
  124. {{\\Huge {event} \\par}}
  125. \\vspace{{1cm}}
  126. {{\\huge {date} \\par}}
  127. \\vspace{{2cm}}
  128. \\begin{{tabularx}}{{\\textwidth}}{{ R | c | X }}
  129. \\hline
  130. \\stone{{black}} {playerBlack} & \\textbf{{{result}}} & {playerWhite} \\stone{{white}} \\\\\\hline
  131. & {komi} Komi & \\\\\\hline
  132. \\end{{tabularx}}
  133. \\vspace{{3cm}}
  134. {moves}
  135. \\end{{center}}
  136. \\end{{document}}
  137. """
  138. with open(f"{fileBase}.tex", 'w') as outFile:
  139. outFile.write(outText)
  140. # should be compiled to pdf?
  141. if args.c:
  142. try:
  143. os.makedirs(os.path.dirname(f"{fileBase}/"), exist_ok=True)
  144. copyfile(f"{fileBase}.tex", f"{fileBase}/temp.tex")
  145. os.remove(f"{fileBase}.tex") # poor mans `move` that works on win and linux
  146. check_call(['latex', f"temp.tex"],
  147. stdout=DEVNULL, stderr=STDOUT, cwd=f'{fileBase}')
  148. check_call(['dvips', f"temp.dvi", "-P", "pdf"],
  149. stdout=DEVNULL, stderr=STDOUT, cwd=f'{fileBase}')
  150. check_call(['ps2pdf', f"temp.ps"],
  151. stdout=DEVNULL, stderr=STDOUT, cwd=f'{fileBase}')
  152. except CalledProcessError:
  153. print("error")
  154. else:
  155. copyfile(f"{fileBase}/temp.pdf", f"{fileBase}.pdf")
  156. rmtree(f"{fileBase}/")
  157. if args.o:
  158. os.system(f'"{fileBase}.pdf"')