homework-05
接口声明
/reg
GET:
输出一个注册表单,其中
id:用户名
pd:密码
POST:
接收一个表单{id="",pd=""},返回:
0:注册成功
-1:注册失败
/attend
GET:
输出一个游戏参与表单,其中
id:用户名
pd:密码
num:1-100之间的实数,表示所要提交的数字
POST:
接受一个表单(id="",pd="",num=""),返回:
-1:用户名不存在
-2:密码错误
-3:数字范围出错(必须在1-100之间)
-4:游戏结束
left_time+","+now_turn+","+success?:
left_time:表示下一轮多少秒后开始
now_turn:当前轮数
success?:1表示提交成功,0表示提交失败(服务器锁中,正在计算)
/result
GET:
返回n行,倒序的golden_number
/
GET:
返回综合信息榜
游戏流程和设计简述
游戏一开始(第0轮)有20s的注册时间,这段时间内必须注册好用户,注册的用户提交{用户名,密码}到/reg,服务器返回0表示提交成功,返回-1表示用户名重复了。这保证了一个用户名只能存在一个。
每次用户往/attend提交自己的 {用户名、密码、数}时,服务器返回三个信息:下一轮比赛开始时间time,当前提交的轮数turn,当前提交是否成功succ。通过后两个信息可以得知自己的提交结果。而time预示了下一轮比赛的开始时间,客户端可以sleep(time)再继续提交,所以说这样客户端很容易实现自动游戏。
这就意味着,客户端和服务端之间不“等待”,服务端不等待所有用户提交后才进行游戏。所以游戏时间是均匀的(我设定为1.2s一次),为了保证能计算出游戏结果,在游戏的最后0.2s里不允许用户再进行提交了。
为了让客户端程序能得到更多可供AI调用的信息,我们在/result里返回了历史的Golden number,来让客户端“学习”golden number分布的模式。
程序主体采用Python编写,http请求处理使用webpy框架实现,主要对每个url实现了GET/POST相应。
在综合信息榜(/)上,为了实现更好的信息展示效果,我们用了canvas元素来绘制折线图,加上Ajax通信手段,来实现异步刷新golden number 变化曲线。
主程序分两类线程,一类线程用来处理http请求,这方面的线程调度由webpy自动处理。另一个线程是游戏结果处理线程。两类线程之间通过全局变量来通信。
Peer用ruby写了个随机提交数字的程序,测试时使用1.2s进行一次游戏,一共40个客户端,能在规定时间内跑完100轮游戏,并输出结果。
回答问题
- 你对于这个系统的服务器和接口是如何设计的? 应该采取哪些设计让游戏能顺利完成? 写出具体的接口。
接口上边已经说了。
- 你和你的同伴分工负责, 设计出服务器应该有几个功能模块, 这些功能模块之间的关系 (用 UML 或其它图例来表示)。
功能模块主要分http请求处理线程和游戏处理线程。 模块之间通过全局变量来通信。uml不会。
- 写出每个模块功能的伪代码,要能做到让另一个同学能看到这些伪代码,就能明确实现的要求并马上开始实现。
??
- 我们的课程有 60 名学生 (60 个客户程序),如何能设计服务器程序和交互的接口让它能在 1 秒钟之内就完成一轮比赛?
可以,只需要加个随机随机化让用户分散在0.5秒里均匀访问就行了。当然我们不能假设用户的行为, 但可以规定。
主程序
1 import web 2 import string 3 import time 4 import thread 5 from web import form 6 import data 7 8 urls = ( 9 '/reg','reg', 10 '/attend','attend', 11 '/','board', 12 '/result','result', 13 '/resulthtml','resulthtml', 14 '/scorehtml','scorehtml', 15 '/infohtml','infohtml', 16 '/turnhtml','turnhtml', 17 '/totalboard','totalboard' 18 ) 19 20 render = web.template.render('templates/') 21 app = web.application(urls, globals()) 22 23 regform = form.Form( 24 form.Textbox("id", 25 form.notnull), 26 form.Password("pd", 27 form.notnull) 28 ) 29 30 attendform = form.Form( 31 form.Textbox("id", 32 form.notnull), 33 form.Password("pd", 34 form.notnull), 35 form.Textbox("num", 36 form.regexp('\d+', 'Must be a digit')), 37 ) 38 39 40 class turnhtml: 41 def GET(self): 42 return data.nowturn 43 44 class infohtml: 45 def GET(self): 46 if data.gameover==1: 47 return "<b>Game is over!</b> the winner is <b>" + data.winner + "</b> score is <b>" + str(data.users[data.winner]) +"</b>" 48 else: 49 return "LEFT "+ str(data.TURNTIME-(time.time()-data.nowstart)) + "s TO SUBMIT YOUR NUMBER" 50 51 class result: 52 def GET(self): 53 s = "" 54 for i in range(len(data.alluped)-1,-1,-1): 55 s += str(data.alluped[i]['result']) + '\n' 56 return s 57 58 class resulthtml: 59 def GET(self): 60 s = "<table ><tr><td>Round:</td>" 61 l = len(data.alluped) 62 for i in range(l-1,max(l-12,-1),-1): 63 s += "<td>"+str(i)+"</td>" 64 s +="</tr><td></td>" 65 for i in range(l-1,max(l-12,-1),-1): 66 s += "<td>"+str(data.alluped[i]['result'])+"</td>" 67 s += "</tr></table>" 68 return s 69 70 class scorehtml: 71 def GET(self): 72 b = sorted(data.users.iteritems(),key=lambda a:a[1],reverse = True) 73 return render.scorehtml(b,data.alluped) 74 75 class totalboard: 76 def GET(self): 77 return render.totalboard(data.users,data.alluped) 78 79 class reg: 80 def GET(self): 81 form = regform() 82 return render.reg(form) 83 def POST(self): 84 form = regform() 85 if not form.validates(): 86 return render.reg(form) 87 else: 88 i = web.input() 89 myvar = dict(id = i.id) 90 result = list(data.db.select('users',myvar,where = "id = $id")) 91 if result==[]: 92 data.db.insert('users',id = i.id,pd = i.pd) 93 data.users[i.id]=0.0 94 return 0 95 else: 96 return -1 97 98 class attend: 99 def GET(self): 100 form = attendform() 101 return render.attend(form) 102 def POST(self): 103 form = attendform() 104 if not form.validates(): 105 return render.attend(form) 106 else: 107 i = web.input() 108 i.num = string.atof(i.num) 109 result = list(data.db.select('users',where = 'id = "%s"'%(i.id))) 110 if result==[]: 111 return -1 112 if result[0]['pd']!=i.pd: 113 return -2 114 if 100<i.num or i.num<1: 115 return -3 116 if data.gameover == 1: 117 return -4 118 a = data.nowuped.get(i.id) 119 t = data.TURNTIME-(time.time()-data.nowstart) 120 if a==None and t>=data.DEAD: 121 data.nown += 1 122 data.nowtot += i.num 123 data.nowuped[i.id] = dict(num = i.num,deltscore = 0) 124 return "%f,%d,%d"%(data.TURNTIME-(time.time()-data.nowstart),data.nowturn,a==None and t>=data.DEAD) 125 126 class board: 127 def GET(self): 128 t = data.TURNTIME-(time.time()-data.nowstart) 129 a = render.board(data.DEAD,data.TURNTIME,t>=data.DEAD,data.nowturn,t,data.alluped,data.users,data.TURNTIME) 130 return a 131 def dealwinlost(uped,users,avgn,nowturn): 132 minv = 101 133 maxv = 0 134 winner = 0 135 for i in uped: 136 delt = abs(uped[i]['num']-avgn) 137 if delt>maxv: 138 maxv = delt 139 if delt<minv: 140 minv = delt 141 winner = i 142 uped[winner]['deltscore'] = 10 143 for i in uped: 144 if i==winner: 145 continue 146 delt = abs(uped[i]['num']-avgn) 147 if delt==maxv: 148 uped[i]['deltscore'] = -1 149 a = list(data.db.select('users')) 150 for i in a: 151 if i['id'] not in uped and i['id']!=winner: 152 uped[i['id']] = dict(num = 0.0,deltscore = -5) 153 for i in a: 154 users[i['id']] += uped[i['id']]['deltscore'] 155 return winner 156 157 def gameinit(): 158 a = list(data.db.select('users')) 159 for i in a: 160 data.users[i['id']]=0.0 161 162 def roundinit(): 163 data.nowuped = {} 164 data.nown = 0 165 data.nowtot = 0 166 167 def round(): 168 data.nowstart = time.time() 169 time.sleep(data.TURNTIME-data.DEAD) 170 if (data.nown!=0): 171 avgn = data.nowtot/data.nown * 0.618 172 data.nowuped['winner'] = dealwinlost(data.nowuped,data.users,avgn,data.nowturn) 173 data.nowuped['result'] = avgn 174 data.nowuped['turn'] = data.nowturn 175 data.alluped.append(data.nowuped) 176 data.nowturn += 1 177 if data.nowturn >= data.MAXTURN: 178 data.gameover = 1 179 t =data.TURNTIME-(time.time()-data.nowstart) 180 if t>0: 181 time.sleep(t) 182 183 def game(): 184 gameinit() 185 temp = data.TURNTIME 186 data.TURNTIME = 20 187 round() 188 data.TURNTIME = temp 189 while True: 190 roundinit() 191 round() 192 if data.gameover ==1: 193 break 194 maxscore = -100000 195 for i in data.users: 196 if data.users[i]>maxscore: 197 maxscore = data.users[i] 198 data.winner = i 199 thread.exit_thread() 200 201 if __name__ == "__main__": 202 thread.start_new_thread(game,()) 203 app.run()