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:

返回综合信息榜

设计说明

这次服务器是由forwil同学编写的。

游戏一开始(第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自动处理。另一个线程是游戏结果处理线程。两类线程之间通过全局变量来通信。

我主要写了客户端,根据以上接口进行注册和提交,并尝试了40个线程同时提交,效果还可以。但由于ruby的多线程是用户级的 ,调度上可能稍慢,如果分开多个进程效果更好。

提交策略参考了邹欣老师的一篇关于黄金点游戏的文章,根据一般情形下 黄金点游戏的趋势来提交。

为了能让服务器能在一秒内完成比赛,我们在设计接口的时候进行了一些简化。 游戏开始后只有两个请求,并且通过返回距下一次游戏开始的时间来减少请求。 后来forwil同学又简化了数据库操作,使数据先暂存,直到游戏结束再进行存储。这样大大减少 了服务器的负担,基本上能一秒进行一次游戏。

回答问题

你对于这个系统的服务器和接口是如何设计的?  应该采取哪些设计让游戏能顺利完成? 写出具体的接口。

   接口上边已经说了。

你和你的同伴分工负责, 设计出服务器应该有几个功能模块,  这些功能模块之间的关系 (用 UML 或其它图例来表示)。

   功能模块主要分http请求处理线程和游戏处理线程。 模块之间通过全局变量来通信。简单的说就是客户端请求提交数字,服务端接受并返回下一次游戏开始时间,客户端sleep相应时间避免无意义的提交。

写出每个模块功能的伪代码,要能做到让另一个同学能看到这些伪代码,就能明确实现的要求并马上开始实现。

      方法很简单,上面一句话就能明白了。

我们的课程有 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         #form.Validator('Must in 1-100', lambda x: 100>=string.atof(x) >=1)),
 38 )
 39 
 40 
 41 class turnhtml:
 42     def GET(self):
 43         return data.nowturn
 44 
 45 class infohtml:
 46     def GET(self):
 47         if data.gameover==1:
 48             return "<b>Game is over!</b>"
 49         else:
 50             return "LEFT "+ str(data.TURNTIME-(time.time()-data.nowstart)) + "s TO SUBMIT YOUR NUMBER"
 51 
 52 class result:
 53     def GET(self):
 54         s = ""
 55         for i in range(len(data.alluped)-1,-1,-1):
 56             s += str(data.alluped[i]['result']) + '\n'
 57         return s
 58 
 59 class resulthtml:
 60     def GET(self):
 61         s = "<table border='1'><tr><td>Round:</td>"
 62         l = len(data.alluped)
 63         for i in range(l-1,max(l-12,-1),-1):
 64             s += "<td>"+str(i)+"</td>"
 65         s +="</tr><td></td>"
 66         for i in range(l-1,max(l-12,-1),-1):
 67             s += "<td>"+str(data.alluped[i]['result'])+"</td>"
 68         s += "</tr></table>"
 69         return s
 70 
 71 class scorehtml:
 72     def GET(self):
 73         return render.scorehtml(data.users,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         #data.alluped.reverse()
130         a = render.board(data.DEAD,data.TURNTIME,t>=data.DEAD,data.nowturn,t,data.alluped,data.users,data.TURNTIME)
131         #data.alluped.reverse()
132         return a
133 def dealwinlost(uped,users,avgn,nowturn):
134     minv = 101
135     maxv = 0
136     winner = 0
137     for i in uped:
138         delt = abs(uped[i]['num']-avgn)
139         if delt>maxv:
140             maxv = delt
141         if delt<minv:
142             minv = delt
143             winner = i
144     uped[winner]['deltscore'] = 10
145     for i in uped:
146         if i==winner:
147             continue
148         delt = abs(uped[i]['num']-avgn)
149         if delt==maxv:
150             uped[i]['deltscore'] = -1
151     a = list(data.db.select('users'))
152     for i in a:
153         if i['id'] not in uped and i['id']!=winner:
154             uped[i['id']] = dict(num = 0.0,deltscore = -5)
155     for i in a:
156         users[i['id']] += uped[i['id']]['deltscore']
157     return winner
158 
159 def gameinit():
160     a = list(data.db.select('users'))
161     for i in a:
162         data.users[i['id']]=0.0    
163 
164 def roundinit():
165     data.nowuped = {}
166     data.nown = 0
167     data.nowtot = 0
168 
169 def round():
170     data.nowstart = time.time()
171     time.sleep(data.TURNTIME-data.DEAD)
172     if (data.nown!=0):
173         avgn = data.nowtot/data.nown * 0.618
174         data.nowuped['winner'] = dealwinlost(data.nowuped,data.users,avgn,data.nowturn)
175         data.nowuped['result'] = avgn
176         data.nowuped['turn'] = data.nowturn
177         data.alluped.append(data.nowuped)
178         data.nowturn += 1
179         if data.nowturn == 101:
180             data.gameover = 1
181         t =data.TURNTIME-(time.time()-data.nowstart)
182         if t>0:
183             time.sleep(t)
184 
185 def timer1s():
186     gameinit()
187     while True:
188         roundinit()
189         round()
190         if data.gameover ==1:
191             break
192     thread.exit_thread() 
193 
194 if __name__ == "__main__":
195     thread.start_new_thread(timer1s,())
196     app.run()
server
 1 require "net/http"
 2 params = Hash.new
 3 params[:id] = ARGV[0]
 4 params[:pd] = ARGV[1]
 5 addr = "http://192.168.1.3"
 6 uri = URI.parse(addr + "/reg")
 7 res = Net::HTTP.post_form(uri, params)
 8 
 9 def gen(last)
10     if last > 5
11         temp = last - rand * Math.exp( last / 5 )
12         return 1 if temp < 1
13         temp
14     else
15         last + rand * Math.exp( last / 2 )
16     end
17 end
18 last = 40
19 
20 while true
21     params[:num] = gen(last)
22     uri = URI.parse(addr + "/attend")
23     res = Net::HTTP.post_form(uri, params)
24     str = res.body.split(/,/)
25     
26     time = str[0].to_f
27     turn = str[1].to_i
28     succ = str[2].to_i
29 
30     puts time
31     sleep(time)
32 
33     uri = URI.parse(addr + "/result")
34     res = Net::HTTP.get(uri) 
35     str = res.split(/\n/)
36     last = str[0].to_f
37 end
client

演示效果

posted @ 2013-11-04 01:48  zjoe  阅读(224)  评论(1编辑  收藏  举报