827 lines
27 KiB
Python
Executable File
827 lines
27 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
# This program is part of the Mindstab GO server package #
|
|
# #
|
|
# See http://ai.mindstab.net/ for more information. #
|
|
# #
|
|
# Copyright 2007 by Dan Ballard, Robert Hausch, and ai.mindstab.net #
|
|
# #
|
|
# It is mostly just a modification of twogtp.py from GnuGo, #
|
|
# origional licence below #
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
# 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 and 2007 #
|
|
# 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 #
|
|
# for more details. #
|
|
# #
|
|
# 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. #
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
|
|
from getopt import *
|
|
import sys
|
|
import string
|
|
import re
|
|
import socket
|
|
import netpipe
|
|
import os
|
|
import time
|
|
import MySQLdb
|
|
|
|
debug = 0
|
|
|
|
# set to True if hosting statss website
|
|
use_sql=False;
|
|
sql_db = "ai_go";
|
|
sql_user = "ai_go";
|
|
sql_pw = "password";
|
|
sgf_to_png = "/path/to/gogui-thumbnailer";
|
|
|
|
if (use_sql):
|
|
conn = MySQLdb.connect (host = "localhost", user = sql_user, passwd = sql_pw, db = sql_db)
|
|
cursor = conn.cursor ()
|
|
|
|
|
|
def coords_to_sgf(size, board_coords):
|
|
global debug
|
|
|
|
board_coords = string.lower(board_coords)
|
|
if board_coords[:4] == "pass":
|
|
return ""
|
|
if debug:
|
|
print "Coords: <" + board_coords + ">"
|
|
letter = board_coords[0]
|
|
digits = board_coords[1:]
|
|
if letter > "i":
|
|
sgffirst = chr(ord(letter) - 1)
|
|
else:
|
|
sgffirst = letter
|
|
sgfsecond = chr(ord("a") + int(size) - int(digits))
|
|
return sgffirst + sgfsecond
|
|
|
|
|
|
|
|
class GTP_connection:
|
|
|
|
#
|
|
# Class members:
|
|
# outfile File to write to
|
|
# infile File to read from
|
|
|
|
def __init__(self, np):
|
|
self.np = np
|
|
|
|
|
|
def exec_cmd(self, cmd):
|
|
global debug
|
|
|
|
if debug:
|
|
sys.stderr.write("GTP command: " + cmd + "\n")
|
|
|
|
try:
|
|
self.np.send(cmd)
|
|
result = self.np.receive()
|
|
except:
|
|
return "ERROR: socket error"
|
|
|
|
#if debug:
|
|
# sys.stderr.write("Reply: " + line + "\n")
|
|
|
|
# Remove trailing newline from the result
|
|
if result[-1] == "\n":
|
|
result = result[:-1]
|
|
|
|
if len(result) == 0:
|
|
return "ERROR: len = 0"
|
|
if (result[0] == "?"):
|
|
return "ERROR: GTP Command failed: " + result[2:]
|
|
if (result[0] == "="):
|
|
return result[2:]
|
|
return "ERROR: Unrecognized answer: " + result
|
|
|
|
|
|
class GTP_player:
|
|
|
|
# Class members:
|
|
# connection GTP_connection
|
|
|
|
def __init__(self, command):
|
|
global use_sql
|
|
self.connection = GTP_connection(command)
|
|
protocol_version = self.connection.exec_cmd("protocol_version")
|
|
if protocol_version[:5] != "ERROR":
|
|
self.protocol_version = protocol_version
|
|
else:
|
|
self.protocol_version = "1"
|
|
self.name = self.connection.exec_cmd("name").strip()
|
|
self.version = self.connection.exec_cmd("version").strip()
|
|
|
|
if use_sql and self.name[:5] != "ERROR":
|
|
self.insert_bot_into_db()
|
|
|
|
def insert_bot_into_db(self):
|
|
global cursor
|
|
cursor.execute("SELECT * from bots where name=\"%s\" and version=\"%s\";" % (self.name, self.version))
|
|
if cursor.rowcount == 0:
|
|
cursor.execute("INSERT into bots (name, version) values(\"%s\", \"%s\");" %(self.name, self.version))
|
|
cursor.execute("SELECT * from bots where name=\"%s\" and version=\"%s\";" % (self.name, self.version))
|
|
row = cursor.fetchone ()
|
|
self.bot_id = int(row[0])
|
|
|
|
def send_score(self, score):
|
|
return self.connection.exec_cmd("game_score " + score)
|
|
|
|
def is_known_command(self, command):
|
|
resp = self.connection.exec_cmd("known_command " + command)
|
|
return resp == "true" or resp[:1] == "1"
|
|
|
|
def genmove(self, color):
|
|
if color[0] in ["b", "B"]:
|
|
command = "black"
|
|
elif color[0] in ["w", "W"]:
|
|
command = "white"
|
|
if self.protocol_version == "1":
|
|
command = "genmove_" + command
|
|
else:
|
|
command = "genmove " + command
|
|
|
|
return self.connection.exec_cmd(command)
|
|
|
|
def black(self, move):
|
|
if self.protocol_version == "1":
|
|
self.connection.exec_cmd("black " + move)
|
|
else:
|
|
self.connection.exec_cmd("play black " + move)
|
|
|
|
def white(self, move):
|
|
if self.protocol_version == "1":
|
|
self.connection.exec_cmd("white " + move)
|
|
else:
|
|
self.connection.exec_cmd("play white " + move)
|
|
|
|
def komi(self, komi):
|
|
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))
|
|
else:
|
|
result = self.connection.exec_cmd("place_free_handicap %d"
|
|
% (handicap))
|
|
|
|
return string.split(result, " ")
|
|
|
|
def loadsgf(self, endgamefile, move_number):
|
|
self.connection.exec_cmd(string.join(["loadsgf", endgamefile,
|
|
str(move_number)]))
|
|
|
|
def list_stones(self, color):
|
|
return string.split(self.connection.exec_cmd("list_stones " + color), " ")
|
|
|
|
def quit(self):
|
|
return self.connection.exec_cmd("quit")
|
|
|
|
def showboard(self):
|
|
board = self.connection.exec_cmd("showboard")
|
|
if board and (board[0] == "\n"):
|
|
board = board[1:]
|
|
return board
|
|
|
|
def get_random_seed(self):
|
|
result = self.connection.exec_cmd("get_random_seed")
|
|
if result[:5] == "ERROR":
|
|
return "unknown"
|
|
return result
|
|
|
|
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")
|
|
|
|
def final_score(self, score_file):
|
|
print "/usr/games/bin/gnugo --score finish -l " + score_file + " 2> /dev/null"
|
|
score = os.popen("/usr/games/bin/gnugo --score finish -l " + score_file + " 2> /dev/null").read()
|
|
#print "SCORE: '" + score + "'"
|
|
result = score[0]
|
|
arr = score.split(" ")
|
|
result = score[0] + '+' + arr[3] + "\n"
|
|
print "RESULT '" + result + "'"
|
|
return result
|
|
|
|
def score(self):
|
|
return self.final_score(self)
|
|
|
|
def cputime(self):
|
|
if (self.is_known_command("cputime")):
|
|
return self.connection.exec_cmd("cputime").strip()
|
|
else:
|
|
return "0"
|
|
|
|
|
|
class GTP_game:
|
|
|
|
# Class members:
|
|
# whiteplayer GTP_player
|
|
# blackplayer GTP_player
|
|
# size int
|
|
# komi float
|
|
# handicap int
|
|
# handicap_type string
|
|
# handicap_stones int
|
|
# moves list of string
|
|
# resultw
|
|
# resultb
|
|
|
|
def __init__(self, whitecommand, blackcommand, size, komi, handicap,
|
|
handicap_type, endgamefile):
|
|
self.whiteplayer = GTP_player(whitecommand)
|
|
self.blackplayer = GTP_player(blackcommand)
|
|
self.size = size
|
|
self.komi = komi
|
|
self.handicap = handicap
|
|
self.handicap_type = handicap_type
|
|
self.endgamefile = endgamefile
|
|
self.sgffilestart = ""
|
|
if endgamefile != "":
|
|
self.init_endgame_contest_game()
|
|
else:
|
|
self.sgffilestart = ""
|
|
|
|
def init_endgame_contest_game(self):
|
|
infile = open(self.endgamefile)
|
|
if not infile:
|
|
print "Couldn't read " + self.endgamefile
|
|
sys.exit(2)
|
|
sgflines = infile.readlines()
|
|
infile.close
|
|
size = re.compile("SZ\[[0-9]+\]")
|
|
move = re.compile(";[BW]\[[a-z]{0,2}\]")
|
|
sgf_start = []
|
|
for line in sgflines:
|
|
match = size.search(line)
|
|
if match:
|
|
self.size = match.group()[3:-1]
|
|
match = move.search(line)
|
|
while match:
|
|
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:
|
|
self.first_to_play = "W"
|
|
else:
|
|
self.first_to_play = "B"
|
|
|
|
def get_position_from_engine(self, engine):
|
|
black_stones = engine.list_stones("black")
|
|
white_stones = engine.list_stones("white")
|
|
self.sgffilestart = ";"
|
|
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"
|
|
|
|
size = self.size
|
|
outfile = open(sgffilename, "w")
|
|
if not outfile:
|
|
print "Couldn't create " + sgffilename
|
|
return
|
|
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()
|
|
handicap = self.handicap
|
|
komi = self.komi
|
|
result = 0#self.resultw
|
|
|
|
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)
|
|
|
|
if handicap > 1:
|
|
outfile.write("AB");
|
|
for stone in self.handicap_stones:
|
|
outfile.write("[%s]" %(coords_to_sgf(size, stone)))
|
|
outfile.write("PL[W]\n")
|
|
|
|
to_play = self.first_to_play
|
|
|
|
for move in self.moves:
|
|
sgfmove = coords_to_sgf(size, move)
|
|
outfile.write(";%s[%s]\n" % (to_play, sgfmove))
|
|
if to_play == "B":
|
|
to_play = "W"
|
|
else:
|
|
to_play = "B"
|
|
outfile.write(")\n")
|
|
outfile.close
|
|
|
|
def set_handicap(self, handicap):
|
|
self.handicap = handicap
|
|
|
|
def swap_players(self):
|
|
temp = self.whiteplayer
|
|
self.whiteplayer = self.blackplayer
|
|
self.blackplayer = temp
|
|
|
|
def play(self, sgffile):
|
|
global verbose
|
|
global cursor
|
|
global use_sql
|
|
global sgf_to_png
|
|
|
|
if verbose >= 1:
|
|
print "Setting boardsize and komi for black\n"
|
|
self.blackplayer.boardsize(self.size)
|
|
self.blackplayer.komi(self.komi)
|
|
|
|
if verbose >= 1:
|
|
print "Setting boardsize and komi for white\n"
|
|
self.whiteplayer.boardsize(self.size)
|
|
self.whiteplayer.komi(self.komi)
|
|
|
|
self.handicap_stones = []
|
|
|
|
if self.endgamefile == "":
|
|
if self.handicap < 2:
|
|
self.first_to_play = "B"
|
|
else:
|
|
self.handicap_stones = self.blackplayer.handicap(self.handicap, self.handicap_type)
|
|
for stone in self.handicap_stones:
|
|
self.whiteplayer.black(stone)
|
|
self.first_to_play = "W"
|
|
else:
|
|
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
|
|
|
|
self.moves = []
|
|
passes = 0
|
|
won_by_resignation = ""
|
|
while passes < 2:
|
|
if to_play == "B":
|
|
move = self.blackplayer.genmove("black")
|
|
if move[:5] == "ERROR":
|
|
# FIXME: write_sgf
|
|
sys.exit(1)
|
|
|
|
if move[:6] == "resign":
|
|
if verbose >= 1:
|
|
print "Black resigns"
|
|
won_by_resignation = "W+Resign"
|
|
break
|
|
else:
|
|
self.moves.append(move)
|
|
if string.lower(move[:4]) == "pass":
|
|
passes = passes + 1
|
|
self.whiteplayer.black(move);
|
|
if verbose >= 1:
|
|
print "Black passes"
|
|
else:
|
|
passes = 0
|
|
self.whiteplayer.black(move)
|
|
if verbose >= 1:
|
|
print "Black plays " + move
|
|
to_play = "W"
|
|
else:
|
|
move = self.whiteplayer.genmove("white")
|
|
if move[:5] == "ERROR":
|
|
# FIXME: write_sgf
|
|
sys.exit(1)
|
|
|
|
if move[:6] == "resign":
|
|
if verbose >= 1:
|
|
print "White resigns"
|
|
won_by_resignation = "B+Resign"
|
|
break
|
|
else:
|
|
self.moves.append(move)
|
|
if string.lower(move[:4]) == "pass":
|
|
passes = passes + 1
|
|
self.blackplayer.white(move)
|
|
if verbose >= 1:
|
|
print "White passes"
|
|
else:
|
|
passes = 0
|
|
self.blackplayer.white(move)
|
|
if verbose >= 1:
|
|
print "White plays " + move
|
|
to_play = "B"
|
|
|
|
if verbose >= 2:
|
|
print self.whiteplayer.showboard() + "\n"
|
|
|
|
score_path= "/home/ai/ai.mindstab.net/htdocs/go/sgf/"
|
|
|
|
file_prefix = str(int(time.time())) +'.'+ str(os.getpid())
|
|
score_file = file_prefix+".sgf";
|
|
image_file = file_prefix+".png";
|
|
|
|
self.writesgf(score_path + score_file)
|
|
|
|
if won_by_resignation == "":
|
|
self.resultw = self.whiteplayer.final_score(score_path +score_file)
|
|
self.resultb = self.resultw #self.blackplayer.final_score()
|
|
else:
|
|
self.resultw = won_by_resignation;
|
|
self.resultb = won_by_resignation;
|
|
if self.whiteplayer.protocol_version[0:6] == "gtp2ip":
|
|
self.whiteplayer.send_score(self.resultw)
|
|
if self.blackplayer.protocol_version[0:6] == "gtp2ip":
|
|
self.blackplayer.send_score(self.resultw)
|
|
|
|
winner = self.resultw[:1]
|
|
score = float((self.resultw[2:]))
|
|
if winner == "w" or winner == "W":
|
|
score = 0 - score
|
|
|
|
if use_sql:
|
|
cursor.execute("INSERT INTO matchs (black_id, white_id, score, date, black_time, white_time, sgffile, handicap) values (%d, %d, %f, now(), \"%s\", \"%s\", \"%s\", %d);" % (self.blackplayer.bot_id, self.whiteplayer.bot_id, score, self.blackplayer.cputime(), self.whiteplayer.cputime(), score_file, 0) )
|
|
os.popen(sgf_to_png + ' ' + score_path + score_file + ' ' + score_path + image_file).read();
|
|
|
|
|
|
# if self.resultb == self.resultw:
|
|
# print "Result: ", self.resultw
|
|
# else:
|
|
# print "Result according to W: ", self.resultw
|
|
# print "Result according to B: ", self.resultb
|
|
# FIXME: $self->writesgf($sgffile) if defined $sgffile;
|
|
#if sgffile != "":
|
|
# print "writing " + sgffile
|
|
# self.writesgf(sgffile)
|
|
|
|
def result(self):
|
|
return (self.resultw, self.resultb)
|
|
|
|
def cputime(self):
|
|
cputime = {}
|
|
cputime["white"] = self.whiteplayer.cputime()
|
|
cputime["black"] = self.blackplayer.cputime()
|
|
return cputime
|
|
|
|
def quit(self):
|
|
self.blackplayer.quit()
|
|
self.whiteplayer.quit()
|
|
|
|
|
|
class GTP_match:
|
|
|
|
# Class members:
|
|
# black
|
|
# white
|
|
# size
|
|
# komi
|
|
# handicap
|
|
# handicap_type
|
|
|
|
def __init__(self, whitecommand, blackcommand, size, komi, handicap,
|
|
handicap_type, streak_length, endgamefilelist):
|
|
self.white = whitecommand
|
|
self.black = blackcommand
|
|
self.size = size
|
|
self.komi = komi
|
|
self.handicap = handicap
|
|
self.handicap_type = handicap_type
|
|
self.streak_length = streak_length
|
|
self.endgamefilelist = endgamefilelist
|
|
|
|
def endgame_contest(self, sgfbase):
|
|
results = []
|
|
i = 1
|
|
for endgamefile in self.endgamefilelist:
|
|
game1 = GTP_game(self.white, self.black, self.size, self.komi,
|
|
0, "", endgamefile)
|
|
game2 = GTP_game(self.black, self.white, self.size, self.komi,
|
|
0, "", endgamefile)
|
|
if verbose:
|
|
print "Replaying", endgamefile
|
|
print "Black:", self.black
|
|
print "White:", self.white
|
|
game1.play("")
|
|
result1 = game1.result()[0]
|
|
if result1 != "0":
|
|
plain_result1 = re.search(r"([BW]\+)([0-9]*\.[0-9]*)", result1)
|
|
result1_float = float(plain_result1.group(2))
|
|
else:
|
|
plain_result1 = re.search(r"(0)", "0")
|
|
result1_float = 0.0
|
|
if result1[0] == "B":
|
|
result1_float *= -1
|
|
if verbose:
|
|
print "Result:", result1
|
|
print "Replaying", endgamefile
|
|
print "Black:", self.white
|
|
print "White:", self.black
|
|
game2.play("")
|
|
result2 = game2.result()[1]
|
|
if verbose:
|
|
print "Result:", result2
|
|
if result2 != "0":
|
|
plain_result2 = re.search(r"([BW]\+)([0-9]*\.[0-9]*)", result2)
|
|
result2_float = float(plain_result2.group(2))
|
|
else:
|
|
plain_result2 = re.search(r"(0)", "0")
|
|
result2_float = 0.0
|
|
|
|
if result2[0] == "B":
|
|
result2_float *= -1
|
|
results.append(result1_float - result2_float)
|
|
if (result1 != result2):
|
|
print endgamefile+ ":", plain_result1.group(), \
|
|
plain_result2.group(), "Difference:",
|
|
print result1_float - result2_float
|
|
else:
|
|
print endgamefile+": Same result:", plain_result1.group()
|
|
sgffilename = "%s%03d" % (sgfbase, i)
|
|
print "writing SGFS"
|
|
game1.writesgf(sgffilename + "_1.sgf")
|
|
game2.writesgf(sgffilename + "_2.sgf")
|
|
game1.quit()
|
|
game2.quit()
|
|
i += 1
|
|
return results
|
|
|
|
def play(self, games, sgfbase):
|
|
last_color = ""
|
|
last_streak = 0
|
|
game = GTP_game(self.white, self.black,
|
|
self.size, self.komi, self.handicap,
|
|
self.handicap_type, "")
|
|
results = []
|
|
for i in range(games):
|
|
if sgfbase != "":
|
|
sgffilename = "%s%03d.sgf" % (sgfbase, i + 1)
|
|
else:
|
|
sgffilename = ""
|
|
game.play(sgffilename)
|
|
result = game.result()
|
|
if result[0] == result[1]:
|
|
print "Game %d: %s" % (i + 1, result[0])
|
|
else:
|
|
print "Game %d: %s %s" % (i + 1, result[0], result[1])
|
|
|
|
if result[0][0] == last_color:
|
|
last_streak += 1
|
|
elif result[0][0] != "0":
|
|
last_color = result[0][0]
|
|
last_streak = 1
|
|
|
|
if last_streak == self.streak_length:
|
|
if last_color == "W":
|
|
self.handicap += 1
|
|
if self.handicap == 1:
|
|
self.handicap = 2
|
|
print "White wins too often. Increasing handicap to %d" \
|
|
% (self.handicap)
|
|
else:
|
|
if self.handicap > 0:
|
|
self.handicap -= 1
|
|
if self.handicap == 1:
|
|
self.handicap = 0
|
|
print "Black wins too often. Decreasing handicap to %d" \
|
|
% (self.handicap)
|
|
else:
|
|
self.handicap = 2
|
|
game.swap_players()
|
|
print "Black looks stronger than white. Swapping colors and setting handicap to 2"
|
|
game.set_handicap(self.handicap)
|
|
last_color = ""
|
|
last_streak = 0
|
|
results.append(result)
|
|
cputime = game.cputime()
|
|
game.quit()
|
|
return results, cputime
|
|
|
|
|
|
# ================================================================
|
|
# Main program
|
|
#
|
|
|
|
|
|
# Default values
|
|
#
|
|
|
|
white = ""
|
|
black = ""
|
|
port = 0
|
|
komi = ""
|
|
size = "19"
|
|
handicap = 0
|
|
handicap_type = "fixed"
|
|
streak_length = -1
|
|
endgame_start_at = 0
|
|
|
|
games = 1
|
|
sgfbase = ""
|
|
|
|
verbose = 0
|
|
|
|
helpstring = """
|
|
|
|
Run with:
|
|
|
|
gtpserver.py --port <port to listen on> [twogtp options]
|
|
|
|
Possible twogtp options:
|
|
|
|
--verbose 1 (to list moves) or --verbose 2 (to draw board)
|
|
--komi <amount>
|
|
--handicap <amount>
|
|
--free-handicap <amount>
|
|
--adjust-handicap <length> (change handicap by 1 after <length> wins
|
|
in a row)
|
|
--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)
|
|
"""
|
|
|
|
def usage():
|
|
print helpstring
|
|
sys.exit(1)
|
|
|
|
#print sys.argv
|
|
|
|
try:
|
|
(opts, params) = getopt(sys.argv[1:], "",
|
|
["port=",
|
|
"black=",
|
|
"white=",
|
|
"verbose=",
|
|
"komi=",
|
|
"boardsize=",
|
|
"size=",
|
|
"handicap=",
|
|
"free-handicap=",
|
|
"adjust-handicap=",
|
|
"games=",
|
|
"sgfbase=",
|
|
"endgame=",
|
|
])
|
|
except:
|
|
usage()
|
|
|
|
for opt, value in opts:
|
|
if opt == "--black":
|
|
black = value
|
|
elif opt == "--white":
|
|
white = value
|
|
elif opt == "--port":
|
|
port = value
|
|
elif opt == "--verbose":
|
|
verbose = int(value)
|
|
elif opt == "--komi":
|
|
komi = value
|
|
elif opt == "--boardsize" or opt == "--size":
|
|
size = value
|
|
elif opt == "--handicap":
|
|
handicap = int(value)
|
|
handicap_type = "fixed"
|
|
elif opt == "--free-handicap":
|
|
handicap = int(value)
|
|
handicap_type = "free"
|
|
elif opt == "--adjust-handicap":
|
|
streak_length = int(value)
|
|
elif opt == "--games":
|
|
games = int(value)
|
|
elif opt == "--sgfbase":
|
|
sgfbase = value
|
|
elif opt == "--endgame":
|
|
endgame_start_at = int(value)
|
|
|
|
if endgame_start_at != 0:
|
|
endgame_filelist = params
|
|
else:
|
|
endgame_filelist = []
|
|
if params != []:
|
|
usage()
|
|
|
|
|
|
if port == 0: #black == "" or white == "":
|
|
usage()
|
|
|
|
if komi == "":
|
|
if handicap > 1 and streak_length == -1:
|
|
komi = "0.5"
|
|
else:
|
|
komi = "5.5"
|
|
|
|
|
|
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
serversocket.bind( ('', int(port)))
|
|
serversocket.settimeout(30)
|
|
serversocket.listen(2)
|
|
try:
|
|
print "%s waiting for gtpclient 1 on port %d" % (os.getpid(), int(port))
|
|
(whitesocket, whiteaddress) = serversocket.accept()
|
|
print "gtpclient 1 connected from %s!" % (str(whiteaddress))
|
|
except socket.timeout:
|
|
print "%s Timeout waiting for client 1, exiting..." % (os.getpid())
|
|
serversocket.close()
|
|
sys.exit(-1)
|
|
|
|
whitenp = netpipe.netpipe(whitesocket)
|
|
try:
|
|
print "%s waiting for gtpclient 2 on port %d" % (os.getpid(), int(port))
|
|
(blacksocket, blackaddress) = serversocket.accept()
|
|
print "gtpclient 2 connected from %s!" % (str(blackaddress))
|
|
except socket.timeout:
|
|
print "%s Timeout waiting for client 2, exiting..." % (os.getpid())
|
|
whitenp.send("quit")
|
|
whitenp.close()
|
|
serversocket.close()
|
|
sys.exit(-1)
|
|
|
|
print "%s playing match!" % (os.getpid())
|
|
|
|
blacknp = netpipe.netpipe(blacksocket)
|
|
|
|
match = GTP_match(whitenp, blacknp, size, komi, handicap, handicap_type,
|
|
streak_length, endgame_filelist)
|
|
if endgame_filelist != []:
|
|
results = match.endgame_contest(sgfbase)
|
|
win_black = 0
|
|
win_white = 0
|
|
for res in results:
|
|
print res
|
|
if res > 0.0:
|
|
win_white += 1
|
|
elif res < 0.0:
|
|
win_black += 1
|
|
print "%d games, %d wins for black, %d wins for white." \
|
|
% (len(results), win_black, win_white)
|
|
|
|
else:
|
|
results, cputimes = match.play(games, sgfbase)
|
|
|
|
i = 0
|
|
for resw, resb in results:
|
|
i = i + 1
|
|
if resw == resb:
|
|
print "Game %d: %s" % (i, resw)
|
|
else:
|
|
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"]
|
|
|
|
|
|
whitenp.close()
|
|
blacknp.close()
|
|
serversocket.close()
|
|
if use_sql:
|
|
cursor.close ()
|
|
conn.close ()
|
|
|