7 from sgflib
import SGFParser
11 parser
= argparse
.ArgumentParser( formatter_class
=argparse
.RawDescriptionHelpFormatter
,
13 This script converts SGF files to GTP format so that you can feed them
14 to Pachi, insert genmove at the right places etc. Might not work on
17 When called with FILENAMES argument, it will create according output
18 files with .gtp extension instead of .sgf, unless --no-gtp option is
21 Otherwise the games are read from standard input. Note that the stdin
22 in this case is read in at once, so for very large collections, it is
23 better to run this script separately for each sgf file.
26 cat *.sgf | %s -g -n 5
29 parser
.add_argument('FILENAMES', help='List of sgf games to process.', nargs
='*', default
=[])
30 parser
.add_argument('-g', help='Automatically append genmove command for the other color.', action
='store_true')
31 parser
.add_argument('-n', help='Output at most first MOVENUM moves.', metavar
='MOVENUM', type=int, default
=10**10)
32 parser
.add_argument('--stdout-only', help='Do not create the .gtp files from FILENAMES, print everything to stdout.', action
='store_true')
33 args
= vars(parser
.parse_args())
35 class UnknownNode(Exception):
38 def get_atr(node
, atr
):
40 return node
.data
[atr
].data
[0]
44 def get_setup(node
, atr
):
46 return node
.data
[atr
].data
[:]
50 def col2num(column
, board_size
):
51 a
, o
, z
= map(ord, ['a', column
, 'z'])
53 return a
+ board_size
- o
54 raise Exception( "Wrong column character: '%s'"%(column
,) )
56 def is_pass_move(coord
, board_size
):
57 # the pass move is represented either by [] ( = empty coord )
58 # OR by [tt] (for boards <= 19 only)
59 return len(coord
) == 0 or ( board_size
<= 19 and coord
== 'tt' )
61 def process_gametree(gametree
, fout
):
62 # cursor for tree traversal
64 # first node is the header
67 handicap
= get_atr(header
, 'HA')
68 board_size
= int(get_atr(header
, 'SZ') or 19)
69 komi
= get_atr(header
, 'KM')
70 player_next
, player_other
= "B", "W"
71 setup_black
= get_setup(header
, 'AB')
72 setup_white
= get_setup(header
, 'AW')
74 print >>fout
, "boardsize", board_size
75 print >>fout
, "clear_board"
77 print >>fout
, "komi", komi
78 if handicap
and handicap
!= '0':
79 print >>fout
, "fixed_handicap", handicap
80 player_next
, player_other
= player_other
, player_next
82 for item
in setup_black
:
86 y
= str(col2num(y
, board_size
))
87 print >>fout
, "play B", x
+y
89 for item
in setup_white
:
93 y
= str(col2num(y
, board_size
))
94 print >>fout
, "play W", x
+y
96 def print_game_step(coord
):
97 if is_pass_move(coord
, board_size
):
98 print >>fout
, "play", player_next
, "pass"
101 # The reason for this incredibly weird thing is that
102 # the GTP protocol excludes `i` in the coordinates
103 # (do you see any purpose in this??)
106 y
= str(col2num(y
, board_size
))
107 print >>fout
, "play", player_next
, x
+y
110 # walk the game tree forward
112 # sgf2gtp.pl ignores n = 0
113 if c
.atEnd
or (args
['n'] and movenum
>= args
['n']):
118 coord
= get_atr(c
.node
, player_next
)
120 print_game_step(coord
)
122 # MAYBE white started?
123 # or one of the players plays two time in a row
124 player_next
, player_other
= player_other
, player_next
125 coord
= get_atr(c
.node
, player_next
)
127 print_game_step(coord
)
129 # TODO handle weird sgf files better
132 player_next
, player_other
= player_other
, player_next
135 print >>fout
, "genmove", player_next
137 def process_sgf_file(fin
, fout
):
139 col
= SGFParser(sgfdata
).parse()
143 process_gametree(gametree
, fout
)
145 # Try next game tree in this file
147 print >>sys
.stderr
, "Unknown Node"
150 if __name__
== "__main__":
151 if not len(args
['FILENAMES']):
152 process_sgf_file(sys
.stdin
, sys
.stdout
)
154 for in_filename
in args
['FILENAMES']:
155 if args
['stdout_only']:
158 if re
.search('sgf$', in_filename
):
159 filename_base
= in_filename
[:-3]
161 filename_base
= in_filename
163 out_filename
= filename_base
+ 'gtp'
165 fout
= open(out_filename
, 'w')
167 fin
= open(in_filename
, 'r')
169 process_sgf_file(fin
, fout
)
172 if not args
['stdout_only']: