init
18
INSTALL
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
----------------- creating the database --------------------
|
||||||
|
#installing PostgreSQL the Debian way
|
||||||
|
apt-get install postgresql
|
||||||
|
su postgres
|
||||||
|
#in template1
|
||||||
|
psql template1
|
||||||
|
#at the prompt write:
|
||||||
|
CREATE DATABASE webgo ;
|
||||||
|
#now create user who is allowed to administrate this database
|
||||||
|
#user and password should be changed;>
|
||||||
|
CREATE USER name PASSWORD 'password';
|
||||||
|
#now make a super user out of this user:
|
||||||
|
#TODO:tighter rights for user, this should be unsecure
|
||||||
|
ALTER USER name CREATEUSER;
|
||||||
|
#now change the user and password variables in the script
|
||||||
|
|
||||||
|
webgouser
|
||||||
|
webgopassword
|
39
README
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
-------------------------------------------
|
||||||
|
WebGo Development:
|
||||||
|
|
||||||
|
Steps to do to get a runnable version:
|
||||||
|
1. create /etc/apache2/conf.d/webgo.conf with content:
|
||||||
|
|
||||||
|
Alias /webgo /home/mtsrc/daten/projekte/webgo/stderr.py
|
||||||
|
|
||||||
|
<DirectoryMatch /home/mtsrc/daten/projekte/webgo/>
|
||||||
|
Options ExecCGI
|
||||||
|
AddHandler cgi-script .py
|
||||||
|
AllowOverride None
|
||||||
|
order allow,deny
|
||||||
|
allow from all
|
||||||
|
</DirectoryMatch>
|
||||||
|
|
||||||
|
You should replace the directory settings for optimal results ;>
|
||||||
|
|
||||||
|
2. set ownership of webgo directory to ownership of the web server
|
||||||
|
|
||||||
|
this should be all. Errors get written to ./cgi_errors.log.
|
||||||
|
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
How to make the goban clickable:
|
||||||
|
data += '<form method="post">'
|
||||||
|
data += '<input type=image src="img/bottomrightline.png" name="untenrechts">'
|
||||||
|
#insert rest of goban here
|
||||||
|
data += '</form>'
|
||||||
|
print data
|
||||||
|
|
||||||
|
How to extract the data from this POST:
|
||||||
|
import cgi
|
||||||
|
#get instance of fieldstorage
|
||||||
|
form = cgi.FieldStorage()
|
||||||
|
#if form == empty (which means if page is displayed for the first time):
|
||||||
|
if form.keys() != []:
|
||||||
|
#cut out the name of the clicked button
|
||||||
|
print string.split(form.keys()[0],".x")[0]
|
55
ROADMAP
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
M0: debug messages as html
|
||||||
|
Create a script that prints the python stderr of thea main script as html,
|
||||||
|
so debugging the cgi becomes more easy.
|
||||||
|
|
||||||
|
M1: user interaction thorugh images
|
||||||
|
Completed, when a user can click on an image, and the image gets replaced
|
||||||
|
as a result.
|
||||||
|
|
||||||
|
m2: autogenerate images
|
||||||
|
There are partly transparent images of black and white stones, center,
|
||||||
|
border and hoshi squares, all available in png format. The Milestone is
|
||||||
|
considered complete when the program can automatically generate combined
|
||||||
|
images of all squares with all stones. The stones have to be on top ;>
|
||||||
|
|
||||||
|
m3: display working 9x9 goban
|
||||||
|
Completed, when the cgi script has an internal representation of a goban
|
||||||
|
that can handle stones, and this goban is displayed on a html page.
|
||||||
|
|
||||||
|
m4: handle player turns
|
||||||
|
The program knows wether it is the turn of black or white, and prints the
|
||||||
|
current turn color on the html page displaying the goban. If player white
|
||||||
|
clicks on an empty square, a white stone will be layed onto that square.
|
||||||
|
Same for Player black.
|
||||||
|
|
||||||
|
m5: goban inside database
|
||||||
|
Manage the goban inside a postgresql database, so all user input changes a
|
||||||
|
table in the database. Includes a 'size' item that contains the length of
|
||||||
|
on side of the board, so that gobans of size 9x9, 13x13 and 19x19 can be
|
||||||
|
displayed.
|
||||||
|
|
||||||
|
. <--- YOU ARE HERE.
|
||||||
|
|
||||||
|
m6: manage users and game sessions inside database
|
||||||
|
manage user accounts and the game(s) they currently play inside the data-
|
||||||
|
base. each game has its own table, which contains players, current turn,
|
||||||
|
size, the goban inside the database. users are managed in a different
|
||||||
|
table. For each user the following data is stored: password, current games
|
||||||
|
(up to 8 or so). So if the user calls the cgi script, she has to send
|
||||||
|
username and password and is then given the choice of game to play. If
|
||||||
|
there is only one active game, directly present the goban.
|
||||||
|
|
||||||
|
m7: implement Go rules
|
||||||
|
Do not accept player moves which violate go rules. Remove beaten stones.
|
||||||
|
Count and display the score for each player.
|
||||||
|
|
||||||
|
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.
|
||||||
|
-- python profiler
|
||||||
|
-- http://peter.mapledesign.co.uk/weblog/archives/python_is_slow.html
|
||||||
|
create a set of images smaller than 50 pixels (for the 19x19 goban)
|
||||||
|
|
||||||
|
m9: make it standards compliant
|
||||||
|
w3c validation
|
1
TODO
Normal file
|
@ -0,0 +1 @@
|
||||||
|
- hoshi Bilder fuer weisse und schwarze Steine erstellen
|
25
generate_goban_images.py
Executable file
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
"""
|
||||||
|
a simple script using pil to generate the goban from background, grid and stone images.
|
||||||
|
"""
|
||||||
|
import Image
|
||||||
|
gridlist = ["bottomleftline","bottomline","bottomrightline",
|
||||||
|
"centerline","hoshi","leftline","rightline",
|
||||||
|
"topleftline", "topline","toprightline"]
|
||||||
|
|
||||||
|
whitestone = Image.open("imgsource/whitestone.png").convert("RGBA")
|
||||||
|
blackstone = Image.open("imgsource/blackstone.png").convert("RGBA")
|
||||||
|
|
||||||
|
for item in gridlist:
|
||||||
|
#creating empty goban
|
||||||
|
bg = Image.open("imgsource/background.png").convert("RGBA")
|
||||||
|
img = Image.open("imgsource/"+item+".png").convert("RGBA")
|
||||||
|
bg.paste(img,None,img)
|
||||||
|
bg.save("img/"+item+".png")
|
||||||
|
tmp = bg #for the black stones
|
||||||
|
#filling with white stones
|
||||||
|
bg.paste(whitestone,None,whitestone)
|
||||||
|
bg.save("img/"+item+"_white.png")
|
||||||
|
#filling with black stones
|
||||||
|
tmp.paste(blackstone,None,blackstone)
|
||||||
|
tmp.save("img/"+item+"_black.png")
|
174
goban.py
Executable file
|
@ -0,0 +1,174 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
the main file for WebGo.
|
||||||
|
"""
|
||||||
|
import sys,string
|
||||||
|
import cgi
|
||||||
|
import pickle
|
||||||
|
import psql,helper
|
||||||
|
|
||||||
|
picklefile = "goban.pickledump"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def display_goban(goban):
|
||||||
|
"""
|
||||||
|
gets: dictionary containing the layout of the used goban.
|
||||||
|
returns: string containing the HTML code for a clickable goban.
|
||||||
|
"""
|
||||||
|
data = ""
|
||||||
|
|
||||||
|
hoshis19x19 = [(4,4),(4,10),(4,16),(10,4),(10,10),(10,16),(16,4),(16,10),(16,16)]
|
||||||
|
hoshis13x13 = [(4,4),(4,10),(7,7),(10,4),(10,10)]
|
||||||
|
hoshis9x9 = [(3,3),(3,7),(5,5),(7,3),(7,7)]
|
||||||
|
|
||||||
|
|
||||||
|
data += '<form method="post">'
|
||||||
|
try:
|
||||||
|
size = goban["size"]
|
||||||
|
except:
|
||||||
|
#TODO: proper error management
|
||||||
|
raise "[EE] display_goban: got broken goban dictionary."
|
||||||
|
sys.exit(1)
|
||||||
|
for x in range(1,size+1):
|
||||||
|
for y in range(1,size+1):
|
||||||
|
# check for white or black stone
|
||||||
|
if goban[(x,y)] == 1:
|
||||||
|
stone = "_white"
|
||||||
|
elif goban[(x,y)] == 2:
|
||||||
|
stone = "_black"
|
||||||
|
else:
|
||||||
|
stone = ""
|
||||||
|
sx = str(x)
|
||||||
|
sy = str(y)
|
||||||
|
# check position:
|
||||||
|
if (x == 1) and (y == 1): # upper left
|
||||||
|
data += '<input type=image src="img/topleftline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||||
|
elif (x == 1) and (y == size): # upper right
|
||||||
|
data += '<input type=image src="img/toprightline'+stone+'.png" name="('+sx+','+sy+')"><br>'
|
||||||
|
elif (x == size) and (y == size): # lower right
|
||||||
|
data += '<input type=image src="img/bottomrightline'+stone+'.png" name="('+sx+','+sy+')"><br>'
|
||||||
|
elif (x == size) and (y == 1): # lower left
|
||||||
|
data += '<input type=image src="img/bottomleftline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||||
|
elif (y == 1): #left line
|
||||||
|
data += '<input type=image src="img/leftline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||||
|
elif (x == 1): # top line
|
||||||
|
data += '<input type=image src="img/topline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||||
|
elif (y == size): # right line
|
||||||
|
data += '<input type=image src="img/rightline'+stone+'.png" name="('+sx+','+sy+')"><br>'
|
||||||
|
elif (x == size): #bottom line
|
||||||
|
data += '<input type=image src="img/bottomline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||||
|
else: # hoshi or empty inner field
|
||||||
|
defaultfield = '<input type=image src="img/centerline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||||
|
#too lazy to make special images for hoshi fields with stones:
|
||||||
|
if goban[(x,y)] == 1:
|
||||||
|
hoshifield = '<input type=image src="img/centerline_white.png" name="('+sx+','+sy+')">'
|
||||||
|
elif goban[(x,y)] == 2:
|
||||||
|
hoshifield = '<input type=image src="img/centerline_black.png" name="('+sx+','+sy+')">'
|
||||||
|
else: #empty hoshi
|
||||||
|
hoshifield = '<input type=image src="img/hoshi.png" name="('+sx+','+sy+')">'
|
||||||
|
if size == 19: # 9 hoshis
|
||||||
|
if (x,y) in hoshis19x19:
|
||||||
|
data += hoshifield
|
||||||
|
else:
|
||||||
|
data += defaultfield
|
||||||
|
elif size == 13:
|
||||||
|
if (x,y) in hoshis13x13:
|
||||||
|
data += hoshifield
|
||||||
|
else:
|
||||||
|
data += defaultfield
|
||||||
|
elif size == 9:
|
||||||
|
if (x,y) in hoshis9x9:
|
||||||
|
data += hoshifield
|
||||||
|
else:
|
||||||
|
data += defaultfield
|
||||||
|
data += '</form>'
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def string_to_tuple(str):
|
||||||
|
"""
|
||||||
|
gets a string. If the string contains '(',')' and ',', then return
|
||||||
|
a tuple processed from the string. If the partial string is empty, then
|
||||||
|
-1 will be returned for that value.
|
||||||
|
"""
|
||||||
|
if (str[0] =='(') and (str[-1] ==')') and (string.find(str,',')):
|
||||||
|
splitlist = string.split(str[1:-1],",")
|
||||||
|
returnlist = []
|
||||||
|
for item in splitlist:
|
||||||
|
try:
|
||||||
|
returnlist.append(int(item))
|
||||||
|
except: #empty string
|
||||||
|
returnlist.append(-1)
|
||||||
|
return tuple(returnlist)
|
||||||
|
|
||||||
|
|
||||||
|
def process_form(goban):
|
||||||
|
"""
|
||||||
|
gets a goban dictionary.
|
||||||
|
|
||||||
|
reads out the returned CGI form.
|
||||||
|
if the goban has been clicked, return a (x,y) tuple of the position.
|
||||||
|
|
||||||
|
"""
|
||||||
|
#get instance of fieldstorage
|
||||||
|
form = cgi.FieldStorage()
|
||||||
|
#if form == empty (which means if page is displayed for the first time):
|
||||||
|
if form.keys() != []:
|
||||||
|
#cut out the name of the clicked button
|
||||||
|
namestring = string.split(form.keys()[0],".x")[0]
|
||||||
|
position = 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
|
||||||
|
|
||||||
|
|
||||||
|
def set_stone(goban, position):
|
||||||
|
"""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 tone (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
|
||||||
|
else:
|
||||||
|
return "Could not make move: Field not empty."
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Print the required header that tells the browser how to render the text.
|
||||||
|
#(currently done by error logger)
|
||||||
|
#print "Content-Type: text/plain\n\n"
|
||||||
|
|
||||||
|
#do stuff
|
||||||
|
data = helper.header()
|
||||||
|
|
||||||
|
#read goban table from database
|
||||||
|
tmplist = psql.read_table("test")
|
||||||
|
#make a dictionary out of the list
|
||||||
|
goban = psql.fetchall_list_to_goban_dict(tmplist)
|
||||||
|
#print goban
|
||||||
|
|
||||||
|
(goban,str) = process_form(goban)
|
||||||
|
data += "<p> Turn number: %s, %s Player's Move.</p>" % (goban["turn_number"],("White","Black")[goban["turn_number"] % 2]) #eleet ;>
|
||||||
|
data += display_goban(goban)
|
||||||
|
if str != "":
|
||||||
|
data +="\n<p>"+str+"</p>\n"
|
||||||
|
|
||||||
|
|
||||||
|
data += helper.footer()
|
||||||
|
print data
|
||||||
|
|
||||||
|
|
38
helper.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
def header():
|
||||||
|
"""return html header"""
|
||||||
|
data = """
|
||||||
|
<html><HEAD>
|
||||||
|
|
||||||
|
</HEAD><body>
|
||||||
|
"""
|
||||||
|
return data
|
||||||
|
|
||||||
|
def footer():
|
||||||
|
"""return html footer"""
|
||||||
|
data = """
|
||||||
|
</body></html>
|
||||||
|
"""
|
||||||
|
return data
|
||||||
|
|
||||||
|
# create a unique session id
|
||||||
|
def generate_session_id():
|
||||||
|
import md5, time, base64, random, string
|
||||||
|
m = md5.new()
|
||||||
|
m.update(str(time.time()))
|
||||||
|
m.update(str(random.random()))
|
||||||
|
return string.replace(base64.encodestring(m.digest())[:-3], '/', '$')
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_int(data):
|
||||||
|
"""
|
||||||
|
gets a string. if string is an integer: return integer.
|
||||||
|
else return given string.
|
||||||
|
"""
|
||||||
|
#check if value is int
|
||||||
|
num = [n for n in data if n.isdigit()]
|
||||||
|
tmp = "".join(num)
|
||||||
|
if tmp == data:
|
||||||
|
ret = int(data)
|
||||||
|
else:
|
||||||
|
ret = data
|
||||||
|
return ret
|
BIN
img/bottomleftline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/bottomleftline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/bottomleftline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/bottomline.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
img/bottomline_black.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
img/bottomline_white.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
img/bottomrightline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/bottomrightline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/bottomrightline_white.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
img/centerline.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
img/centerline_black.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
img/centerline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/centerwhite.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
img/hoshi.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/hoshi_black.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
img/hoshi_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/leftline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/leftline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/leftline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/rightline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/rightline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/rightline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/topleftline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/topleftline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/topleftline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/topline.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
img/topline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/topline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/toprightline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/toprightline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/toprightline_white.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
imgsource/background.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
imgsource/blackstone.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
imgsource/bottomleftline.png
Normal file
After Width: | Height: | Size: 187 B |
BIN
imgsource/bottomline.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
imgsource/bottomrightline.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
imgsource/centerline.png
Normal file
After Width: | Height: | Size: 194 B |
BIN
imgsource/hoshi.png
Normal file
After Width: | Height: | Size: 229 B |
BIN
imgsource/leftline.png
Normal file
After Width: | Height: | Size: 195 B |
BIN
imgsource/rightline.png
Normal file
After Width: | Height: | Size: 194 B |
BIN
imgsource/topleftline.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
imgsource/topline.png
Normal file
After Width: | Height: | Size: 187 B |
BIN
imgsource/toprightline.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
imgsource/whitestone.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
23
init_webgo.py
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
import psql
|
||||||
|
|
||||||
|
|
||||||
|
def clear():
|
||||||
|
try:
|
||||||
|
psql.drop_table("test")
|
||||||
|
psql.drop_table("users")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create():
|
||||||
|
psql.create_goban_table(9)
|
||||||
|
psql.create_user_table()
|
||||||
|
psql.add_webgo_user("gast","gast")
|
||||||
|
psql.add_webgo_user("gast2","gast2")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
clear()
|
||||||
|
create()
|
||||||
|
|
||||||
|
|
||||||
|
|
130
login.py
Executable file
|
@ -0,0 +1,130 @@
|
||||||
|
import psql,init_webgo,helper
|
||||||
|
import time
|
||||||
|
from mod_python import *
|
||||||
|
|
||||||
|
def process_form(req,form):
|
||||||
|
"""
|
||||||
|
reads username and passsword from form
|
||||||
|
"""
|
||||||
|
#if form == empty (which means if page is displayed for the first time):
|
||||||
|
req.write(str(form.keys()))
|
||||||
|
#req.write("<br>"+"name="+form['name']+", password="+form['password']+"<hr>")
|
||||||
|
if form.keys() != []:
|
||||||
|
|
||||||
|
if ("name" in form.keys()) and ("password" in form.keys()):
|
||||||
|
#extract name and password
|
||||||
|
name = form["name"]
|
||||||
|
password = form["password"]
|
||||||
|
sessionid = form["sessionid"]
|
||||||
|
origpassword = psql.get_user_info(name,"password")
|
||||||
|
#check if user exists (else we would get an error string)
|
||||||
|
if origpassword != "no such user": #no error message, now check password
|
||||||
|
req.write("hier bin ich in der Schleife.<br>")
|
||||||
|
req.write(str(origpassword)+"<br>")
|
||||||
|
if password == origpassword:
|
||||||
|
#login accepted
|
||||||
|
psql.set_user_sessionid(name,sessionid)
|
||||||
|
psql.set_user_timeout(name)
|
||||||
|
#now display list of games.
|
||||||
|
game_overview_form(req,name,sessionid)
|
||||||
|
else:
|
||||||
|
req.write("Login incorrect. Please try again.<br>")
|
||||||
|
req.write(login_form())
|
||||||
|
else: #no such user
|
||||||
|
req.write("Login incorrect. Please try again.<br>")
|
||||||
|
req.write(login_form())
|
||||||
|
else: #one of (name,password) is missing:
|
||||||
|
req.write("Please enter your name and password.")
|
||||||
|
req.write(login_form())
|
||||||
|
|
||||||
|
|
||||||
|
def game_overview_form(req,user,sessionid):
|
||||||
|
"""
|
||||||
|
gets the name of a user and the queue of games.
|
||||||
|
prints a form with the option to select,create and delete games.
|
||||||
|
"""
|
||||||
|
data = helper.header()+ """
|
||||||
|
<h2> Current Games: </h2>
|
||||||
|
<form method="post">
|
||||||
|
"""
|
||||||
|
gamelist = psql.get_user_game_list(user)
|
||||||
|
req.write(str(gamelist)+"<hr>\n")
|
||||||
|
#display list of current games
|
||||||
|
counter = 10
|
||||||
|
tmp = ""
|
||||||
|
for item in gamelist:
|
||||||
|
if (item != None) and (item != "None"):
|
||||||
|
tmp += '<input type="radio"> name="game" value="%s"> %s\n' % (item,item)
|
||||||
|
tmp += '<input type="hidden" name="sessionid" value="%s">\n' % sessionid
|
||||||
|
tmp += '<input type="hidden" name="username" value="%s">\n' % user
|
||||||
|
#later write partners name and whether its our turn or not
|
||||||
|
#data += "Your partner: %s."
|
||||||
|
tmp +="<br>"
|
||||||
|
counter -= 1
|
||||||
|
if tmp == "": #no current games
|
||||||
|
data += "You don't have any running games.\n"
|
||||||
|
else:
|
||||||
|
data += tmp
|
||||||
|
data += '<input type=submit name="play" value="Play selected game">\n<input type=submit name="delete" value="Delete selected game">\n</form>'
|
||||||
|
#now comes the option for creating new games.
|
||||||
|
data += "<h2>Start a new Game</h2>\n"
|
||||||
|
if counter > 0:
|
||||||
|
data+= "You have %s free game slots.<br>" % counter
|
||||||
|
data += """
|
||||||
|
<form>
|
||||||
|
<input type=submit name="create" value="Start a new game">
|
||||||
|
<input type="hidden" name="sessionid" value="%s">
|
||||||
|
<input type="hidden" name="username" value="%s">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
""" % (sessionid, user)
|
||||||
|
else:
|
||||||
|
data+= "Sorry, all your game slots are in use."
|
||||||
|
#display "You have ... free game slots" + Button "create game"
|
||||||
|
#bei "create game neue funktion zum Erstellen + Mitspieler aus Liste auswählen.
|
||||||
|
data+=helper.footer()
|
||||||
|
#check for timeout:
|
||||||
|
if (psql.get_user_info(user,'timeout') >= int(time.time())) and (sessionid == psql.get_user_info(user,'sessionid')):
|
||||||
|
req.write(data)
|
||||||
|
else:
|
||||||
|
req.write(helper.header()+ 'your session timed out'+helper.footer())
|
||||||
|
|
||||||
|
def login_form():
|
||||||
|
"""
|
||||||
|
print welcome message and html form.
|
||||||
|
"""
|
||||||
|
data = helper.header() + """
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
<p>Name:<br>
|
||||||
|
<input name="name" size="40"></p>
|
||||||
|
<p>Pasword:<br>
|
||||||
|
<input name="password" size="40"></p>
|
||||||
|
<input type="hidden" name="sessionid" value="%s">
|
||||||
|
<p><input type="submit" value="login"></p>
|
||||||
|
</form>
|
||||||
|
""" % helper.generate_session_id()
|
||||||
|
data += helper.footer()
|
||||||
|
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def create_new_game(req,user):
|
||||||
|
"""
|
||||||
|
create a new game for user.
|
||||||
|
gets: request object, username.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main(req):
|
||||||
|
#get instance of fieldstorage
|
||||||
|
form = util.FieldStorage(req)
|
||||||
|
if "sessionid" not in form.keys():
|
||||||
|
req.write(login_form())
|
||||||
|
elif ("create" in form.keys()) and ("username" in form.keys()):
|
||||||
|
#user wants to create a new game
|
||||||
|
create_new_game(req,form["username"])
|
||||||
|
else:
|
||||||
|
process_form(req,form)
|
73
main.py
Executable file
|
@ -0,0 +1,73 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
"""
|
||||||
|
simple cgi wrapper for a cgi script, prints the error of the script as html
|
||||||
|
taken from http://gnosis.cx/publish/programming/feature_5min_python.html
|
||||||
|
modified a little
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mod_python import apache
|
||||||
|
|
||||||
|
import sys, traceback
|
||||||
|
import init_webgo
|
||||||
|
|
||||||
|
DEBUG = 1
|
||||||
|
|
||||||
|
|
||||||
|
def handler(req):
|
||||||
|
# "Content-type: text/html\n\n"
|
||||||
|
req.content_type = "text/html"
|
||||||
|
try: # use explicit exception handling
|
||||||
|
#reinitialize database
|
||||||
|
init_webgo.main()
|
||||||
|
|
||||||
|
login = apache.import_module("login")
|
||||||
|
login.main(req)
|
||||||
|
return apache.OK
|
||||||
|
except:
|
||||||
|
import time
|
||||||
|
errtime = '----- '+ time.ctime(time.time()) +' -----\n'
|
||||||
|
errlog = open('/tmp/cgi_errors.log', 'a')
|
||||||
|
errlog.write(errtime)
|
||||||
|
errlog.write(ErrorMsg())
|
||||||
|
data = """<html><head><title>CGI Error Encountered!</title></head>
|
||||||
|
<body><p>Sorry, a problem was encountered running WebGo.</p>
|
||||||
|
<p>Please check the error log on the server for details.</p>
|
||||||
|
<hr><pre>"""
|
||||||
|
data += ErrorMsg()
|
||||||
|
data+="</pre>\n</body></html>"
|
||||||
|
req.write(data)
|
||||||
|
return apache.OK
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def ErrorMsg(escape=0):
|
||||||
|
"""
|
||||||
|
returns: string
|
||||||
|
|
||||||
|
simualtes the traceback output and if argemument
|
||||||
|
<escape> set to 1 (true) the string will be
|
||||||
|
converted to fit into html documents without problems.
|
||||||
|
from Dirk Holtwick
|
||||||
|
"""
|
||||||
|
import traceback, sys, string
|
||||||
|
|
||||||
|
type=None
|
||||||
|
value=None
|
||||||
|
tb=None
|
||||||
|
limit=None
|
||||||
|
type, value, tb = sys.exc_info()
|
||||||
|
body = "Traceback (innermost last):\n"
|
||||||
|
list = traceback.format_tb(tb, limit) + traceback.format_exception_only(type, value)
|
||||||
|
body = body + "%-20s %s" % (
|
||||||
|
string.join(list[:-1], ""),
|
||||||
|
list[-1],
|
||||||
|
)
|
||||||
|
if escape:
|
||||||
|
import cgi
|
||||||
|
body = cgi.escape(body)
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#start(req)
|
7
mptest.py
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
from mod_python import apache
|
||||||
|
|
||||||
|
def handler(req):
|
||||||
|
req.write("Hello World!")
|
||||||
|
return apache.OK
|
||||||
|
|
||||||
|
|
51
old_functions.py
Executable file
|
@ -0,0 +1,51 @@
|
||||||
|
def init_goban(size=9):
|
||||||
|
"""
|
||||||
|
Returns an empty goban in the form of a dictionary where the keys are
|
||||||
|
x,y-tuples: (1,1) is the upper left corner, (9,9) the lower right one
|
||||||
|
for a 9x9 goban.
|
||||||
|
The additional key named size contains the length of a side of the goban.
|
||||||
|
Content of a field:
|
||||||
|
0 == empty field
|
||||||
|
1 == white stone on field
|
||||||
|
2 == black stone on field
|
||||||
|
Field turn_number: Current turn to be played. Even: white, uneven: black.
|
||||||
|
Optional argument size: define length of a side of the goban (one of
|
||||||
|
9, 12 or 19). Defaults to 9.
|
||||||
|
Returns dictionary.
|
||||||
|
"""
|
||||||
|
goban = {}
|
||||||
|
for x in range(1,size+1):
|
||||||
|
for y in range(1,size+1):
|
||||||
|
goban[(x,y)] = 0
|
||||||
|
if (size == 9) or (size == 13) or (size == 19):
|
||||||
|
goban["size"] = size
|
||||||
|
else:
|
||||||
|
goban["size"] = 9
|
||||||
|
goban["turn_number"] = 1
|
||||||
|
|
||||||
|
#some stones for testing purposes
|
||||||
|
goban[(1,2)] = 1; goban[(2,1)] = 1; goban[(2,3)] = 1; goban[(3,3)] = 1; goban[(6,7)] = 1; goban[(6,9)] = 1
|
||||||
|
goban[(2,2)] = 2; goban[(1,3)] = 2; goban[(4,7)] = 2; goban[(4,9)] = 2; goban[(5,8)] = 2; goban[(7,9)] = 2
|
||||||
|
|
||||||
|
return goban
|
||||||
|
|
||||||
|
|
||||||
|
def save_goban(goban):
|
||||||
|
"""gets a goban dictionary and pickles it to a file.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
f = open(picklefile,"w")
|
||||||
|
pickle.dump(goban,f)
|
||||||
|
f.close()
|
||||||
|
except:
|
||||||
|
print "error: could not write to file "+picklefile+"!"
|
||||||
|
|
||||||
|
def load_goban():
|
||||||
|
"""trys to load and return a pickled goban. If this does not work return
|
||||||
|
a newly initialized goban."""
|
||||||
|
try:
|
||||||
|
f = open(picklefile,"r")
|
||||||
|
return pickle.load(f)
|
||||||
|
f.close() #datei ist jetzt genullt
|
||||||
|
except:
|
||||||
|
return init_goban()
|
274
psql.py
Executable file
|
@ -0,0 +1,274 @@
|
||||||
|
import helper
|
||||||
|
import pgdb,sys
|
||||||
|
|
||||||
|
dbusername="webgouser"
|
||||||
|
dbpassword="webgopassword"
|
||||||
|
dbname="webgo"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pgdb.connect(connect_string) -> connection
|
||||||
|
#connect_string = 'host:database:user:password:opt:tty'
|
||||||
|
connect_string='localhost:'+dbname+':'+dbusername+':'+dbpassword
|
||||||
|
db=pgdb.connect(connect_string)
|
||||||
|
|
||||||
|
|
||||||
|
def create_table(name,layout=""):
|
||||||
|
"""
|
||||||
|
create_table(name:string, layout:string)
|
||||||
|
returns name if successfull, None if not.
|
||||||
|
|
||||||
|
|
||||||
|
simple function to create a table. the function itself would just
|
||||||
|
create an empty table with the command 'CREATE TABLE ();'.
|
||||||
|
The real layout is given to the function as an argument of type
|
||||||
|
string. This string contains all column declarations in SQL syntax:
|
||||||
|
"var1 real, var2 int"
|
||||||
|
name is the name of the table
|
||||||
|
|
||||||
|
"""
|
||||||
|
executestring = "CREATE TABLE %s ( %s );" % (name,layout)
|
||||||
|
sql_one_liner(executestring)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def insert_into_table(table, content):
|
||||||
|
"""
|
||||||
|
gets the name of a table and a content string.
|
||||||
|
executes INSERT INTO name VALUES content;
|
||||||
|
"""
|
||||||
|
executestring = "INSERT INTO %s VALUES %s" % (table,content)
|
||||||
|
sql_one_liner(executestring)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def drop_table(name):
|
||||||
|
"""
|
||||||
|
gets: name of table.
|
||||||
|
executes DROP TABLE <name>
|
||||||
|
"""
|
||||||
|
executestring = "DROP TABLE %s;" % (name)
|
||||||
|
sql_one_liner(executestring)
|
||||||
|
|
||||||
|
|
||||||
|
def sql_one_liner(data):
|
||||||
|
"""
|
||||||
|
gets:SQL statement
|
||||||
|
creates a cursor, executes <statement>, closes cursor.
|
||||||
|
"""
|
||||||
|
cursor=db.cursor()
|
||||||
|
cursor.execute(data)
|
||||||
|
# Commit the changes
|
||||||
|
db.commit()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def create_goban_table(size):
|
||||||
|
"""
|
||||||
|
gets:size of goban.
|
||||||
|
creates postgresql table containing goban data.
|
||||||
|
returns: name of created table.
|
||||||
|
"""
|
||||||
|
tablename="test"
|
||||||
|
data="line varchar(15)"
|
||||||
|
for i in range(1,size+1):
|
||||||
|
if data != "":
|
||||||
|
data += ", "
|
||||||
|
data += "x"+str(i) + ' varchar(15)'
|
||||||
|
create_table(tablename,data)
|
||||||
|
#table created, now fill the table
|
||||||
|
for i in range(1,size+1):
|
||||||
|
tmplist=[]
|
||||||
|
tmplist.append("y"+str(i))
|
||||||
|
for k in range(1,size+1):
|
||||||
|
tmplist.append(0)
|
||||||
|
insert_into_table(tablename,str(tuple(tmplist)))
|
||||||
|
#ok, goban itself has been created and filled,
|
||||||
|
#now insert additional variables
|
||||||
|
tmplist=[]
|
||||||
|
tmplist.append("turn_number")
|
||||||
|
tmplist.append("1")
|
||||||
|
insert_into_table(tablename,str(tuple(tmplist)))
|
||||||
|
#size of goban
|
||||||
|
tmplist=[]
|
||||||
|
tmplist.append("size")
|
||||||
|
tmplist.append(size)
|
||||||
|
insert_into_table(tablename,str(tuple(tmplist)))
|
||||||
|
#name of goban (=name of table in database)
|
||||||
|
tmplist=[]
|
||||||
|
tmplist.append("name")
|
||||||
|
tmplist.append(tablename)
|
||||||
|
insert_into_table(tablename,str(tuple(tmplist)))
|
||||||
|
return tablename
|
||||||
|
|
||||||
|
def create_user_table():
|
||||||
|
"""
|
||||||
|
creates a table named users with following columns:
|
||||||
|
name - name of user
|
||||||
|
password - passsword of user
|
||||||
|
game1 - the 10 game slots of this user contain names
|
||||||
|
... of goban tables
|
||||||
|
game10
|
||||||
|
sessionid - id of current session
|
||||||
|
timeout - when does session time out?
|
||||||
|
"""
|
||||||
|
data = "username varchar(15)"
|
||||||
|
data += ", password varchar(15)"
|
||||||
|
for i in range(1,11):
|
||||||
|
data += ", game"+str(i)+" varchar(15)"
|
||||||
|
data += ", sessionid text"
|
||||||
|
data += ", timeout int"
|
||||||
|
create_table("users",data)
|
||||||
|
|
||||||
|
|
||||||
|
def read_table(table):
|
||||||
|
"""
|
||||||
|
gets the name of a table, does a
|
||||||
|
SELECT * FROM table;
|
||||||
|
returns output.
|
||||||
|
"""
|
||||||
|
cursor=db.cursor()
|
||||||
|
data="SELECT * FROM %s;" % (table)
|
||||||
|
cursor.execute(data)
|
||||||
|
# Commit the changes
|
||||||
|
db.commit()
|
||||||
|
ret = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def get_user_game_list(name):
|
||||||
|
"""
|
||||||
|
gets a username, returns the list of games for user.
|
||||||
|
"""
|
||||||
|
cursor=db.cursor()
|
||||||
|
ret = []
|
||||||
|
for x in range(1,11):
|
||||||
|
data="SELECT game%s FROM users WHERE username = '%s'" % (x,name)
|
||||||
|
try:
|
||||||
|
cursor.execute(data)
|
||||||
|
# Commit the changes
|
||||||
|
db.commit()
|
||||||
|
ret.append(cursor.fetchone()[0]) #[0], because return is a list
|
||||||
|
except:
|
||||||
|
ret = "could not get info of all games -- table corrupt?"
|
||||||
|
return ret
|
||||||
|
cursor.close()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def get_user_info(name,infotype):
|
||||||
|
"""
|
||||||
|
gets the name of a user and the type of requested info.
|
||||||
|
returns info from database.
|
||||||
|
"""
|
||||||
|
cursor=db.cursor()
|
||||||
|
if infotype in ("password","sessionid","timeout"):
|
||||||
|
|
||||||
|
data="SELECT %s FROM users WHERE username = '%s'" % (infotype,name)
|
||||||
|
try:
|
||||||
|
cursor.execute(data)
|
||||||
|
# Commit the changes
|
||||||
|
db.commit()
|
||||||
|
ret = cursor.fetchone()[0] #[0], because return is a list
|
||||||
|
except:
|
||||||
|
ret = "no such user"
|
||||||
|
cursor.close()
|
||||||
|
else:
|
||||||
|
ret = "Are your sure about the infotype?"
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def set_user_sessionid(username,sessionid):
|
||||||
|
"""
|
||||||
|
gets username and sessionid, writes sessid into database
|
||||||
|
"""
|
||||||
|
executestring ="UPDATE users SET sessionid = '%s' WHERE username = '%s'" %(sessionid, username)
|
||||||
|
sql_one_liner(executestring)
|
||||||
|
|
||||||
|
def set_user_timeout(username):
|
||||||
|
"""
|
||||||
|
gets username, sets timeout to time.time + 30min
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
timeout = int(time.time()) + 1800 #current time in seconds + seconds for session
|
||||||
|
executestring ="UPDATE users SET timeout = '%s' WHERE username = '%s'" %(timeout, username)
|
||||||
|
sql_one_liner(executestring)
|
||||||
|
|
||||||
|
|
||||||
|
def update_database_field(table,column,line,data):
|
||||||
|
"""
|
||||||
|
gets: table name, column name, line name, new content for field.
|
||||||
|
executes an SQL UPDATE statement for line.
|
||||||
|
"""
|
||||||
|
#TODO:schreiben
|
||||||
|
executestring ="UPDATE %s SET %s = '%s' WHERE line = '%s'" %(table,column,data,line)
|
||||||
|
sql_one_liner(executestring)
|
||||||
|
|
||||||
|
def update_goban_field(table,x,y,content):
|
||||||
|
"""
|
||||||
|
gets: goban dictionary, x,y coordinates, new content for field.
|
||||||
|
modifies goban in database.
|
||||||
|
"""
|
||||||
|
update_database_field(table,"x"+str(x),"y"+str(y),content)
|
||||||
|
|
||||||
|
def update_turn_number(table,new_number):
|
||||||
|
"""
|
||||||
|
gets: name of table,new turn number
|
||||||
|
modifies 'turn_number' in table
|
||||||
|
"""
|
||||||
|
update_database_field(table,"x1","turn_number",new_number)
|
||||||
|
|
||||||
|
def add_webgo_user(name,password):
|
||||||
|
"""
|
||||||
|
adds a database entry for a user.
|
||||||
|
gets: username and password
|
||||||
|
"""
|
||||||
|
#size of goban
|
||||||
|
tmplist=[]
|
||||||
|
tmplist.append(name)
|
||||||
|
tmplist.append(password)
|
||||||
|
insert_into_table("users",str(tuple(tmplist)))
|
||||||
|
|
||||||
|
def fetchall_list_to_goban_dict(list):
|
||||||
|
"""
|
||||||
|
gets the output from read_table (a list),
|
||||||
|
returns a goban dictionary.
|
||||||
|
"""
|
||||||
|
#create dictionary
|
||||||
|
ret = {}
|
||||||
|
#get size of goban
|
||||||
|
for item in list:
|
||||||
|
if item[0] == "size":
|
||||||
|
size=int(item[1])
|
||||||
|
ret["size"] = size
|
||||||
|
#populate dictionary with goban field
|
||||||
|
for item in list:
|
||||||
|
if item[0][0] == "y": #goban fields
|
||||||
|
#get current y coordinate
|
||||||
|
y = int(item[0][1:])
|
||||||
|
#fill dictionary for current y coordinate
|
||||||
|
for x in range(1,size+1):
|
||||||
|
ret[(x,y)]=helper.check_for_int(item[x])
|
||||||
|
else: #other variables
|
||||||
|
ret[item[0]]=helper.check_for_int(item[1])
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test():
|
||||||
|
#create_table("test")
|
||||||
|
drop_table("test")
|
||||||
|
create_goban_table(9)
|
||||||
|
update_database_field("test","x1","turn_number",4)
|
||||||
|
list = read_table("test")
|
||||||
|
print list
|
||||||
|
dict = fetchall_list_to_goban_dict(list)
|
||||||
|
print dict
|
||||||
|
print dict["turn_number"]
|
||||||
|
print dict["name"]
|
||||||
|
print type(dict["turn_number"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#test()
|
||||||
|
|
||||||
|
|
3
test.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
def hello(req):
|
||||||
|
x = "Hello, world!"
|
||||||
|
return x
|