黄金点游戏 在上次课程设计作业的基础上(C++完成),我们发现C++较难实现可视化,并且不利于后续迭代,因此我们利用python重构了整份代码。
当设定的玩家名为“AI”时,我们实现了一个简单的AI;上次课程设计作业中,我们令AI随机生成0~100中的一个浮点数,但这样AI的决策和之前各玩家的报数和G值(各玩家报数的平均值*0.618)完全无关。
在参考了MSRA的获奖算法后,我们让AI先在0~上轮的G值(如果是第一轮游戏,则为(100+0)/2 * 0.618)中随机一个浮点数,然后AI有较小的概率将自己的报数再增加一次,增量为0~上轮的G值(如果是第一轮游戏,则为(100+0)/2 * 0.618)中随机的浮点数乘以一个系数(暂时为0.1),通过函数mIn()保证不大于等于100。这参考MSRA的获奖算法中,逐渐降低平均值(随着游戏轮数增加的一般情况)+随机化(以应对某个玩家突然报大数的突发状况)的思路。
现在与AI的游戏已经有一定的决策深度,但我们发现了新的问题:玩家数n>2时,玩家可以通过故意报出大数提高上轮的G值(大概率-2分),然后再报小数并取得当轮的胜利(大概率+n分),这样每两轮玩家的分数有很大概率会提升至少一次。在算法方面仍然有进一步提升的空间,后续可能会以模仿棋的思路,令AI去学习玩家的报数策略,让AI更有竞争力。
功能描述
Player类:维护与单一玩家相关的信息。
init():初始化。
SetPlayerName():设置该玩家为拥有指定姓名的人类玩家。
newChoice():设置该玩家当前轮的报数。
win():设置该玩家当前轮赢(+n分,n为玩家数)。
draw():设置该玩家当前轮平(计0分)。
lose():设置该玩家当前轮负(-2分)。
showName():返回该玩家的姓名。
choice_i():返回该玩家第i轮的报数。
gain_i():返回该玩家第i轮的得分。
Game类:维护与当前对局相关的信息。
Start():开始一局全新的游戏。
init():初始化。
addPlayer():增加一个新玩家。
cal():计算当前轮的G值,并维护每个对局中玩家在当前轮的胜平负。
input_number():如果非AI玩家当前轮均已报数,则维护当前轮的对局。
newTurn():显示得分表与当前轮结果,并开始新的一轮。
scoreBoard():显示计分板。
class Player:
def __init__(self):
self.is_human = False
self.name = "Hawaii"
self.score = 0
self.choice = []
self.gain = []
def setPlayerName(self,st):
self.is_human = True
self.name = st
def newChoice(self,mid,x = 0):
temp = 0
if not self.is_human:
temp = mid*(random.randint(0,32767)+1)/(32769)
if random.randint(0,32767) > 32000:
temp = min(100-0.000001, temp+0.1*mid*(random.randint(0,32767)+1)/(32769))
else:
temp = x
self.choice.append(temp)
def win(self,n):
self.gain.append(n)
self.score+=n
def draw(self):
self.gain.append(0)
self.score+=0
def lose(self):
self.gain.append(-2)
self.score-=2
def showName(self):
return self.name
def choice_i(self,i):
i-=1
if(0<=i and i<len(self.choice)):
return self.choice[i]
else:
return -1
def gain_i(self,i):
i-=1
if(0<=i and i<len(self.gain)):
return self.gain[i]
return -1
class Game:
def Start(self,st):
self.n = eval(st)
name_list = []
label_list = []
entry_list = []
for i in range(self.n):
tmp_label = Label(self.root,text = "玩家"+str(i+1)+"的姓名")
tmp_label.grid (row = i+1,sticky = E)
tmp_entry = Entry(self.root)
tmp_entry.grid (row = i+1,column = 1,sticky = E)
label_list.append(tmp_label)
entry_list.append(tmp_entry)
'''for i in entry_list:
name_list.append(i.get())'''
tmp_button = Button(self.root,text = "确定",command = lambda:self.addPlayer(entry_list))
tmp_button.grid(row = self.n,column = 2,sticky = E)
#self.root.update()
#self.newTurn()
def __init__(self):
self.n = 0
self.t = 0
self.p = []
self.mindis = 0
self.maxdis = 0
self.G_list = []
self.mindis_list = []
self.maxdis_list = []
self.root = Tk()
self.root.title = ('黄金点游戏')
l_users = Label(self.root,text = "人数")
l_users.grid (row=0,sticky = W)
self.entry0 = Entry(self.root)
self.entry0.grid(row = 0,column = 1,sticky = E)
Button(self.root,text = "开始",command = lambda :self.Start(self.entry0.get())).grid(row = 0,column = 2)
self.root.mainloop()
'''while self.n<1:
print("请输入游戏人数:")
self.n = eval(input())'''
'''for i in range(self.n):
st = input("请输入玩家"+str(i)+"的姓名(若姓名为AI,则为AI):")
if(st != "AI"):
self.p.append(Player())
self.p[-1].setPlayerName(st)
else:
self.p.append(Player())'''
def addPlayer(self,entry_list):
for i in entry_list:
self.p.append(Player())
if i.get() != 'AI':
self.p[-1].setPlayerName(i.get())
self.newTurn()
def cal(self):
avg = 0.0
for i in self.p:
avg+=i.choice_i(self.t)
avg/=self.n
avg*=0.618
#print("裁判公布G值:",avg)
self.G_list.append(avg)
self.mindis =100.0
self.maxdis = 0.0
dis = []
for i in self.p:
dis.append(abs(i.choice_i(self.t)-avg))
self.mindis = min(dis)
self.maxdis = max(dis)
self.mindis_list.append(self.mindis)
self.maxdis_list.append(self.maxdis)
#print("maxdis:",self.maxdis," mindis:",self.mindis)
for i in range(self.n):
if dis[i] == self.mindis:
self.p[i].win(self.n)
elif dis[i] == self.maxdis:
self.p[i].lose()
else :
self.p[i].draw()
self.scoreBoard()
def input_number(self,entry_list):
mid = 50 * 0.618
if len(self.G_list) > 0:
mid = self.G_list[-1]
for i in range(self.n):
if self.p[i].is_human:
if not entry_list[i].get():
return
self.p[i].newChoice(mid,eval(entry_list[i].get()))
else:
self.p[i].newChoice(mid)
self.cal()
self.newTurn()
def newTurn(self):
self.t+=1
#print("第 "+str(self.t)+" 轮")
turn_label = Label(self.root,text = "第 "+str(self.t)+" 轮")
turn_label.grid(row = 0,column = 0,sticky = W)
self.turnLabel = Label(self.root)
label_list = []
entry_list = []
for i in range(self.n):
tmp_label = Label(self.root,text = "玩家"+str(i+1)+"的数字")
tmp_label.grid (row = i+1,column = 0,sticky = W)
if self.p[i].is_human:
tmp_entry = Entry(self.root)
tmp_entry.grid (row = i+1,column = 1,sticky = W)
tmp_entry.show = "*"
entry_list.append(tmp_entry)
else:
tmp_entry = Label(self.root,text = "AI 已经生成数字")
tmp_label.grid(row = i+1,column = 1,sticky = W)
entry_list.append(tmp_entry)
label_list.append(tmp_label)
tmp_button = Button(self.root,text = "确定",command = lambda:self.input_number(entry_list))
tmp_button.grid(row = self.n,column = 2,sticky = E)
def scoreBoard(self):
st = '第'+str(self.t)+'轮游戏\n裁判公布G值:'+str(self.G_list[-1])+'\n'
for i in self.p:
st+=i.showName()+":\n"+"输入 "+str(i.choice_i(self.t))+"\t得 "+str(i.gain_i(self.t))+'\t当前得分:'+str(i.score)+'\n'
messagebox.showinfo(title="计分板", message=st)
print("欢迎进入黄金点游戏!")
#while True:
game = Game()