webgo/gnugo.py

347 lines
8.6 KiB
Python
Raw Permalink Normal View History

import string,sys,popen2,re
import helper,filehandling
gnugocommand = "/usr/games/gnugo --mode gtp"
showboard = """
=
A B C D E F G
7 . . . . . . . 7
6 . . . . . . . 6
5 . . + . + . . 5
4 . . X X . . . 4
3 . . O . + . . 3
2 . . . . . . . 2 WHITE (O) has captured 0 stones
1 . . . . . . . 1 BLACK (X) has captured 0 stones
A B C D E F G
"""
def is_int( str ):
""" Is the given string an integer? """
ok = 1
try:
num = int(str)
except ValueError:
ok = 0
return ok
def showboard_to_goban_dict(showboard):
"""
gets a string containing the result of the 'showboard' command from
gnugo --mode gtp.
returns a goban dictionary .
"""
gobandict= {}
showboardlist = string.split(showboard,'\n')
while '' in showboardlist:
showboardlist.remove('')
#remove '='
if string.find(showboardlist[0],"=")>=0:
showboardlist.remove(showboardlist[0])
#get boardsize
tmplist = string.split(showboardlist[0]," ")
while '' in tmplist:
tmplist.remove('')
boardsize = len(tmplist)
#remove first A B C D ...
showboardlist.remove(showboardlist[0])
#remove last A B C D ...
showboardlist.remove(showboardlist[-1])
for i in range(0,boardsize):
#make string to list
linelist = string.split(showboardlist[i],' ')
#clean list from '' entries
while '' in linelist:
linelist.remove('')
# remove numbers
for p in range(0,boardsize+1):
if is_int(linelist[p]):
linelist.remove(linelist[p])
#print linelist,'\n'
#now fill dictionary:
for j in range(0,boardsize):
if (linelist[j] == ".") or (linelist[j] == "+"):
gobandict[(i+1,j+1)] = 0 #empty
if linelist[j] == "O":
gobandict[(i+1,j+1)] = 1 #white stone
if linelist[j] == "X":
gobandict[(i+1,j+1)] = 2 #black stone
#check for info of caputered stones:
if len(linelist) > boardsize:
if 'WHITE' in linelist:
whitecapture = "white has captured %s stones." % linelist[-2]
#print whitecapture
elif 'BLACK' in linelist:
blackcapture = "black has captured %s stones." % linelist[-2]
gobandict["size"] = boardsize
return gobandict
def sgf_to_goban_dict(gobandict, filename=""):
"""
gets a goban dictionary and optionally a temporary filename.
saves string to file, loads in gnugo, does showboard, convert showboard to gobandict.
return gobandict 'lite' (just coordinates and size)
"""
sgf = gobandict["sgf"]
#generate tmpfile
if filename == "":
filename = filehandling.gen_temp_file()
#write sgf data to file
filehandling.write_file(filename,sgf)
#open connection to gnugo:
conn = GTP_connection(gnugocommand)
#load sgf file in gnugo
result = conn.exec_cmd("loadsgf "+filename)
#make move (looks like 'play black F2'
result = conn.exec_cmd("showboard")
#convert to goban dict
ret = showboard_to_goban_dict(result)
result = conn.exec_cmd("quit")
return ret
### GTP_connection is based on twogtp.py packaged with gnugo.
class GTP_connection:
"""
gets a program call string, opens an interactive session with
program called with 'program'. GTP_connection.exec_cmd(command)
returns result of command.
#
# Class members:
# outfile File to write to
# infile File to read from
"""
def __init__(self, command):
try:
infile, outfile = popen2.popen2(command)
except:
print "popen2 failed"
self.infile = infile
self.outfile = outfile
def exec_cmd(self, cmd):
self.outfile.write(cmd + "\n\n")
self.outfile.flush()
result = ""
line = self.infile.readline()
while line != "\n":
result = result + line
line = self.infile.readline()
# 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
def is_legal(gobandict,coords):
"""
gets a goban dict and a (x,y) tuple.
tests wether proposed move is legal.
returns True or False.
"""
2006-05-17 14:38:28 +02:00
"""
turn = gobandict["turn_number"]
#who's turn is it?
color = ["white","black"][turn % 2] #even turn: white plays, else black plays
"""
gamename = gobandict["name"]
color = gobandict["play"]
size = gobandict["size"]
#convert given coordinates
gnucoords = " " + helper.dict_coords_to_gnugo_coords(coords,size)
#open connection to gnugo:
conn = GTP_connection(gnugocommand)
result = conn.exec_cmd("is_legal "+ color +gnucoords)
if result == "1":
return True
else:
return False
result = conn.exec_cmd("quit")
def create_sgf_file(size, filename=""):
"""
gets: board size, optionally a temporary filename to use.
returns the content of an empty sgf file.
"""
conn = GTP_connection(gnugocommand)
2006-05-17 14:38:28 +02:00
result = conn.exec_cmd("boardsize %s" % str(size))
if filename == "":
filename = filehandling.gen_temp_file()
result = conn.exec_cmd("printsgf "+filename)
ret = filehandling.read_file(filename)
result = conn.exec_cmd("quit")
return ret
def make_move_in_sgf(gobandict,coords,filename = ""):
"""
gets: goban dict, (x,y) tuple for move, optionally a filename for tempfile.
writes the string to a file, opens gnugo, makes the move, writes new sgf
to temporary file, reads content of temporary file.
returns: sgf string of new file.
"""
size = gobandict["size"]
sgf = gobandict["sgf"]
#convert given coordinates
gnucoords = " " + helper.dict_coords_to_gnugo_coords(coords,size)
# get current player
color = gobandict["play"]
#generate tmpfile
if filename == "":
filename = filehandling.gen_temp_file()
#write sgf data to file
filehandling.write_file(filename,sgf)
#open connection to gnugo:
conn = GTP_connection(gnugocommand)
#load sgf file in gnugo
result = conn.exec_cmd("loadsgf "+filename)
#make move (looks like 'play black F2'
result = conn.exec_cmd("play "+color+gnucoords)
#save result to file
result = conn.exec_cmd("printsgf "+filename)
#read out new file
sgf = filehandling.read_file(filename)
result = conn.exec_cmd("quit")
return sgf
def parse_static_gnugo_sgf(s):
"""
gets a string containing the data saved by the gnugo "printsgf" order.
"""
2006-05-17 14:38:28 +02:00
#dictionary to return
ret = {}
#removing newlines vom given string
s = s.replace("\r\n","") #windoze
s = s.replace("\n","") #unix
#getting the board size
# looks like SZ[19]
# the pattern finds a number ([0-9]) with length (1-2) between "SZ[" and "]"
boardsize = re.search("SZ\[([0-9]{1,2})\]",s).groups()[0]
boardsize = int(boardsize)
ret["size"] = boardsize
#now fill ret with default values (== 0):
for i in range (1,boardsize+1):
for k in range (1,boardsize+1):
ret[(i,k)] = 0
#some regexp patterns:
#(?: ...) groups items
stones = "%s((?:\[[a-z][a-z]\])*)" #dont forget to set %s!
whitestones = stones % "AW"
blackstones = stones % "AB"
#getting white stones
#looks like AW[bb][cb][cc][cd][de][df]
try:
stonestring = re.search(whitestones,s).groups()[0]
except:
#we could not find anything
stonestring = ""
if stonestring != "":
ret = set_stones(stonestring,1,ret)
#getting black stones
try:
stonestring = re.search(blackstones,s).groups()[0]
except:
#we could not find anything
stonestring = ""
if stonestring != "":
ret = set_stones(stonestring,2,ret)
#who's turn is it?
if string.find(s,"PL[B]")>0:
ret["play"] = "black"
else:
ret["play"] = "white"
return ret
def set_stones(stonestring,value,ret):
"""
gets:
- string from sgf file with stone postitions in brackets: '[ab][ac]...'
- value to set
- dictionary to transform
does:
returns:
- dicionary
"""
#create a list of letters
letters = [" "]
letters.extend(list(string.letters)[0:26])
#regexp
stonestolist = "\[([a-z][a-z])\]"
rawlist = re.findall(stonestolist,stonestring)
for item in rawlist:
#fill in specific values
ret[(letters.index(item[1]),letters.index(item[0]))] = value
return ret
if __name__ == '__main__':
#showboard_to_goban_dict(showboard)
import popen2
conn = GTP_connection("gnugo --mode gtp")
result = conn.exec_cmd("boardsize 7")
result = conn.exec_cmd("genmove black")
result = conn.exec_cmd("genmove white")
result = conn.exec_cmd("printsgf test.sgf")
result = conn.exec_cmd("quit")
conn = GTP_connection("gnugo --mode gtp")
result = conn.exec_cmd("loadsgf test.sgf")
result = conn.exec_cmd("showboard")
print result,"\n----\n"
print showboard_to_goban_dict(result)
print create_sgf_file(7)
#loadsgf
#printsgf
#is_legal black F2
#dict_coords_to_gnugo_coords(coords) translates (6,6) to "F2"
#sample output
s = """(;
GM[1]
FF[4]
SZ[19]
GN[GNU Go 3.7.4 load and print]
DT[2005-10-09]
KM[5.5]
RU[Japanese]
AP[GNU Go:3.7.4]
AW[bb][cb][cc][cd][de][df]
[ag][cg][ah][ch][dh][ai][bi][ci]
AB[ba][ab][ac][bc][bd][be][ce][af][cf][bg][bh]
PL[B]
)"""
print parse_static_gnugo_sgf(s)