webgo now utilizes gnugo to evaluate moves. The game is now playable (though I dont know what happens if someone wins/finishes a game :))
This commit is contained in:
parent
2630332479
commit
cc76ec975b
12 changed files with 504 additions and 83 deletions
|
@ -45,7 +45,6 @@ body {
|
|||
height: 120px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-image: url(cryptobox-img/vault_pingu.png);
|
||||
background-position: top right;
|
||||
background-attachment: scroll;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -247,13 +246,13 @@ input#submit:hover {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* the submit buttons have to have id="goban" for the following style, for use in goban display ------------- */
|
||||
/*input#goban {
|
||||
|
||||
input {
|
||||
padding: 0px;
|
||||
border: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
*/
|
||||
|
||||
/*input#goban:hover {
|
||||
padding: 0px;
|
||||
border: 0px;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import psql,login
|
||||
import psql,login,helper
|
||||
|
||||
#helper = apache.import_module("helper")
|
||||
|
||||
def get_game_slot_of_game(player, gamename):
|
||||
"""
|
||||
|
@ -18,7 +20,7 @@ def main(req,form):
|
|||
manage the removal of game from game slots of players and
|
||||
delete game from database.
|
||||
"""
|
||||
helper.debug(req,form,str(form.keys())+" sessionid in form:"+form["sessionid"]+"<hr>")
|
||||
helper.debug(req,form,"<br>"+str(form.keys())+" sessionid in form:"+form["sessionid"]+"<br>")
|
||||
try:
|
||||
gamename = form["game"]
|
||||
except:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
-------------------- installing GnuGo ----------------------
|
||||
apt-get install gnugo
|
||||
----------------- creating the database --------------------
|
||||
#installing PostgreSQL the Debian way
|
||||
apt-get install postgresql
|
||||
|
|
|
@ -12,22 +12,30 @@ imgsource/ with the desired ones. then run generate_goban_images.py and
|
|||
the new images get written to img/.
|
||||
------------------------------------------
|
||||
overview of the files:
|
||||
main.py - the main script. Essentially a wrapper that prints the
|
||||
main.py -the main script. Essentially a wrapper that prints the
|
||||
traceback if something went wrong.
|
||||
login.py - this file contains functions for login and game
|
||||
login.py -this file contains functions for login and game
|
||||
selection/modification.
|
||||
goban.py - code for displaying a goban as a html page.
|
||||
psql.py - all functions directly accessing the database.
|
||||
init_webgo.py - re-initialize the whole game. deletes everything and
|
||||
goban.py -code for displaying a goban as a html page.
|
||||
psql.py -all functions directly accessing the database.
|
||||
init_webgo.py -re-initialize the whole game. deletes everything and
|
||||
sets some defaults.
|
||||
helper.py - small functions for string manipulation etc.
|
||||
helper.py -small functions for string manipulation etc.
|
||||
generate_goban_images.py
|
||||
- recreate the goban images. just run from console.
|
||||
-recreate the goban images. just run from console.
|
||||
gnugo.py -contains functions using gnugo in gtp mode. used for
|
||||
evaluating wether or not a move is legal.
|
||||
filehandling.py -contains functions for, well, file handling ;>
|
||||
|
||||
------------------------------------------
|
||||
coding guidelines
|
||||
tab == 4 whitespaces.
|
||||
function names are lowercase with _ as a seperator.
|
||||
function names are all lowercase with _ as a seperator.
|
||||
------------------------------------------
|
||||
how is the data saved internally:
|
||||
for each go game a table is generated. this table has a 'sgf' field containing the current board in sgf file format (needed for gnugo move validation). It also has fields for each goban field. The 'sgf' entry is the SPOT for the current state of the board.
|
||||
If the board gets changed, this means the 'sgf' file gets changed and then the changed fields in the table get
|
||||
updated accordingly. This is a bit unclean, but it means I do not have to write a sgf parser ;>
|
||||
------------------------------------------
|
||||
known bugs:
|
||||
"DatabaseError: error 'ERROR: current transaction is aborted, commands ignored until end of transaction block"
|
||||
|
|
|
@ -38,12 +38,12 @@ m6: manage users and game sessions inside database
|
|||
username and password and is then given the choice of game to play. If
|
||||
there is only one active game, directly present the goban.
|
||||
|
||||
. <--- YOU ARE HERE.
|
||||
|
||||
m7: implement Go rules
|
||||
Do not accept player moves which violate go rules. Remove beaten stones.
|
||||
Count and display the score for each player.
|
||||
|
||||
. <--- YOU ARE HERE.
|
||||
|
||||
m8: make it pretty, make it fast.
|
||||
Create nicer images replacing the old ones which were just for testing
|
||||
Find bottlenecks and optimize them for speed.
|
||||
|
|
|
@ -1 +1,6 @@
|
|||
- hoshi Bilder fuer weisse und schwarze Steine erstellen
|
||||
- remove temporarily created files ;>
|
||||
- what happens if a game is finished?
|
||||
- add a description field for a goban table
|
||||
- add a date-of-creation-creation field and a last-changed-field to goban table
|
||||
- display game list more nicely with players and last change.
|
85
filehandling.py
Normal file
85
filehandling.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
import os,tempfile,dircache,string,commands
|
||||
|
||||
|
||||
def gen_temp_dir(prefix=None):
|
||||
"""generate a secure temporary directory. returns name of dir."""
|
||||
dirname=tempfile.mkdtemp("",prefix)
|
||||
dirname+="/"
|
||||
return dirname
|
||||
|
||||
|
||||
def read_file(filename):
|
||||
"""
|
||||
read from the file whose name is given
|
||||
@param filename String : name of file to read from
|
||||
@return String: content of given file or None
|
||||
"""
|
||||
try:
|
||||
f = open(filename,"r")
|
||||
filecontent = f.read()
|
||||
f.close()
|
||||
except:
|
||||
#TODO:if debug >=1:
|
||||
print "(EE)[%s]: \"%s\" is not readable!"%(__name__, filename)
|
||||
return ""
|
||||
return filecontent
|
||||
|
||||
|
||||
|
||||
|
||||
def is_dir_readable(path):
|
||||
"""
|
||||
Gets the name of a directory.
|
||||
returns True if dir is readable, else False.
|
||||
"""
|
||||
return os.access(path,os.R_OK)
|
||||
|
||||
|
||||
|
||||
def write_file(filename,content):
|
||||
"""
|
||||
Write content to the given filename.
|
||||
gets: filename,content.
|
||||
"""
|
||||
try:
|
||||
f = open(filename,"w")#oeffnen und schliessen =>
|
||||
f.close() #datei ist jetzt genullt
|
||||
f = open(filename,"a") #anhaengend oeffnen
|
||||
f.write(content)
|
||||
f.close()
|
||||
return ""
|
||||
except:
|
||||
#TODO: debug
|
||||
#if self.debug >=1:
|
||||
print "(EE)[%s]: \"%s\" is not writeable!"%(__name__, filename)
|
||||
return filename
|
||||
|
||||
def basename(filename):
|
||||
return os.path.basename(filename)
|
||||
|
||||
|
||||
def make_all_dirs_absolute(prefix,dirlist):
|
||||
"""
|
||||
this function gets an absolute pathname and a list of pathnames
|
||||
relative to the first. It returns this list, but with absolute
|
||||
entries.
|
||||
"""
|
||||
ret=[]
|
||||
for element in dirlist:
|
||||
ret.append(os.path.normpath(prefix+"/"+element))
|
||||
return ret
|
||||
|
||||
|
||||
def gen_temp_dir(prefix='pk'):
|
||||
'''returns the name of a secure temporary directory
|
||||
with optional prefix as parameter'''
|
||||
dirname=tempfile.mkdtemp("",prefix)
|
||||
dirname+="/"
|
||||
return dirname
|
||||
|
||||
def gen_temp_file(suffix="--gnugo"):
|
||||
"""
|
||||
returns the name of a generated temporay file.
|
||||
optionally gets a suffix for the filename.
|
||||
"""
|
||||
return tempfile.mkstemp(suffix)[1]
|
247
gnugo.py
Normal file
247
gnugo.py
Normal file
|
@ -0,0 +1,247 @@
|
|||
import string,sys,popen2
|
||||
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,req,form):
|
||||
"""
|
||||
gets a goban dict and a (x,y) tuple.
|
||||
tests wether proposed move is legal.
|
||||
returns True or False.
|
||||
"""
|
||||
size = gobandict["size"]
|
||||
turn = gobandict["turn_number"]
|
||||
#who's turn is it?
|
||||
color = ["white","black"][turn % 2] #even turn: white plays, else black plays
|
||||
#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)
|
||||
result = conn.exec_cmd("boardsize "+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(req,form,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"]
|
||||
turn = gobandict["turn_number"]
|
||||
sgf = gobandict["sgf"]
|
||||
|
||||
#convert given coordinates
|
||||
gnucoords = " " + helper.dict_coords_to_gnugo_coords(coords,size)
|
||||
# get current player
|
||||
#even turn: white plays, else its blacks turn
|
||||
color = ["white","black"][turn % 2]
|
||||
helper.debug(req,form,"color: %s -- turn: %s " % (color,turn))
|
||||
#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
|
||||
|
||||
|
||||
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"
|
||||
|
98
goban.py
98
goban.py
|
@ -4,8 +4,7 @@ DEBUG = 1
|
|||
|
||||
import sys,string
|
||||
import cgi
|
||||
import pickle
|
||||
import psql,helper
|
||||
import psql,helper,gnugo
|
||||
|
||||
picklefile = "goban.pickledump"
|
||||
|
||||
|
@ -25,7 +24,7 @@ def display_goban(goban,req,form):
|
|||
hoshis9x9 = [(3,3),(3,7),(5,5),(7,3),(7,7)]
|
||||
|
||||
|
||||
data += '<p><form method="post">'
|
||||
data += '\n<p>\n<form method="post">\n'
|
||||
data += """<input type="hidden" name="sessionid" value="%s">
|
||||
<input type="hidden" name="username" value="%s">
|
||||
<input type="hidden" name="game" value="%s">
|
||||
|
@ -50,30 +49,30 @@ def display_goban(goban,req,form):
|
|||
sy = str(y)
|
||||
# check position:
|
||||
if (x == 1) and (y == 1): # upper left
|
||||
data += '<input type=image id="goban" src="img/topleftline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
data += '<input type=image id="goban" src="img/topleftline'+stone+'.png" name="('+sx+','+sy+')"\n>'
|
||||
elif (x == 1) and (y == size): # upper right
|
||||
data += '<input type=image id="goban" src="img/toprightline'+stone+'.png" name="('+sx+','+sy+')"><br>'
|
||||
data += '<input type=image id="goban" src="img/toprightline'+stone+'.png" name="('+sx+','+sy+')"\n><br\n>'
|
||||
elif (x == size) and (y == size): # lower right
|
||||
data += '<input type=image id="goban" src="img/bottomrightline'+stone+'.png" name="('+sx+','+sy+')"><br>'
|
||||
data += '<input type=image id="goban" src="img/bottomrightline'+stone+'.png" name="('+sx+','+sy+')"\n><br>\n'
|
||||
elif (x == size) and (y == 1): # lower left
|
||||
data += '<input type=image id="goban" src="img/bottomleftline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
data += '<input type=image id="goban" src="img/bottomleftline'+stone+'.png" name="('+sx+','+sy+')"\n>'
|
||||
elif (y == 1): #left line
|
||||
data += '<input type=image id="goban" src="img/leftline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
data += '<input type=image id="goban" src="img/leftline'+stone+'.png" name="('+sx+','+sy+')"\n>'
|
||||
elif (x == 1): # top line
|
||||
data += '<input type=image id="goban" src="img/topline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
data += '<input type=image id="goban" src="img/topline'+stone+'.png" name="('+sx+','+sy+')"\n>'
|
||||
elif (y == size): # right line
|
||||
data += '<input type=image id="goban" src="img/rightline'+stone+'.png" name="('+sx+','+sy+')"><br>'
|
||||
data += '<input type=image id="goban" src="img/rightline'+stone+'.png" name="('+sx+','+sy+')"\n><br\n>'
|
||||
elif (x == size): #bottom line
|
||||
data += '<input type=image id="goban" src="img/bottomline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
data += '<input type=image id="goban" src="img/bottomline'+stone+'.png" name="('+sx+','+sy+')"\n>'
|
||||
else: # hoshi or empty inner field
|
||||
defaultfield = '<input type=image id="goban" src="img/centerline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
defaultfield = '<input type=image id="goban" src="img/centerline'+stone+'.png" name="('+sx+','+sy+')"\n>'
|
||||
#too lazy to make special images for hoshi fields with stones:
|
||||
if goban[(x,y)] == 1:
|
||||
hoshifield = '<input type=image id="goban" src="img/centerline_white.png" name="('+sx+','+sy+')">'
|
||||
hoshifield = '<input type=image id="goban" src="img/centerline_white.png" name="('+sx+','+sy+')"\n>'
|
||||
elif goban[(x,y)] == 2:
|
||||
hoshifield = '<input type=image id="goban" src="img/centerline_black.png" name="('+sx+','+sy+')">'
|
||||
hoshifield = '<input type=image id="goban" src="img/centerline_black.png" name="('+sx+','+sy+')"\n>'
|
||||
else: #empty hoshi
|
||||
hoshifield = '<input type=image id="goban" src="img/hoshi.png" name="('+sx+','+sy+')">'
|
||||
hoshifield = '<input type=image id="goban" src="img/hoshi.png" name="('+sx+','+sy+')"\n>'
|
||||
if size == 19: # 9 hoshis
|
||||
if (x,y) in hoshis19x19:
|
||||
data += hoshifield
|
||||
|
@ -100,7 +99,7 @@ def display_goban(goban,req,form):
|
|||
|
||||
|
||||
|
||||
def process_form(req,form,goban):
|
||||
def process_form(req,form,gobandict):
|
||||
"""
|
||||
gets a goban dictionary.
|
||||
|
||||
|
@ -108,35 +107,66 @@ def process_form(req,form,goban):
|
|||
if the goban has been clicked, return a (x,y) tuple of the position.
|
||||
|
||||
"""
|
||||
ret = ""
|
||||
#if form == empty (which means if page is displayed for the first time):
|
||||
if form.keys() != []:
|
||||
#cut out the name of the clicked button
|
||||
for item in form.keys():
|
||||
if string.find(item,").x")>0:
|
||||
namestring = string.split(item,".x")[0]
|
||||
|
||||
position = helper.string_to_tuple(namestring)
|
||||
ret = set_stone(goban, position)
|
||||
if (type(ret) == type("")):
|
||||
return (goban,ret) #return old goban and error string
|
||||
else:
|
||||
goban = ret
|
||||
return (goban,"") # return new goban and empty string
|
||||
coordstring = string.split(item,".x")[0]
|
||||
position = helper.string_to_tuple(coordstring)
|
||||
ret = set_stone(gobandict, position,req,form)
|
||||
return ret
|
||||
|
||||
|
||||
def set_stone(goban, position):
|
||||
def set_stone(gobandict, position,req,form):
|
||||
"""gets a goban dictionary and a (x,y)-tuple. Returns a modified goban."""
|
||||
turn = goban["turn_number"]
|
||||
if (goban[position] == 0): #empty field
|
||||
goban[position] = (turn % 2) + 1 #even turn: white stone (1), else black stone(2)
|
||||
goban["turn_number"] += 1
|
||||
#now write changed values to database
|
||||
psql.update_goban_field(goban["name"],position[0],position[1],(turn % 2) + 1)
|
||||
psql.update_turn_number(goban["name"],goban["turn_number"])
|
||||
return goban
|
||||
size = gobandict["size"]
|
||||
name = gobandict["name"]
|
||||
turn = gobandict["turn_number"]
|
||||
if (gobandict[position] == 0): #empty field
|
||||
if gnugo.is_legal(gobandict,position,req,form): #gnugo says the move is ok
|
||||
#let gnugo make the above move, let gnugo write move to file
|
||||
new_sgf = gnugo.make_move_in_sgf(req,form,gobandict,position)
|
||||
#write new sgf file into database
|
||||
psql.update_goban_table_field(name,"x1","sgf",new_sgf)
|
||||
#...and in current gobandict
|
||||
gobandict["sgf"] = new_sgf
|
||||
#now convert sgf to gobandict coordinates.
|
||||
gobanlite = gnugo.sgf_to_goban_dict(gobandict)
|
||||
#merge new coordinates with gobandict and update database where necessary
|
||||
(new_gobandict, something_changed) = update_goban_dict_and_table(gobandict,gobanlite)
|
||||
#and finally:
|
||||
if something_changed:
|
||||
psql.update_turn_number(name,turn+1)
|
||||
helper.debug(req,form,"updated turn b/c gobandict has changed")
|
||||
else:
|
||||
helper.debug(req,form,"gobandict has not been changed, leaving turn_number untouched")
|
||||
helper.debug(req,form,"set_stone:game name: %s, turn: %s, value at position: %s" % (name,turn,new_gobandict[position]))
|
||||
return ""
|
||||
else: #move not ok
|
||||
return "This is not a legal move (says Gnugo)."
|
||||
else: #position not empty
|
||||
return "Could not make move: Field not empty."
|
||||
|
||||
def update_goban_dict_and_table(gobandict,gobanlite):
|
||||
"""
|
||||
gets a gobandict and a gobdict light (just coordinates and size).
|
||||
updates the fields in gobandict and database.
|
||||
returns changed gobandict and True (or False) if something has been changed (or not).
|
||||
"""
|
||||
tf = False
|
||||
for key in gobanlite.keys():
|
||||
if gobandict[key] != gobanlite[key]:
|
||||
#found difference in dicts.
|
||||
#update gobandict
|
||||
gobandict[key] = gobanlite[key]
|
||||
#update databse table. the only valid difference can be changed goban field positions.
|
||||
psql.update_goban_field(gobandict["name"],key[0],key[1],gobandict[key])
|
||||
tf = True
|
||||
return gobandict,tf
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
def main(gamename):
|
||||
|
|
33
helper.py
33
helper.py
|
@ -5,10 +5,7 @@ DEBUG = 1
|
|||
def header():
|
||||
"""return html header"""
|
||||
data = """
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGo</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
@ -17,8 +14,8 @@ def header():
|
|||
<meta http-equiv="expires" content="0" />
|
||||
<link rel="stylesheet" media="screen" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1> WebGo </h1>
|
||||
<body>
|
||||
"""
|
||||
return data
|
||||
|
||||
|
@ -30,9 +27,9 @@ def debug(req,form, optstr = ""):
|
|||
"""
|
||||
if DEBUG:
|
||||
if optstr == "":
|
||||
req.write(str(form.keys()))
|
||||
req.write("Debug: "+str(form.keys())+"<br>\n")
|
||||
else:
|
||||
req.write(optstr)
|
||||
req.write("Debug: "+optstr+"<br>\n")
|
||||
|
||||
def footer():
|
||||
"""return html footer"""
|
||||
|
@ -88,3 +85,25 @@ def string_to_tuple(str):
|
|||
except: #empty string
|
||||
returnlist.append(-1)
|
||||
return tuple(returnlist)
|
||||
|
||||
def dict_coords_to_gnugo_coords(coords,size):
|
||||
"""
|
||||
gets a (x,y) coordinate tuple and boardsize.
|
||||
returns a string in gnugo syntax. examples:
|
||||
gets (1,1), returns "A7".
|
||||
gets (6,2), returns "B2".
|
||||
"""
|
||||
letterlist = [" "]
|
||||
letterlist.extend(list(string.letters[26:]))
|
||||
letterlist.remove("I")
|
||||
letter = letterlist[coords[1]]
|
||||
digit = size+1-coords[0]
|
||||
return letter+str(digit)
|
||||
|
||||
def test():
|
||||
print dict_coords_to_gnugo_coords((6,5),7)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
||||
|
||||
|
40
playgame.py
40
playgame.py
|
@ -9,15 +9,13 @@ def is_my_turn(req,form,gobandict):
|
|||
check wether or not the current this is the players turn.
|
||||
return true or false.
|
||||
"""
|
||||
|
||||
#INFO: player1 is black,player2 is white. black starts the game.
|
||||
me = form["username"]
|
||||
player1 = gobandict["player1"]
|
||||
player2 = gobandict["player2"]
|
||||
|
||||
#get turn_number.
|
||||
turn_number = gobandict["turn_number"]
|
||||
#if turn_number modulo 2 == 0: are we player two?
|
||||
#if player 2 can play: are we player two?
|
||||
# yes:return True, else return False
|
||||
if turn_number % 2 == 0:
|
||||
if me == player2:
|
||||
|
@ -41,42 +39,46 @@ def main(req,form):
|
|||
"""
|
||||
display selected goban and manage user input.
|
||||
"""
|
||||
helper.debug(req,form,str(form.keys())+"<hr>")
|
||||
|
||||
try:
|
||||
gamename = form["game"]
|
||||
except:
|
||||
gamename = ""
|
||||
if gamename != "":
|
||||
|
||||
data = ""
|
||||
req.write(helper.header())
|
||||
#helper.debug(req,form,str(form.keys()))
|
||||
#read goban table from database
|
||||
tmplist = psql.read_table(gamename)
|
||||
#make a dictionary out of the list
|
||||
gobandict = psql.fetchall_list_to_goban_dict(tmplist)
|
||||
#check if user has already clicked onto a field:
|
||||
foundx = False
|
||||
click_on_field = False
|
||||
for item in form.keys():
|
||||
if string.find(item,").x") > 0:
|
||||
foundx = True
|
||||
if foundx:
|
||||
click_on_field = True
|
||||
if click_on_field:
|
||||
#if yes: is this the user's turn? then process form.
|
||||
if is_my_turn(req,form,gobandict):
|
||||
(gobandict,retstring) = goban.process_form(req,form,gobandict)
|
||||
|
||||
#do stuff
|
||||
data = helper.header()
|
||||
|
||||
|
||||
data += "Turn number: "+str(gobandict["turn_number"])+". "
|
||||
helper.debug(req,form,"its my turn , i am going to process the form: --")
|
||||
retstring = goban.process_form(req,form,gobandict)
|
||||
if retstring != "":
|
||||
helper.debug(req,form,"playgame.main says: "+str(retstring))
|
||||
else:
|
||||
#reload gobandict, it has been changed.
|
||||
gobandict = psql.fetchall_list_to_goban_dict(psql.read_table(gamename))
|
||||
else:
|
||||
helper.debug(req,form,"its not my turn.")
|
||||
data += """<br>Turn number: %s. %s plays.\n
|
||||
""" % (str(gobandict["turn_number"]), ["White","Black"][int(gobandict["turn_number"] % 2)])
|
||||
|
||||
#check whether its our turn
|
||||
#if yes: print 'your move' and display goban and process move
|
||||
#if not: print '...s move' and display goban.
|
||||
if is_my_turn(req,form,gobandict):
|
||||
data += ("Its your turn.<br>")
|
||||
data += "<br>Its your turn.<br>\n"
|
||||
else:
|
||||
data+= ("This is not your turn. You have to wait for the move of the other player.<br>")
|
||||
|
||||
|
||||
data += "<br>This is not your turn. You have to wait for the move of the other player.<br>\n"
|
||||
#print goban
|
||||
data += goban.display_goban(gobandict,req,form)
|
||||
data += login.navigation_bar(req,form)
|
||||
|
|
28
psql.py
28
psql.py
|
@ -1,4 +1,4 @@
|
|||
import helper
|
||||
import helper,gnugo
|
||||
import pgdb,sys
|
||||
|
||||
DEBUG = 1
|
||||
|
@ -50,6 +50,7 @@ def create_goban_table(player1,player2,size):
|
|||
name
|
||||
player1
|
||||
player2
|
||||
sgf
|
||||
|
||||
and the meaning of these fields:
|
||||
(xn,yn) is a field of the goban,
|
||||
|
@ -58,8 +59,7 @@ def create_goban_table(player1,player2,size):
|
|||
(name,x1) is the name of this goban.
|
||||
(player1,x1) is the name of one player.
|
||||
(player2,x1) is the name of the other player.
|
||||
|
||||
|
||||
(sgf,x1) contains a sgf file with the current board.
|
||||
"""
|
||||
tablename = helper.generate_game_name()
|
||||
data="line text"
|
||||
|
@ -101,6 +101,12 @@ def create_goban_table(player1,player2,size):
|
|||
tmplist.append("player2")
|
||||
tmplist.append(player2)
|
||||
insert_into_table(tablename,str(tuple(tmplist)))
|
||||
#empty sgf file as string
|
||||
tmplist=[]
|
||||
tmplist.append("sgf")
|
||||
sgf = gnugo.create_sgf_file(size)
|
||||
tmplist.append(sgf)
|
||||
insert_into_table(tablename,str(tuple(tmplist)))
|
||||
return tablename
|
||||
|
||||
|
||||
|
@ -324,6 +330,9 @@ def set_game_slot(username,gameslot,gamename):
|
|||
executestring ="UPDATE users SET %s = '%s' WHERE username = '%s'" %(gameslot,gamename, username)
|
||||
sql_one_liner(executestring)
|
||||
|
||||
|
||||
|
||||
|
||||
################# access of goban tables ####################################################################
|
||||
|
||||
def get_players_for_game(tablename):
|
||||
|
@ -361,6 +370,19 @@ def update_turn_number(table,new_number):
|
|||
"""
|
||||
update_goban_table_field(table,"x1","turn_number",new_number)
|
||||
|
||||
def get_sgf(table):
|
||||
"""
|
||||
gets a table name,
|
||||
returns content of "sgf" field.
|
||||
"""
|
||||
cursor=db.cursor()
|
||||
data="select x1 from %s where line='sgf';" % (table)
|
||||
cursor.execute(data)
|
||||
# Commit the changes
|
||||
db.commit()
|
||||
sgf = cursor.fetchone()[0]
|
||||
cursor.close()
|
||||
return sgf
|
||||
|
||||
|
||||
def fetchall_list_to_goban_dict(list):
|
||||
|
|
Loading…
Reference in a new issue