# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# This program is distributed with GNU Go, a Go program. #
# Write gnugo@gnu.org or see http://www.gnu.org/software/gnugo/ #
# for more information. #
# Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, #
# 2008 and 2009 by the Free Software Foundation. #
# This program is free software; you can redistribute it and/or #
# modify it under the terms of the GNU General Public License #
# as published by the Free Software Foundation - version 3, #
# or (at your option) any later version. #
# This program is distributed in the hope that it will be #
# useful, but WITHOUT ANY WARRANTY; without even the implied #
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR #
# PURPOSE. See the GNU General Public License in file COPYING #
# You should have received a copy of the GNU General Public #
# License along with this program; if not, write to the Free #
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, #
# Boston, MA 02111, USA. #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def coords_to_sgf(size
, board_coords
):
board_coords
= string
.lower(board_coords
)
if board_coords
== "pass":
print "Coords: <" + board_coords
+ ">"
digits
= board_coords
[1:]
sgffirst
= chr(ord(letter
) - 1)
sgfsecond
= chr(ord("a") + int(size
) - int(digits
))
return sgffirst
+ sgfsecond
# outfile File to write to
# infile File to read from
def __init__(self
, command
):
infile
, outfile
= popen2
.popen2(command
)
sys
.stderr
.write("GTP command: " + cmd
+ "\n")
self
.outfile
.write(cmd
+ "\n\n")
line
= self
.infile
.readline()
line
= self
.infile
.readline()
sys
.stderr
.write("Reply: " + line
+ "\n")
# Remove trailing newline from the result
return "ERROR: GTP Command failed: " + result
[2:]
return "ERROR: Unrecognized answer: " + result
# connection GTP_connection
def __init__(self
, command
):
self
.connection
= GTP_connection(command
)
protocol_version
= self
.connection
.exec_cmd("protocol_version")
if protocol_version
[:5] != "ERROR":
self
.protocol_version
= protocol_version
self
.protocol_version
= "1"
def is_known_command(self
, command
):
return self
.connection
.exec_cmd("known_command " + command
) == "true"
def genmove(self
, color
):
if color
[0] in ["b", "B"]:
elif color
[0] in ["w", "W"]:
if self
.protocol_version
== "1":
command
= "genmove_" + command
command
= "genmove " + command
return self
.connection
.exec_cmd(command
)
if self
.protocol_version
== "1":
self
.connection
.exec_cmd("black " + move
)
self
.connection
.exec_cmd("play black " + move
)
if self
.protocol_version
== "1":
self
.connection
.exec_cmd("white " + move
)
self
.connection
.exec_cmd("play white " + move
)
self
.connection
.exec_cmd("komi " + komi
)
def boardsize(self
, size
):
self
.connection
.exec_cmd("boardsize " + size
)
if self
.protocol_version
!= "1":
self
.connection
.exec_cmd("clear_board")
def handicap(self
, handicap
, handicap_type
):
if handicap_type
== "fixed":
result
= self
.connection
.exec_cmd("fixed_handicap %d" % (handicap
))
result
= self
.connection
.exec_cmd("place_free_handicap %d"
return string
.split(result
, " ")
def loadsgf(self
, endgamefile
, move_number
):
self
.connection
.exec_cmd(string
.join(["loadsgf", endgamefile
,
def list_stones(self
, color
):
return string
.split(self
.connection
.exec_cmd("list_stones " + color
), " ")
return self
.connection
.exec_cmd("quit")
board
= self
.connection
.exec_cmd("showboard")
if board
and (board
[0] == "\n"):
def get_random_seed(self
):
result
= self
.connection
.exec_cmd("get_random_seed")
if result
[:5] == "ERROR":
def set_random_seed(self
, seed
):
self
.connection
.exec_cmd("set_random_seed " + seed
)
def get_program_name(self
):
return self
.connection
.exec_cmd("name") + " " + \
self
.connection
.exec_cmd("version")
return self
.connection
.exec_cmd("final_score")
return self
.final_score(self
)
if (self
.is_known_command("cputime")):
return self
.connection
.exec_cmd("cputime")
def __init__(self
, whitecommand
, blackcommand
, size
, komi
, handicap
,
handicap_type
, endgamefile
):
self
.whiteplayer
= GTP_player(whitecommand
)
self
.blackplayer
= GTP_player(blackcommand
)
self
.handicap_type
= handicap_type
self
.endgamefile
= endgamefile
self
.init_endgame_contest_game()
def init_endgame_contest_game(self
):
infile
= open(self
.endgamefile
)
print "Couldn't read " + self
.endgamefile
sgflines
= infile
.readlines()
size
= re
.compile("SZ\[[0-9]+\]")
move
= re
.compile(";[BW]\[[a-z]{0,2}\]")
match
= size
.search(line
)
self
.size
= match
.group()[3:-1]
match
= move
.search(line
)
sgf_start
.append("A" + match
.group()[1:])
line
= line
[match
.end():]
match
= move
.search(line
)
self
.endgame_start
= len(sgf_start
) - endgame_start_at
self
.sgffilestart
= ";" + string
.join(
sgf_start
[:self
.endgame_start
-1], "") + "\n"
if self
.endgame_start
% 2 == 0:
def get_position_from_engine(self
, engine
):
black_stones
= engine
.list_stones("black")
white_stones
= engine
.list_stones("white")
if len(black_stones
) > 0:
self
.sgffilestart
+= "AB"
for stone
in black_stones
:
self
.sgffilestart
+= "[%s]" % coords_to_sgf(self
.size
, stone
)
self
.sgffilestart
+= "\n"
if len(white_stones
) > 0:
self
.sgffilestart
+= "AW"
for stone
in white_stones
:
self
.sgffilestart
+= "[%s]" % coords_to_sgf(self
.size
, stone
)
self
.sgffilestart
+= "\n"
def writesgf(self
, sgffilename
):
"Write the game to an SGF file after a game"
outfile
= open(sgffilename
, "w")
print "Couldn't create " + sgffilename
black_name
= self
.blackplayer
.get_program_name()
white_name
= self
.whiteplayer
.get_program_name()
black_seed
= self
.blackplayer
.get_random_seed()
white_seed
= self
.whiteplayer
.get_random_seed()
outfile
.write("(;GM[1]FF[4]RU[Japanese]SZ[%s]HA[%s]KM[%s]RE[%s]\n" %
(size
, handicap
, komi
, result
))
outfile
.write("PW[%s (random seed %s)]PB[%s (random seed %s)]\n" %
(white_name
, white_seed
, black_name
, black_seed
))
outfile
.write(self
.sgffilestart
)
for stone
in self
.handicap_stones
:
outfile
.write("[%s]" %(coords_to_sgf(size
, stone
)))
to_play
= self
.first_to_play
sgfmove
= coords_to_sgf(size
, move
)
outfile
.write(";%s[%s]\n" % (to_play
, sgfmove
))
def set_handicap(self
, handicap
):
self
.whiteplayer
= self
.blackplayer
print "Setting boardsize and komi for black\n"
self
.blackplayer
.boardsize(self
.size
)
self
.blackplayer
.komi(self
.komi
)
print "Setting boardsize and komi for white\n"
self
.whiteplayer
.boardsize(self
.size
)
self
.whiteplayer
.komi(self
.komi
)
self
.handicap_stones
= []
if self
.endgamefile
== "":
self
.handicap_stones
= self
.blackplayer
.handicap(self
.handicap
, self
.handicap_type
)
for stone
in self
.handicap_stones
:
self
.whiteplayer
.black(stone
)
self
.blackplayer
.loadsgf(self
.endgamefile
, self
.endgame_start
)
self
.blackplayer
.set_random_seed("0")
self
.whiteplayer
.loadsgf(self
.endgamefile
, self
.endgame_start
)
self
.whiteplayer
.set_random_seed("0")
if self
.blackplayer
.is_known_command("list_stones"):
self
.get_position_from_engine(self
.blackplayer
)
elif self
.whiteplayer
.is_known_command("list_stones"):
self
.get_position_from_engine(self
.whiteplayer
)
to_play
= self
.first_to_play
move
= self
.blackplayer
.genmove("black")
won_by_resignation
= "W+Resign"
if string
.lower(move
[:4]) == "pass":
self
.whiteplayer
.black(move
)
print "Black plays " + move
move
= self
.whiteplayer
.genmove("white")
won_by_resignation
= "B+Resign"
if string
.lower(move
[:4]) == "pass":
self
.blackplayer
.white(move
)
print "White plays " + move
print self
.whiteplayer
.showboard() + "\n"
if won_by_resignation
== "":
self
.resultw
= self
.whiteplayer
.final_score()
self
.resultb
= self
.blackplayer
.final_score()
self
.resultw
= won_by_resignation
;
self
.resultb
= won_by_resignation
;
# if self.resultb == self.resultw:
# print "Result: ", self.resultw
# print "Result according to W: ", self.resultw
# print "Result according to B: ", self.resultb
# FIXME: $self->writesgf($sgffile) if defined $sgffile;
return (self
.resultw
, self
.resultb
)
cputime
["white"] = self
.whiteplayer
.cputime()
cputime
["black"] = self
.blackplayer
.cputime()
def __init__(self
, whitecommand
, blackcommand
, size
, komi
, handicap
,
handicap_type
, streak_length
, endgamefilelist
):
self
.white
= whitecommand
self
.black
= blackcommand
self
.handicap_type
= handicap_type
self
.streak_length
= streak_length
self
.endgamefilelist
= endgamefilelist
def endgame_contest(self
, sgfbase
):
for endgamefile
in self
.endgamefilelist
:
game1
= GTP_game(self
.white
, self
.black
, self
.size
, self
.komi
,
game2
= GTP_game(self
.black
, self
.white
, self
.size
, self
.komi
,
print "Replaying", endgamefile
print "Black:", self
.black
print "White:", self
.white
result1
= game1
.result()[0]
plain_result1
= re
.search(r
"([BW]\+)([0-9]*\.[0-9]*)", result1
)
result1_float
= float(plain_result1
.group(2))
plain_result1
= re
.search(r
"(0)", "0")
print "Replaying", endgamefile
print "Black:", self
.white
print "White:", self
.black
result2
= game2
.result()[1]
plain_result2
= re
.search(r
"([BW]\+)([0-9]*\.[0-9]*)", result2
)
result2_float
= float(plain_result2
.group(2))
plain_result2
= re
.search(r
"(0)", "0")
results
.append(result1_float
- result2_float
)
print endgamefile
+ ":", plain_result1
.group(), \
plain_result2
.group(), "Difference:",
print result1_float
- result2_float
print endgamefile
+": Same result:", plain_result1
.group()
sgffilename
= "%s%03d" % (sgfbase
, i
)
game1
.writesgf(sgffilename
+ "_1.sgf")
game2
.writesgf(sgffilename
+ "_2.sgf")
def play(self
, games
, sgfbase
):
game
= GTP_game(self
.white
, self
.black
,
self
.size
, self
.komi
, self
.handicap
,
sgffilename
= "%s%03d.sgf" % (sgfbase
, i
+ 1)
if result
[0] == result
[1]:
print "Game %d: %s" % (i
+ 1, result
[0])
print "Game %d: %s %s" % (i
+ 1, result
[0], result
[1])
if result
[0][0] == last_color
:
elif result
[0][0] != "0":
last_color
= result
[0][0]
if last_streak
== self
.streak_length
:
print "White wins too often. Increasing handicap to %d" \
print "Black wins too often. Decreasing handicap to %d" \
print "Black looks stronger than white. Swapping colors and setting handicap to 2"
game
.set_handicap(self
.handicap
)
# ================================================================
twogtp --white \'<path to program 1> --mode gtp [program options]\' \\
--black \'<path to program 2> --mode gtp [program options]\' \\
--verbose 1 (to list moves) or --verbose 2 (to draw board)
--adjust-handicap <length> (change handicap by 1 after <length> wins
--size <board size> (default 19)
--games <number of games to play> (default 1)
--sgfbase <filename> (create sgf files with sgfbase as basename)
--endgame <moves before end> (endgame contest - add filenames of
games to be replayed after last option)
(opts
, params
) = getopt(sys
.argv
[1:], "",
elif opt
== "--boardsize" or opt
== "--size":
elif opt
== "--handicap":
elif opt
== "--free-handicap":
elif opt
== "--adjust-handicap":
streak_length
= int(value
)
endgame_start_at
= int(value
)
if endgame_start_at
!= 0:
endgame_filelist
= params
if black
== "" or white
== "":
if handicap
> 1 and streak_length
== -1:
match
= GTP_match(white
, black
, size
, komi
, handicap
, handicap_type
,
streak_length
, endgame_filelist
)
if endgame_filelist
!= []:
results
= match
.endgame_contest(sgfbase
)
print "%d games, %d wins for black, %d wins for white." \
% (len(results
), win_black
, win_white
)
results
, cputimes
= match
.play(games
, sgfbase
)
for resw
, resb
in results
:
print "Game %d: %s" % (i
, resw
)
print "Game %d: %s %s" % (i
, resb
, resw
)
if (cputimes
["white"] != "0"):
print "White: %ss CPU time" % cputimes
["white"]
if (cputimes
["black"] != "0"):
print "Black: %ss CPU time" % cputimes
["black"]