Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

199 рядки
6.5 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('-t', "--texOnly", action="store_true", dest="t", 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 format_date(date):
  22. months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
  23. ds = date.split("-") # year month day
  24. ds.reverse()
  25. ds[1] = months[int(ds[1])-1]
  26. return " ".join(ds)
  27. def get_tag_from_header(tag):
  28. eventIdxStart = header.lower().find(f"{tag.lower()}[")
  29. eventIdxEnd = -1
  30. if eventIdxStart != -1 :
  31. eventIdxEnd = header.find("]", eventIdxStart)
  32. return header[len(tag) + 1 + eventIdxStart:eventIdxEnd]
  33. return ""
  34. def extract_coordinates(move):
  35. try:
  36. firstCoordinate = move[2]
  37. secondCoordinate = ord(move[3])-96
  38. # mirror at x axis so it looks normal
  39. secondCoordinate = parsedHeader["boardSize"] - secondCoordinate + 1
  40. if ord(move[2]) >= ord("i"):
  41. firstCoordinate = chr(ord(firstCoordinate) + 1)
  42. return firstCoordinate, secondCoordinate
  43. except IndexError:
  44. # wierd move
  45. return -1,-1
  46. def generate_title():
  47. out = []
  48. if parsedHeader["event"] != "":
  49. out.extend([parsedHeader["event"], "\\\\"])
  50. out.append(parsedHeader["playerBlack"])
  51. if parsedHeader["rankBlack"] != "":
  52. out.extend(["[", parsedHeader["rankBlack"], "]"])
  53. out.extend([" - ", parsedHeader["playerWhite"]])
  54. if parsedHeader["rankWhite"] != "":
  55. out.extend(["[", parsedHeader["rankWhite"], "]"])
  56. return "".join(out)
  57. def generate_moves():
  58. finished = False
  59. outText = []
  60. for i in range(len(splitBoardAt)-1):
  61. currentSplit = splitBoardAt[i]
  62. nextSplit = splitBoardAt[i+1]
  63. outText.append("\\begin{psgoboard}\n\t")
  64. # old moves
  65. for j in range(currentSplit):
  66. firstCoordinate, secondCoordinate = extract_coordinates(moves[j])
  67. if not (firstCoordinate == -1 or secondCoordinate == -1):
  68. outText.append(f"\\move*{{{firstCoordinate}}}{{{secondCoordinate}}} ")
  69. if j % 5 == 4:
  70. outText.append("\n\t")
  71. elif secondCoordinate < 10: # nice spacing
  72. outText.append(" ")
  73. # new moves
  74. for j in range(nextSplit-currentSplit):
  75. firstCoordinate, secondCoordinate = extract_coordinates(moves[currentSplit+j])
  76. if not (firstCoordinate == -1 or secondCoordinate == -1):
  77. outText.append(f"\\move{{{firstCoordinate}}}{{{secondCoordinate}}} ")
  78. if j % 5 == 4:
  79. outText.append("\n\t")
  80. elif secondCoordinate < 10: # nice spacing
  81. outText.append(" ")
  82. # was it the last move?
  83. if currentSplit+j == len(moves)-1:
  84. finished = True
  85. break
  86. outText.append("\n\\end{psgoboard}\n")
  87. if finished:
  88. break
  89. elif not args.cn:
  90. outText.append("\n\\setcounter{gomove}{0}\n")
  91. return "".join(outText)
  92. parsedHeader = {
  93. "event" : get_tag_from_header("EV"),
  94. "gameName" : get_tag_from_header("GN"),
  95. "date" : get_tag_from_header("DT"),
  96. "boardSize" : int(get_tag_from_header("SZ")),
  97. "playerBlack" : get_tag_from_header("PB"),
  98. "playerWhite" : get_tag_from_header("PW"),
  99. "rankBlack" : get_tag_from_header("BR"),
  100. "rankWhite" : get_tag_from_header("WR"),
  101. "komi" : get_tag_from_header("KM"),
  102. "result" : get_tag_from_header("RE")
  103. }
  104. # title = generate_title()
  105. event = parsedHeader["event"]
  106. gameName = parsedHeader["gameName"]
  107. date = format_date(parsedHeader["date"])
  108. result = parsedHeader["result"]
  109. komi = parsedHeader["komi"]
  110. playerWhite = parsedHeader["playerWhite"]
  111. playerBlack = parsedHeader["playerBlack"]
  112. if parsedHeader["rankBlack"] != "":
  113. playerBlack += f" ({parsedHeader['rankBlack']})"
  114. if parsedHeader["rankWhite"] != "":
  115. playerWhite += f" ({parsedHeader['rankWhite']})"
  116. moves = generate_moves()
  117. outText = f"""
  118. \\documentclass[a4paper]{{article}}
  119. \\usepackage{{psgo}}
  120. \\usepackage[ngerman]{{babel}}
  121. \\usepackage[margin=2cm,nohead]{{geometry}}
  122. \\usepackage{{tabularx}}
  123. \\newcolumntype{{R}}{{>{{\\raggedleft\\arraybackslash}}X}}
  124. \\setgounit{{0.5cm}}
  125. \\setcounter{{gomove}}{{0}}
  126. \\begin{{document}}
  127. \\sffamily
  128. \\def\\arraystretch{{2}}
  129. \\begin{{center}}
  130. \\vspace*{{1cm}}
  131. {{\\Huge {event} \\par}}
  132. \\vspace{{0.6cm}}
  133. {{\\huge {gameName} \\par}}
  134. \\vspace{{0.6cm}}
  135. {{\\Large {date} \\par}}
  136. \\vspace{{2cm}}
  137. \\begin{{tabularx}}{{\\textwidth}}{{ R | c | X }}
  138. \\hline
  139. \\stone{{black}} {playerBlack} & \\textbf{{{result}}} & {playerWhite} \\stone{{white}} \\\\\\hline
  140. & {komi} Komi & \\\\\\hline
  141. \\end{{tabularx}}
  142. \\vspace{{3cm}}
  143. {moves}
  144. \\end{{center}}
  145. \\end{{document}}
  146. """
  147. with open(f"{fileBase}.tex", 'w') as outFile:
  148. outFile.write(outText)
  149. # should be compiled to pdf?
  150. if not args.t:
  151. try:
  152. os.makedirs(os.path.dirname(f"{fileBase}/"), exist_ok=True)
  153. copyfile(f"{fileBase}.tex", f"{fileBase}/temp.tex")
  154. os.remove(f"{fileBase}.tex") # poor mans `move` that works on win and linux
  155. check_call(['latex', f"temp.tex"],
  156. stdout=DEVNULL, stderr=STDOUT, cwd=f'{fileBase}')
  157. check_call(['dvips', f"temp.dvi", "-P", "pdf"],
  158. stdout=DEVNULL, stderr=STDOUT, cwd=f'{fileBase}')
  159. check_call(['ps2pdf', f"temp.ps"],
  160. stdout=DEVNULL, stderr=STDOUT, cwd=f'{fileBase}')
  161. except CalledProcessError:
  162. print("error")
  163. else:
  164. copyfile(f"{fileBase}/temp.pdf", f"{fileBase}.pdf")
  165. rmtree(f"{fileBase}/")
  166. # hould output be opened?
  167. if args.o:
  168. os.system(f'"{fileBase}.pdf"')