pachi_py/pachi/tools/sgf2gtp.py (119 lines of code) (raw):

#! /usr/bin/env python import sys import argparse import re from sgflib import SGFParser DEBUG = False parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=""" This script converts SGF files to GTP format so that you can feed them to Pachi, insert genmove at the right places etc. Might not work on obscure SGF files. When called with FILENAMES argument, it will create according output files with .gtp extension instead of .sgf, unless --no-gtp option is specified. Otherwise the games are read from standard input. Note that the stdin in this case is read in at once, so for very large collections, it is better to run this script separately for each sgf file. example: cat *.sgf | %s -g -n 5 """%(sys.argv[0])) parser.add_argument('FILENAMES', help='List of sgf games to process.', nargs='*', default=[]) parser.add_argument('-g', help='Automatically append genmove command for the other color.', action='store_true') parser.add_argument('-n', help='Output at most first MOVENUM moves.', metavar='MOVENUM', type=int, default=10**10) parser.add_argument('--stdout-only', help='Do not create the .gtp files from FILENAMES, print everything to stdout.', action='store_true') args = vars(parser.parse_args()) class UnknownNode(Exception): pass def get_atr(node, atr): try: return node.data[atr].data[0] except KeyError: return None def get_setup(node, atr): try: return node.data[atr].data[:] except KeyError: return None def col2num(column, board_size): a, o, z = map(ord, ['a', column, 'z']) if a <= o <= z: return a + board_size - o raise Exception( "Wrong column character: '%s'"%(column,) ) def is_pass_move(coord, board_size): # the pass move is represented either by [] ( = empty coord ) # OR by [tt] (for boards <= 19 only) return len(coord) == 0 or ( board_size <= 19 and coord == 'tt' ) def process_gametree(gametree, fout): # cursor for tree traversal c = gametree.cursor() # first node is the header header = c.node handicap = get_atr(header, 'HA') board_size = int(get_atr(header, 'SZ') or 19) komi = get_atr(header, 'KM') player_next, player_other = "B", "W" setup_black = get_setup(header, 'AB') setup_white = get_setup(header, 'AW') print >>fout, "boardsize", board_size print >>fout, "clear_board" if komi: print >>fout, "komi", komi if handicap and handicap != '0': print >>fout, "fixed_handicap", handicap player_next, player_other = player_other, player_next if setup_black: for item in setup_black: x, y = item if x >= 'i': x = chr(ord(x)+1) y = str(col2num(y, board_size)) print >>fout, "play B", x+y if setup_white: for item in setup_white: x, y = item if x >= 'i': x = chr(ord(x)+1) y = str(col2num(y, board_size)) print >>fout, "play W", x+y def print_game_step(coord): if is_pass_move(coord, board_size): print >>fout, "play", player_next, "pass" else: x, y = coord # The reason for this incredibly weird thing is that # the GTP protocol excludes `i` in the coordinates # (do you see any purpose in this??) if x >= 'i': x = chr(ord(x)+1) y = str(col2num(y, board_size)) print >>fout, "play", player_next, x+y movenum = 0 # walk the game tree forward while 1: # sgf2gtp.pl ignores n = 0 if c.atEnd or (args['n'] and movenum >= args['n']): break c.next() movenum += 1 coord = get_atr(c.node, player_next) if coord != None: print_game_step(coord) else: # MAYBE white started? # or one of the players plays two time in a row player_next, player_other = player_other, player_next coord = get_atr(c.node, player_next) if coord != None: print_game_step(coord) else: # TODO handle weird sgf files better raise UnknownNode player_next, player_other = player_other, player_next if args['g']: print >>fout, "genmove", player_next def process_sgf_file(fin, fout): sgfdata = fin.read() col = SGFParser(sgfdata).parse() for gametree in col: try: process_gametree(gametree, fout) except UnknownNode: # Try next game tree in this file if DEBUG: print >>sys.stderr, "Unknown Node" continue if __name__ == "__main__": if not len(args['FILENAMES']): process_sgf_file(sys.stdin, sys.stdout) else: for in_filename in args['FILENAMES']: if args['stdout_only']: fout = sys.stdout else: if re.search('sgf$', in_filename): filename_base = in_filename[:-3] else: filename_base = in_filename # Save the .gtp file out_filename = filename_base + 'gtp' fout = open(out_filename, 'w') fin = open(in_filename, 'r') process_sgf_file(fin, fout) fin.close() if not args['stdout_only']: fout.close()