work5
这一次写的内容是黄金豆小游戏,由于现在偏重写服务器端。对于算法层面其实涉及不多,更多偏于工程上的架构。
总而言之本次作业的服务器核心是用web.py所写,而且为了方便其他用户写客户端,架构非常简单。
具体而言,首先从接口上来说,服务器只涉及到两个表单操作,注册表单和提交表单,注册也同样可以在网页上进行。
由于是测试服务器通讯的,为了方便客户端调试很多细节与题设有所不同,但修改起来非常简单。
注册模块:
注册需要提交用户名和密码{[username],[password]}
1.只有在游戏开始前可以进行注册。
2.注册失败,会返回-1.
3.注册成功,会返回0.
4.注册信息会录入注册表中。
提交模块:
提交模式需要提交用户名和密码以及给的数字{[username],[password],[nut]}
1.如果下一轮还未开始用户已经提交,便会等待。
2.如果用户提交后,本轮还未结束,依然等待直到,结束,返回G值
3.若登录失败,则返回-1(测试完毕后,若为按时提交也返回-1)
游戏模块:
1.会等待本轮用户提交完毕才会给出G值(测试完毕后改为,若1秒之后仍未提交,直接失败)。
2.分数策略如提所述。
即服务器与客户端同步,以免发生一开始客户端设计的缺陷引发客户端一次都无法返回有意义的结果。
正式发布时,服务器将不等待客户端的同步。
画图由一个monitor实时返回此时AVG的值。值得一提的是,由于web.py对于线程数有限制(10),我们修改了web.py source里的代码,把THREAD数目放宽才使得该项目成为可能。
测试程序:
开了30个线程测了50组数据,图像反应正常。
#coding=utf-8
import wx
from matplotlib.figure import Figure
import matplotlib.font_manager as font_manager
import numpy as np
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import web
import datetime
import time
from web import form
import matplotlib.pyplot as plt
import thread
web.config.debug = False
import sys
succ = '0'
fail = '1'
PLAYERNUM = 30
reload(sys)
sys.setdefaultencoding("utf-8")
class Game():
nowtime = 0
paticipate_num = 0
active_num = 0
total =0.0
avg = 0.0
score = {}
times = {}
number = {}
def __init__(self):
pass
G = Game()
x = []
y = []
urls=('/','Index',
'/reg','Reg',
'/login','Login',
'/logout','Logout',
'/showusers','Showusers',
)
app=web.application(urls,globals())
render = web.template.render('templates',base='base')
session = web.session.Session(app, web.session.DiskStore('sessions'),initializer={'login':0,'id':'0'})
db1=web.database(dbn='mysql',
db='golden',
user='root',
pw='199089455',
port=3306,)
###首页
class Index:
def GET(self):
#return web.ctx.ip
return datetime.datetime.now().strftime('%Y%m%d%H%M%S%S')
###注册页
class Reg:
vusername= form.regexp(r".{3,20}$", '用户名长度为3-20位')
vpass = form.regexp(r".{6,20}$", '密码长度为6-20位')
vemail = form.regexp(r".*@.*", "must be a valid email address")
regForm=web.form.Form(
form.Textbox('username',vusername,description=u'用户名'),
form.Password("password",vpass,description=u"密码"),
form.Password("password2", description=u"确认密码"),
form.Button(u"马上注册", type="submit", description="submit"),
validators = [
form.Validator("两次输入的密码不一致", lambda i: i.password == i.password2)]
)
def GET(self):
return render.reg(self.regForm,u'注册成功后自动跳往登陆界面')
def POST(self):
formdata=web.input();
username=web.net.websafe(formdata.username)
password=web.net.websafe(formdata.password)
regdate=datetime.datetime.now().strftime('%Y%m%d%H%M%S')
if not self.regForm.validates() and G.paticipate_num>=PLAYERNUM:
return fail
#判断用户名是否存在:
else:
if len(getUserByUserName(username))>0:
#return render.reg(self.regForm,u'用户名已存在')
return fail
else:
ret=addUser(username,password,regdate)
if ret==0:
#return u'注册成功'
G.paticipate_num+=1
G.score [username]= 0
G.times [username]= 0
G.number[username]= 0
return succ
else:
return fail
###登陆页面
class Login:
loginForm=form.Form(form.Textbox('username',description=u'用户名'),
form.Password("password",description=u"密码"),
form.Button(u"马上登陆", type="submit", description="submit"),)
def GET(self):
if logged():
return 'you are logged '+session.id
else:
return render.login(self.loginForm)
def POST(self):
postdata=web.input()
username=web.net.websafe(postdata.username)
password=web.net.websafe(postdata.password)
nut=float(web.net.websafe(postdata.nut))
rslist=getUserByUserName(username)
if len(rslist)==0:
#return render.login(self.loginForm,'用户名不存在')
return
else:
if password==rslist[0].password:
while G.times[username]>=G.nowtime:
time.sleep(0.001)
G.times[username]+=1
G.number[username]=nut
G.active_num+=1
G.total+=nut
while G.times[username]==G.nowtime:
time.sleep(0.001)
return G.avg*0.618
else:
#return render.login(self.loginForm,'用户名及密码不匹配')
return fail
###注销登陆页面
class Logout:
def GET(self):
session.login=0
session.kill()
return 'you have logged out!'
###显示用户列表
class Showusers:
def GET(self):
if logged():
usersList=getUsers()
return render.users(usersList)
else:
return 'please log in first!'
###判断用户是否登陆
def logged():
if session.login==1:
return True
else:
return False
################################################ 数据库操作部分 begin ############################################################
#增加用户
def addUser(username,password,regdate):
ret=db1.insert('t_user',username=username ,password=password,regdate=regdate)
return ret
#获取用户列表
def getUsers(orderby='regdate'):
rs=db1.select('t_user',order=orderby)
rslist=[]
for r in rs:
rslist.append(r)
return rslist
#根据用户名查询用户信息
def getUserByUserName(username):
rs=db1.select('t_user',where='username="'+username+'"')
rslist=[]
for r in rs:
rslist.append(r)
return rslist
################################################ 数据库操作部分 end ############################################################
################################################ 游戏线程 begin ############################################################
def Gamestart():
while (G.paticipate_num!= PLAYERNUM):
time.sleep(0.1)
if G.paticipate_num== PLAYERNUM:
for G.nowtime in range(1,51):
G.total = 0.0
G.active_num = 0
while G.active_num<G.paticipate_num:
time.sleep(0.1)
print G.nowtime
G.avg = G.total/PLAYERNUM
winner = ""
minsqr = 10000.0
loser = ""
maxsqr = -10000.0
for each in G.number:
if (G.number[each]-G.avg*0.618)*(G.number[each]-G.avg*0.618)<minsqr:
minsqr = (G.number[each]-G.avg*0.618)*(G.number[each]-G.avg*0.618)
winner = each
if (G.number[each]-G.avg*0.618)*(G.number[each]-G.avg*0.618)>maxsqr:
maxsqr = (G.number[each]-G.avg*0.618)*(G.number[each]-G.avg*0.618)
loser = each
G.score[winner] += PLAYERNUM
G.score[loser] -= 2
x.append(G.nowtime)
y.append(G.avg)
G.nowtime=51
################################################ 游戏线程 end ############################################################
POINTS = 300
################################################ 绘图部分 begin ############################################################
class PlotFigure(wx.Frame):
"""Matplotlib wxFrame with animation effect"""
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, title="Monitor", size=(600, 400))
# Matplotlib Figure
self.fig = Figure((6, 4), 100)
# bind the Figure to the backend specific canvas
self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig)
# add a subplot
self.ax = self.fig.add_subplot(111)
# limit the X and Y axes dimensions
self.ax.set_ylim([0, 100])
self.ax.set_xlim([0, POINTS])
self.ax.set_autoscale_on(False)
self.ax.set_xticks([])
# we want a tick every 10 point on Y (101 is to have 10
self.ax.set_yticks(range(0, 101, 10))
# disable autoscale, since we don't want the Axes to ad
# draw a grid (it will be only for Y)
self.ax.grid(True)
# generates first "empty" plots
self.user = [None] * POINTS
self.l_user,=self.ax.plot(range(POINTS),self.user,label='AVG')
# add the legend
self.ax.legend(loc='upper center',
ncol=4,
prop=font_manager.FontProperties(size=10))
# force a draw on the canvas()
# trick to show the grid and the legend
self.canvas.draw()
# save the clean background - everything but the line
# is drawn and saved in the pixel buffer background
self.bg = self.canvas.copy_from_bbox(self.ax.bbox)
# bind events coming from timer with id = TIMER_ID
# to the onTimer callback function
wx.EVT_TIMER(self, TIMER_ID, self.onTimer)
def onTimer(self, evt):
"""callback function for timer events"""
# restore the clean background, saved at the beginning
self.canvas.restore_region(self.bg)
# update the data
temp = G.avg
self.user = self.user[1:] + [temp]
# update the plot
self.l_user.set_ydata(self.user)
# just draw the "animated" objects
self.ax.draw_artist(self.l_user)# It is used to efficiently update Axes data (axis ticks, labels, etc are not updated)
self.canvas.blit(self.ax.bbox)
def __del__( self ):
t.Stop()
########################################################################################################################
TIMER_ID = wx.NewId()
if __name__=="__main__":
thread.start_new_thread(Gamestart,())
thread.start_new_thread(app.run,())
app1 = wx.PySimpleApp()
frame = PlotFigure()
t = wx.Timer(frame, TIMER_ID)
t.Start(50)
frame.Show()
app1.MainLoop()