| 1 | #! /usr/bin/env python |
| 2 | |
| 3 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # |
| 4 | # This program is distributed with GNU Go, a Go program. # |
| 5 | # # |
| 6 | # Write gnugo@gnu.org or see http://www.gnu.org/software/gnugo/ # |
| 7 | # for more information. # |
| 8 | # # |
| 9 | # Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, # |
| 10 | # 2008 and 2009 by the Free Software Foundation. # |
| 11 | # # |
| 12 | # This program is free software; you can redistribute it and/or # |
| 13 | # modify it under the terms of the GNU General Public License # |
| 14 | # as published by the Free Software Foundation - version 3, # |
| 15 | # or (at your option) any later version. # |
| 16 | # # |
| 17 | # This program is distributed in the hope that it will be # |
| 18 | # useful, but WITHOUT ANY WARRANTY; without even the implied # |
| 19 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # |
| 20 | # PURPOSE. See the GNU General Public License in file COPYING # |
| 21 | # for more details. # |
| 22 | # # |
| 23 | # You should have received a copy of the GNU General Public # |
| 24 | # License along with this program; if not, write to the Free # |
| 25 | # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # |
| 26 | # Boston, MA 02111, USA. # |
| 27 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # |
| 28 | |
| 29 | from getopt import * |
| 30 | import popen2 |
| 31 | import sys |
| 32 | import string |
| 33 | import re |
| 34 | |
| 35 | |
| 36 | debug = 0 |
| 37 | |
| 38 | |
| 39 | def coords_to_sgf(size, board_coords): |
| 40 | global debug |
| 41 | |
| 42 | board_coords = string.lower(board_coords) |
| 43 | if board_coords == "pass": |
| 44 | return "" |
| 45 | if debug: |
| 46 | print "Coords: <" + board_coords + ">" |
| 47 | letter = board_coords[0] |
| 48 | digits = board_coords[1:] |
| 49 | if letter > "i": |
| 50 | sgffirst = chr(ord(letter) - 1) |
| 51 | else: |
| 52 | sgffirst = letter |
| 53 | sgfsecond = chr(ord("a") + int(size) - int(digits)) |
| 54 | return sgffirst + sgfsecond |
| 55 | |
| 56 | |
| 57 | |
| 58 | class GTP_connection: |
| 59 | |
| 60 | # |
| 61 | # Class members: |
| 62 | # outfile File to write to |
| 63 | # infile File to read from |
| 64 | |
| 65 | def __init__(self, command): |
| 66 | try: |
| 67 | infile, outfile = popen2.popen2(command) |
| 68 | except: |
| 69 | print "popen2 failed" |
| 70 | sys.exit(1) |
| 71 | self.infile = infile |
| 72 | self.outfile = outfile |
| 73 | |
| 74 | def exec_cmd(self, cmd): |
| 75 | global debug |
| 76 | |
| 77 | if debug: |
| 78 | sys.stderr.write("GTP command: " + cmd + "\n") |
| 79 | self.outfile.write(cmd + "\n\n") |
| 80 | self.outfile.flush() |
| 81 | result = "" |
| 82 | line = self.infile.readline() |
| 83 | while line != "\n": |
| 84 | result = result + line |
| 85 | line = self.infile.readline() |
| 86 | if debug: |
| 87 | sys.stderr.write("Reply: " + line + "\n") |
| 88 | |
| 89 | # Remove trailing newline from the result |
| 90 | if result[-1] == "\n": |
| 91 | result = result[:-1] |
| 92 | |
| 93 | if len(result) == 0: |
| 94 | return "ERROR: len = 0" |
| 95 | if (result[0] == "?"): |
| 96 | return "ERROR: GTP Command failed: " + result[2:] |
| 97 | if (result[0] == "="): |
| 98 | return result[2:] |
| 99 | return "ERROR: Unrecognized answer: " + result |
| 100 | |
| 101 | |
| 102 | class GTP_player: |
| 103 | |
| 104 | # Class members: |
| 105 | # connection GTP_connection |
| 106 | |
| 107 | def __init__(self, command): |
| 108 | self.connection = GTP_connection(command) |
| 109 | protocol_version = self.connection.exec_cmd("protocol_version") |
| 110 | if protocol_version[:5] != "ERROR": |
| 111 | self.protocol_version = protocol_version |
| 112 | else: |
| 113 | self.protocol_version = "1" |
| 114 | |
| 115 | def is_known_command(self, command): |
| 116 | return self.connection.exec_cmd("known_command " + command) == "true" |
| 117 | |
| 118 | def genmove(self, color): |
| 119 | if color[0] in ["b", "B"]: |
| 120 | command = "black" |
| 121 | elif color[0] in ["w", "W"]: |
| 122 | command = "white" |
| 123 | if self.protocol_version == "1": |
| 124 | command = "genmove_" + command |
| 125 | else: |
| 126 | command = "genmove " + command |
| 127 | |
| 128 | return self.connection.exec_cmd(command) |
| 129 | |
| 130 | def black(self, move): |
| 131 | if self.protocol_version == "1": |
| 132 | self.connection.exec_cmd("black " + move) |
| 133 | else: |
| 134 | self.connection.exec_cmd("play black " + move) |
| 135 | |
| 136 | def white(self, move): |
| 137 | if self.protocol_version == "1": |
| 138 | self.connection.exec_cmd("white " + move) |
| 139 | else: |
| 140 | self.connection.exec_cmd("play white " + move) |
| 141 | |
| 142 | def komi(self, komi): |
| 143 | self.connection.exec_cmd("komi " + komi) |
| 144 | |
| 145 | def boardsize(self, size): |
| 146 | self.connection.exec_cmd("boardsize " + size) |
| 147 | if self.protocol_version != "1": |
| 148 | self.connection.exec_cmd("clear_board") |
| 149 | |
| 150 | def handicap(self, handicap, handicap_type): |
| 151 | if handicap_type == "fixed": |
| 152 | result = self.connection.exec_cmd("fixed_handicap %d" % (handicap)) |
| 153 | else: |
| 154 | result = self.connection.exec_cmd("place_free_handicap %d" |
| 155 | % (handicap)) |
| 156 | |
| 157 | return string.split(result, " ") |
| 158 | |
| 159 | def loadsgf(self, endgamefile, move_number): |
| 160 | self.connection.exec_cmd(string.join(["loadsgf", endgamefile, |
| 161 | str(move_number)])) |
| 162 | |
| 163 | def list_stones(self, color): |
| 164 | return string.split(self.connection.exec_cmd("list_stones " + color), " ") |
| 165 | |
| 166 | def quit(self): |
| 167 | return self.connection.exec_cmd("quit") |
| 168 | |
| 169 | def showboard(self): |
| 170 | board = self.connection.exec_cmd("showboard") |
| 171 | if board and (board[0] == "\n"): |
| 172 | board = board[1:] |
| 173 | return board |
| 174 | |
| 175 | def get_random_seed(self): |
| 176 | result = self.connection.exec_cmd("get_random_seed") |
| 177 | if result[:5] == "ERROR": |
| 178 | return "unknown" |
| 179 | return result |
| 180 | |
| 181 | def set_random_seed(self, seed): |
| 182 | self.connection.exec_cmd("set_random_seed " + seed) |
| 183 | |
| 184 | def get_program_name(self): |
| 185 | return self.connection.exec_cmd("name") + " " + \ |
| 186 | self.connection.exec_cmd("version") |
| 187 | |
| 188 | def final_score(self): |
| 189 | return self.connection.exec_cmd("final_score") |
| 190 | |
| 191 | def score(self): |
| 192 | return self.final_score(self) |
| 193 | |
| 194 | def cputime(self): |
| 195 | if (self.is_known_command("cputime")): |
| 196 | return self.connection.exec_cmd("cputime") |
| 197 | else: |
| 198 | return "0" |
| 199 | |
| 200 | |
| 201 | class GTP_game: |
| 202 | |
| 203 | # Class members: |
| 204 | # whiteplayer GTP_player |
| 205 | # blackplayer GTP_player |
| 206 | # size int |
| 207 | # komi float |
| 208 | # handicap int |
| 209 | # handicap_type string |
| 210 | # handicap_stones int |
| 211 | # moves list of string |
| 212 | # resultw |
| 213 | # resultb |
| 214 | |
| 215 | def __init__(self, whitecommand, blackcommand, size, komi, handicap, |
| 216 | handicap_type, endgamefile): |
| 217 | self.whiteplayer = GTP_player(whitecommand) |
| 218 | self.blackplayer = GTP_player(blackcommand) |
| 219 | self.size = size |
| 220 | self.komi = komi |
| 221 | self.handicap = handicap |
| 222 | self.handicap_type = handicap_type |
| 223 | self.endgamefile = endgamefile |
| 224 | self.sgffilestart = "" |
| 225 | if endgamefile != "": |
| 226 | self.init_endgame_contest_game() |
| 227 | else: |
| 228 | self.sgffilestart = "" |
| 229 | |
| 230 | def init_endgame_contest_game(self): |
| 231 | infile = open(self.endgamefile) |
| 232 | if not infile: |
| 233 | print "Couldn't read " + self.endgamefile |
| 234 | sys.exit(2) |
| 235 | sgflines = infile.readlines() |
| 236 | infile.close |
| 237 | size = re.compile("SZ\[[0-9]+\]") |
| 238 | move = re.compile(";[BW]\[[a-z]{0,2}\]") |
| 239 | sgf_start = [] |
| 240 | for line in sgflines: |
| 241 | match = size.search(line) |
| 242 | if match: |
| 243 | self.size = match.group()[3:-1] |
| 244 | match = move.search(line) |
| 245 | while match: |
| 246 | sgf_start.append("A" + match.group()[1:]) |
| 247 | line = line[match.end():] |
| 248 | match = move.search(line) |
| 249 | self.endgame_start = len(sgf_start) - endgame_start_at |
| 250 | self.sgffilestart = ";" + string.join( |
| 251 | sgf_start[:self.endgame_start-1], "") + "\n" |
| 252 | if self.endgame_start % 2 == 0: |
| 253 | self.first_to_play = "W" |
| 254 | else: |
| 255 | self.first_to_play = "B" |
| 256 | |
| 257 | def get_position_from_engine(self, engine): |
| 258 | black_stones = engine.list_stones("black") |
| 259 | white_stones = engine.list_stones("white") |
| 260 | self.sgffilestart = ";" |
| 261 | if len(black_stones) > 0: |
| 262 | self.sgffilestart += "AB" |
| 263 | for stone in black_stones: |
| 264 | self.sgffilestart += "[%s]" % coords_to_sgf(self.size, stone) |
| 265 | self.sgffilestart += "\n" |
| 266 | if len(white_stones) > 0: |
| 267 | self.sgffilestart += "AW" |
| 268 | for stone in white_stones: |
| 269 | self.sgffilestart += "[%s]" % coords_to_sgf(self.size, stone) |
| 270 | self.sgffilestart += "\n" |
| 271 | |
| 272 | def writesgf(self, sgffilename): |
| 273 | "Write the game to an SGF file after a game" |
| 274 | |
| 275 | size = self.size |
| 276 | outfile = open(sgffilename, "w") |
| 277 | if not outfile: |
| 278 | print "Couldn't create " + sgffilename |
| 279 | return |
| 280 | black_name = self.blackplayer.get_program_name() |
| 281 | white_name = self.whiteplayer.get_program_name() |
| 282 | black_seed = self.blackplayer.get_random_seed() |
| 283 | white_seed = self.whiteplayer.get_random_seed() |
| 284 | handicap = self.handicap |
| 285 | komi = self.komi |
| 286 | result = self.resultw |
| 287 | |
| 288 | outfile.write("(;GM[1]FF[4]RU[Japanese]SZ[%s]HA[%s]KM[%s]RE[%s]\n" % |
| 289 | (size, handicap, komi, result)) |
| 290 | outfile.write("PW[%s (random seed %s)]PB[%s (random seed %s)]\n" % |
| 291 | (white_name, white_seed, black_name, black_seed)) |
| 292 | outfile.write(self.sgffilestart) |
| 293 | |
| 294 | if handicap > 1: |
| 295 | outfile.write("AB"); |
| 296 | for stone in self.handicap_stones: |
| 297 | outfile.write("[%s]" %(coords_to_sgf(size, stone))) |
| 298 | outfile.write("PL[W]\n") |
| 299 | |
| 300 | to_play = self.first_to_play |
| 301 | |
| 302 | for move in self.moves: |
| 303 | sgfmove = coords_to_sgf(size, move) |
| 304 | outfile.write(";%s[%s]\n" % (to_play, sgfmove)) |
| 305 | if to_play == "B": |
| 306 | to_play = "W" |
| 307 | else: |
| 308 | to_play = "B" |
| 309 | outfile.write(")\n") |
| 310 | outfile.close |
| 311 | |
| 312 | def set_handicap(self, handicap): |
| 313 | self.handicap = handicap |
| 314 | |
| 315 | def swap_players(self): |
| 316 | temp = self.whiteplayer |
| 317 | self.whiteplayer = self.blackplayer |
| 318 | self.blackplayer = temp |
| 319 | |
| 320 | def play(self, sgffile): |
| 321 | "Play a game" |
| 322 | global verbose |
| 323 | |
| 324 | if verbose >= 1: |
| 325 | print "Setting boardsize and komi for black\n" |
| 326 | self.blackplayer.boardsize(self.size) |
| 327 | self.blackplayer.komi(self.komi) |
| 328 | |
| 329 | if verbose >= 1: |
| 330 | print "Setting boardsize and komi for white\n" |
| 331 | self.whiteplayer.boardsize(self.size) |
| 332 | self.whiteplayer.komi(self.komi) |
| 333 | |
| 334 | self.handicap_stones = [] |
| 335 | |
| 336 | if self.endgamefile == "": |
| 337 | if self.handicap < 2: |
| 338 | self.first_to_play = "B" |
| 339 | else: |
| 340 | self.handicap_stones = self.blackplayer.handicap(self.handicap, self.handicap_type) |
| 341 | for stone in self.handicap_stones: |
| 342 | self.whiteplayer.black(stone) |
| 343 | self.first_to_play = "W" |
| 344 | else: |
| 345 | self.blackplayer.loadsgf(self.endgamefile, self.endgame_start) |
| 346 | self.blackplayer.set_random_seed("0") |
| 347 | self.whiteplayer.loadsgf(self.endgamefile, self.endgame_start) |
| 348 | self.whiteplayer.set_random_seed("0") |
| 349 | if self.blackplayer.is_known_command("list_stones"): |
| 350 | self.get_position_from_engine(self.blackplayer) |
| 351 | elif self.whiteplayer.is_known_command("list_stones"): |
| 352 | self.get_position_from_engine(self.whiteplayer) |
| 353 | |
| 354 | to_play = self.first_to_play |
| 355 | |
| 356 | self.moves = [] |
| 357 | passes = 0 |
| 358 | won_by_resignation = "" |
| 359 | while passes < 2: |
| 360 | if to_play == "B": |
| 361 | move = self.blackplayer.genmove("black") |
| 362 | if move[:5] == "ERROR": |
| 363 | # FIXME: write_sgf |
| 364 | sys.exit(1) |
| 365 | |
| 366 | if move[:6] == "resign": |
| 367 | if verbose >= 1: |
| 368 | print "Black resigns" |
| 369 | won_by_resignation = "W+Resign" |
| 370 | break |
| 371 | else: |
| 372 | self.moves.append(move) |
| 373 | if string.lower(move[:4]) == "pass": |
| 374 | passes = passes + 1 |
| 375 | if verbose >= 1: |
| 376 | print "Black passes" |
| 377 | else: |
| 378 | passes = 0 |
| 379 | self.whiteplayer.black(move) |
| 380 | if verbose >= 1: |
| 381 | print "Black plays " + move |
| 382 | to_play = "W" |
| 383 | else: |
| 384 | move = self.whiteplayer.genmove("white") |
| 385 | if move[:5] == "ERROR": |
| 386 | # FIXME: write_sgf |
| 387 | sys.exit(1) |
| 388 | |
| 389 | if move[:6] == "resign": |
| 390 | if verbose >= 1: |
| 391 | print "White resigns" |
| 392 | won_by_resignation = "B+Resign" |
| 393 | break |
| 394 | else: |
| 395 | self.moves.append(move) |
| 396 | if string.lower(move[:4]) == "pass": |
| 397 | passes = passes + 1 |
| 398 | if verbose >= 1: |
| 399 | print "White passes" |
| 400 | else: |
| 401 | passes = 0 |
| 402 | self.blackplayer.white(move) |
| 403 | if verbose >= 1: |
| 404 | print "White plays " + move |
| 405 | to_play = "B" |
| 406 | |
| 407 | if verbose >= 2: |
| 408 | print self.whiteplayer.showboard() + "\n" |
| 409 | |
| 410 | if won_by_resignation == "": |
| 411 | self.resultw = self.whiteplayer.final_score() |
| 412 | self.resultb = self.blackplayer.final_score() |
| 413 | else: |
| 414 | self.resultw = won_by_resignation; |
| 415 | self.resultb = won_by_resignation; |
| 416 | # if self.resultb == self.resultw: |
| 417 | # print "Result: ", self.resultw |
| 418 | # else: |
| 419 | # print "Result according to W: ", self.resultw |
| 420 | # print "Result according to B: ", self.resultb |
| 421 | # FIXME: $self->writesgf($sgffile) if defined $sgffile; |
| 422 | if sgffile != "": |
| 423 | self.writesgf(sgffile) |
| 424 | |
| 425 | def result(self): |
| 426 | return (self.resultw, self.resultb) |
| 427 | |
| 428 | def cputime(self): |
| 429 | cputime = {} |
| 430 | cputime["white"] = self.whiteplayer.cputime() |
| 431 | cputime["black"] = self.blackplayer.cputime() |
| 432 | return cputime |
| 433 | |
| 434 | def quit(self): |
| 435 | self.blackplayer.quit() |
| 436 | self.whiteplayer.quit() |
| 437 | |
| 438 | |
| 439 | class GTP_match: |
| 440 | |
| 441 | # Class members: |
| 442 | # black |
| 443 | # white |
| 444 | # size |
| 445 | # komi |
| 446 | # handicap |
| 447 | # handicap_type |
| 448 | |
| 449 | def __init__(self, whitecommand, blackcommand, size, komi, handicap, |
| 450 | handicap_type, streak_length, endgamefilelist): |
| 451 | self.white = whitecommand |
| 452 | self.black = blackcommand |
| 453 | self.size = size |
| 454 | self.komi = komi |
| 455 | self.handicap = handicap |
| 456 | self.handicap_type = handicap_type |
| 457 | self.streak_length = streak_length |
| 458 | self.endgamefilelist = endgamefilelist |
| 459 | |
| 460 | def endgame_contest(self, sgfbase): |
| 461 | results = [] |
| 462 | i = 1 |
| 463 | for endgamefile in self.endgamefilelist: |
| 464 | game1 = GTP_game(self.white, self.black, self.size, self.komi, |
| 465 | 0, "", endgamefile) |
| 466 | game2 = GTP_game(self.black, self.white, self.size, self.komi, |
| 467 | 0, "", endgamefile) |
| 468 | if verbose: |
| 469 | print "Replaying", endgamefile |
| 470 | print "Black:", self.black |
| 471 | print "White:", self.white |
| 472 | game1.play("") |
| 473 | result1 = game1.result()[0] |
| 474 | if result1 != "0": |
| 475 | plain_result1 = re.search(r"([BW]\+)([0-9]*\.[0-9]*)", result1) |
| 476 | result1_float = float(plain_result1.group(2)) |
| 477 | else: |
| 478 | plain_result1 = re.search(r"(0)", "0") |
| 479 | result1_float = 0.0 |
| 480 | if result1[0] == "B": |
| 481 | result1_float *= -1 |
| 482 | if verbose: |
| 483 | print "Result:", result1 |
| 484 | print "Replaying", endgamefile |
| 485 | print "Black:", self.white |
| 486 | print "White:", self.black |
| 487 | game2.play("") |
| 488 | result2 = game2.result()[1] |
| 489 | if verbose: |
| 490 | print "Result:", result2 |
| 491 | if result2 != "0": |
| 492 | plain_result2 = re.search(r"([BW]\+)([0-9]*\.[0-9]*)", result2) |
| 493 | result2_float = float(plain_result2.group(2)) |
| 494 | else: |
| 495 | plain_result2 = re.search(r"(0)", "0") |
| 496 | result2_float = 0.0 |
| 497 | |
| 498 | if result2[0] == "B": |
| 499 | result2_float *= -1 |
| 500 | results.append(result1_float - result2_float) |
| 501 | if (result1 != result2): |
| 502 | print endgamefile+ ":", plain_result1.group(), \ |
| 503 | plain_result2.group(), "Difference:", |
| 504 | print result1_float - result2_float |
| 505 | else: |
| 506 | print endgamefile+": Same result:", plain_result1.group() |
| 507 | sgffilename = "%s%03d" % (sgfbase, i) |
| 508 | game1.writesgf(sgffilename + "_1.sgf") |
| 509 | game2.writesgf(sgffilename + "_2.sgf") |
| 510 | game1.quit() |
| 511 | game2.quit() |
| 512 | i += 1 |
| 513 | return results |
| 514 | |
| 515 | def play(self, games, sgfbase): |
| 516 | last_color = "" |
| 517 | last_streak = 0 |
| 518 | game = GTP_game(self.white, self.black, |
| 519 | self.size, self.komi, self.handicap, |
| 520 | self.handicap_type, "") |
| 521 | results = [] |
| 522 | for i in range(games): |
| 523 | sgffilename = "%s%03d.sgf" % (sgfbase, i + 1) |
| 524 | game.play(sgffilename) |
| 525 | result = game.result() |
| 526 | if result[0] == result[1]: |
| 527 | print "Game %d: %s" % (i + 1, result[0]) |
| 528 | else: |
| 529 | print "Game %d: %s %s" % (i + 1, result[0], result[1]) |
| 530 | |
| 531 | if result[0][0] == last_color: |
| 532 | last_streak += 1 |
| 533 | elif result[0][0] != "0": |
| 534 | last_color = result[0][0] |
| 535 | last_streak = 1 |
| 536 | |
| 537 | if last_streak == self.streak_length: |
| 538 | if last_color == "W": |
| 539 | self.handicap += 1 |
| 540 | if self.handicap == 1: |
| 541 | self.handicap = 2 |
| 542 | print "White wins too often. Increasing handicap to %d" \ |
| 543 | % (self.handicap) |
| 544 | else: |
| 545 | if self.handicap > 0: |
| 546 | self.handicap -= 1 |
| 547 | if self.handicap == 1: |
| 548 | self.handicap = 0 |
| 549 | print "Black wins too often. Decreasing handicap to %d" \ |
| 550 | % (self.handicap) |
| 551 | else: |
| 552 | self.handicap = 2 |
| 553 | game.swap_players() |
| 554 | print "Black looks stronger than white. Swapping colors and setting handicap to 2" |
| 555 | game.set_handicap(self.handicap) |
| 556 | last_color = "" |
| 557 | last_streak = 0 |
| 558 | results.append(result) |
| 559 | cputime = game.cputime() |
| 560 | game.quit() |
| 561 | return results, cputime |
| 562 | |
| 563 | |
| 564 | # ================================================================ |
| 565 | # Main program |
| 566 | # |
| 567 | |
| 568 | |
| 569 | # Default values |
| 570 | # |
| 571 | |
| 572 | white = "" |
| 573 | black = "" |
| 574 | komi = "" |
| 575 | size = "19" |
| 576 | handicap = 0 |
| 577 | handicap_type = "fixed" |
| 578 | streak_length = -1 |
| 579 | endgame_start_at = 0 |
| 580 | |
| 581 | games = 1 |
| 582 | sgfbase = "twogtp" |
| 583 | |
| 584 | verbose = 0 |
| 585 | |
| 586 | helpstring = """ |
| 587 | |
| 588 | Run with: |
| 589 | |
| 590 | twogtp --white \'<path to program 1> --mode gtp [program options]\' \\ |
| 591 | --black \'<path to program 2> --mode gtp [program options]\' \\ |
| 592 | [twogtp options] |
| 593 | |
| 594 | Possible twogtp options: |
| 595 | |
| 596 | --verbose 1 (to list moves) or --verbose 2 (to draw board) |
| 597 | --komi <amount> |
| 598 | --handicap <amount> |
| 599 | --free-handicap <amount> |
| 600 | --adjust-handicap <length> (change handicap by 1 after <length> wins |
| 601 | in a row) |
| 602 | --size <board size> (default 19) |
| 603 | --games <number of games to play> (default 1) |
| 604 | --sgfbase <filename> (create sgf files with sgfbase as basename) |
| 605 | --endgame <moves before end> (endgame contest - add filenames of |
| 606 | games to be replayed after last option) |
| 607 | """ |
| 608 | |
| 609 | def usage(): |
| 610 | print helpstring |
| 611 | sys.exit(1) |
| 612 | |
| 613 | try: |
| 614 | (opts, params) = getopt(sys.argv[1:], "", |
| 615 | ["black=", |
| 616 | "white=", |
| 617 | "verbose=", |
| 618 | "komi=", |
| 619 | "boardsize=", |
| 620 | "size=", |
| 621 | "handicap=", |
| 622 | "free-handicap=", |
| 623 | "adjust-handicap=", |
| 624 | "games=", |
| 625 | "sgfbase=", |
| 626 | "endgame=", |
| 627 | ]) |
| 628 | except: |
| 629 | usage(); |
| 630 | |
| 631 | for opt, value in opts: |
| 632 | if opt == "--black": |
| 633 | black = value |
| 634 | elif opt == "--white": |
| 635 | white = value |
| 636 | elif opt == "--verbose": |
| 637 | verbose = int(value) |
| 638 | elif opt == "--komi": |
| 639 | komi = value |
| 640 | elif opt == "--boardsize" or opt == "--size": |
| 641 | size = value |
| 642 | elif opt == "--handicap": |
| 643 | handicap = int(value) |
| 644 | handicap_type = "fixed" |
| 645 | elif opt == "--free-handicap": |
| 646 | handicap = int(value) |
| 647 | handicap_type = "free" |
| 648 | elif opt == "--adjust-handicap": |
| 649 | streak_length = int(value) |
| 650 | elif opt == "--games": |
| 651 | games = int(value) |
| 652 | elif opt == "--sgfbase": |
| 653 | sgfbase = value |
| 654 | elif opt == "--endgame": |
| 655 | endgame_start_at = int(value) |
| 656 | |
| 657 | if endgame_start_at != 0: |
| 658 | endgame_filelist = params |
| 659 | else: |
| 660 | endgame_filelist = [] |
| 661 | if params != []: |
| 662 | usage() |
| 663 | |
| 664 | |
| 665 | if black == "" or white == "": |
| 666 | usage() |
| 667 | |
| 668 | if komi == "": |
| 669 | if handicap > 1 and streak_length == -1: |
| 670 | komi = "0.5" |
| 671 | else: |
| 672 | komi = "5.5" |
| 673 | |
| 674 | match = GTP_match(white, black, size, komi, handicap, handicap_type, |
| 675 | streak_length, endgame_filelist) |
| 676 | if endgame_filelist != []: |
| 677 | results = match.endgame_contest(sgfbase) |
| 678 | win_black = 0 |
| 679 | win_white = 0 |
| 680 | for res in results: |
| 681 | print res |
| 682 | if res > 0.0: |
| 683 | win_white += 1 |
| 684 | elif res < 0.0: |
| 685 | win_black += 1 |
| 686 | print "%d games, %d wins for black, %d wins for white." \ |
| 687 | % (len(results), win_black, win_white) |
| 688 | |
| 689 | else: |
| 690 | results, cputimes = match.play(games, sgfbase) |
| 691 | |
| 692 | i = 0 |
| 693 | for resw, resb in results: |
| 694 | i = i + 1 |
| 695 | if resw == resb: |
| 696 | print "Game %d: %s" % (i, resw) |
| 697 | else: |
| 698 | print "Game %d: %s %s" % (i, resb, resw) |
| 699 | if (cputimes["white"] != "0"): |
| 700 | print "White: %ss CPU time" % cputimes["white"] |
| 701 | if (cputimes["black"] != "0"): |
| 702 | print "Black: %ss CPU time" % cputimes["black"] |