第一次玩大话骰这个游戏是在大学,和本科舍友玩的时候突然提了一嘴,似乎可以用统计的方法去进行游戏决策辅助,当时提了一嘴也没有多想,没想到在异国他乡的现在,和舍友在车上谈论的时候记忆又翻了回来,于是回来又研究了一下,用了一些很粗浅的统计知识去建了一个模,又编了一个小程序去进行辅助。仅供参考。

1.规则

大话骰规则如下:

2.模型

我们需要输入的变量是

  1. 参与游戏的人数n

  2. 每个人的骰子数量p

  3. 自己的骰子情况,使用向量表示每个数字的个数

\[(a,b,c,d,e,f)\]

表示有a个1,b个2,c个3,d个4,e个5,f个6。

\[a+b+c+d+e+f=p\]

容易知道,每个人的骰子满足多项分布(Multinomial distribution)

\[(a,b,c,d,e,f)\sim Multinomial(p,1/6)\]

可以得到联合概率分布为

\[P(A=a,B=b,C=c,D=d,E=e,F=f)=\frac{p!}{a!b!c!d!e!f!}(\frac{1}{6})^{p}\]

\(a,b,c,d,e,f\)满足二项分布,可以得到

\[P(A=a)=\frac{p!}{a!(p-a)!}(\frac{1}{6})^a(\frac{5}{6})^{p-a}\]

在一次游戏中,使用\((a_1,a_2,a_3,...,a_n)\)代表每个人的骰子中a的个数.

使用我们需要的是

\[P(\sum_{i=1}^{n-1}a_i=A)=\frac{((n-1)p)!}{A!((n-1)p-A)!}(\frac{1}{6})^A(\frac{5}{6})^{(n-1)p-A}\]

下面的函数是输入概率与每个人骰子的结果,返回这个结果的概率

p=c(1/6,1/6,1/6,1/6,1/6,1/6)
multi <- function(p, x) {
  n <- sum(x)   ##试验次数

  f1 <- prod(p^x)
  f2 <- prod(factorial(x))
  return(factorial(n)*f1/f2)      
}

x=c(1,1,1,1,1,1)
multi(p,x)
## [1] 0.0154321

下面是二项分布的代码

x=c(0,1,2,3,4,5,6)
out=dbinom(x,6,1/6)

n为总人数,除自己之外的人数为n-1,自己的骰子的结果是已知的,所以除自己之外所有人每个数字的个数的概率为

multi_n <- function(n_1,p,pro) {
  x=seq(0,n_1*p,by=1)
  
  out=pbinom(x,n_1*p,pro)
  
  return(out)
}

由于在游戏中,1可以代表任何数,所以实际上,单个骰子的结果,每个数字出现的概率是\(1/3\)

3.实例

下面我举一个具体例子,假设有4个人,每个人有6个骰子,我们查看一局游戏中单个数字骰子的数量分布,查看概率密度图为

n=4 #人数
n_1=n-1
p=6 #骰子的个数

output=multi_n(n_1,p,1/3)


plot(output)

output
##  [1] 0.0006766395 0.0067663948 0.0326478551 0.1016650826 0.2310723840
##  [6] 0.4122426060 0.6085103465 0.7767398384 0.8923976140 0.9566519338
## [11] 0.9855663777 0.9960807210 0.9991474044 0.9998551006 0.9999814749
## [16] 0.9999983248 0.9999999045 0.9999999974 1.0000000000
max(which(output<0.5))
## [1] 6

可以看到,对于每个数字,其余三个人中的骰子六个的概率最高。

4.策略

假设我们目前的骰子的结果为\(x=(x_1,x_2,x_3,x_4,x_5,x_6)\),这是先验的,用\(max(x)\)表示个数最多的数字,\(argmax(x)\)表示最多的数量的数字。

假设对手,也就是上一家目前的骰子结果为\(y=(y_1,y_2,y_3,y_4,y_5,y_6)\),用\(max(y)\)表示个数最多的数字,\(argmax(y)\)表示最多的数量的数字。上一家喊的骰子为a个b,记为\(jd_y=(x,y)\)

Rmk: 对于我们来说,在对手理性的情况下,我们可以根据对手喊的骰子来判断对手的骰子分布,对手喊的骰子存在有效信息;但在对手是非理性的情况下,对手可能会存在夸大自己骰子的情况,来误导我们对于场上骰子数量偏向的判断。

所以,我们将对手分为两种情况来看,假设对手为理性,与假设对手不理性。

4.1假设对手为理性的

在对手为理性的情况下,我们要明确的一点是,对手喊的骰子与场上的骰子数量存在正相关性,也即

\[Corr(y,jd_y)>0\]

我们目前的骰子的结果为\(x=(x_1,x_2,x_3,x_4,x_5,x_6)\),对手喊的为\(jd_y=(num,int)\),我们将要喊的骰子为\(jd_x=(X,Y)\),我们此时有两个选择

\[Y=int, \space or \space Y=argmax(x)\]

我们分别寻找在这两个选项下概率最大时的X。

\(Y=int\),我们拥有这个数字的个数是\(y[int]\),在场上这个数字的总个数最大概率时的个数为

\[x[1]+x[int]+arg(binom([1:(n-1)*p],(n-1)*p,1/3))\]

由于理性对手中的int会相对多,所以这种情况下我们的决策为

\[\Big(x[1]+x[int]+arg(binom([1:(n-1)*p],(n-1)*p,1/3))+1,int \Big)\] 在这里,我们简单的加了一个1,因为我们可以根据先验信息,得到int的数量肯定会比无先验信息时的数量多,加一是最稳妥的做法。

\(Y=argmax(x)\)时,我们的决策就是

\[\Big(x[1]+x[argmax(x)]+arg(binom([1:(n-1)*p],(n-1)*p,1/3)),argmax(x) \Big)\]

4.2假设对手为非理性的

在对手为非理性的情况下,对手有可能随机的耍诈,即喊的骰子结果与自己的骰子情况不符,也即我们不能通过对手喊的结果来判断\(Corr(y,jd_y)\)

在这种情况下,我们选择最稳妥的办法,喊的策略与理性对手下\(Y=argmax(x)\)的策略一样

\[\Big(x[1]+x[argmax(x)]+arg(binom([1:(n-1)*p],(n-1)*p,1/3)),argmax(x) \Big)\]

或者

\[\Big(x[1]+x[int]+arg(binom([1:(n-1)*p],(n-1)*p,1/3)),int \Big)\]

代码

下面是对手理性的情况下的决策系统

dice_judge= function(n_p,n_d,my_re,ene_judge,p){

  n_1=n_p-1
  output=multi_n(n_1,n_d,1/3)
  judge_int1=ene_judge[2]
  judge_int2=which.max(my_re)
  judge_num1=my_re[1]+my_re[judge_int1]+max(which(output<=p))+1
  judge_num2=my_re[1]+max(my_re)+max(which(output<=p))
  
  out_put=list(output1=c(judge_num1,judge_int1),output2=c(judge_num2,judge_int2))
  
  
  if (out_put[[1]][1]<ene_judge[1] | (out_put[[1]][1]==ene_judge[1] & out_put[[1]][2]<=ene_judge[2])){
    out_put$output1=0
  }
  
  if (out_put[[2]][1]<ene_judge[1] | (out_put[[2]][1]==ene_judge[1] & out_put[[2]][2]<=ene_judge[2])){
    out_put$output2=0
  }
  
  
  # if (out_put[[1]][1]==ene_judge[1] & out_put[[1]][2]<=ene_judge[2]){
  #   out_put$output1=NULL
  # }
  
  
  # if (out_put[[2]][1]==ene_judge[1] & out_put[[2]][2]<=ene_judge[2]){
  #   out_put$output1=NULL
  # }
  
  if (out_put$output1[1]==0){
    out_put$output1=NULL
  }
  
  if (out_put$output2[1]==0){
    out_put$output2=NULL
  }
  
  if (purrr::is_empty(out_put)){
    out_put="over"
  }
  
  return(out_put)
}

上面的代码就是整个决策系统,这里的n_p代表有多少人,n_d代表每个人有几个骰子,my_re代表我的骰子结果,ene_judge代表上一家的骰子的声明,p代表我的风险期望,一般都是0.5,也就是代表我希望的获胜概率在\(1-0.5=50%\)以上,但由于大话骰属于游戏,存在非理性的情况,大家的风险预期会增加,所以可以适当提高p来增加风险。

下面示范

a=dice_judge(n_p=4,n_d=6,my_re=c(1,1,3,1,0,0),ene_judge=c(10,2),p=0.5)
a
## $output2
## [1] 10  3

上面的例子代表,如果有四个人玩游戏,每个人6个骰子,一局中我的骰子为\((1,1,3,1,0,0)\),上一家喊的是10个2,风险期望是0.5,那我应该喊10个3。

另一个例子

a=dice_judge(n_p=4,n_d=6,my_re=c(1,2,2,1,0,0),ene_judge=c(10,2),p=0.5)
a
## [1] "over"

如果有四个人玩游戏,每个人6个骰子,一局中我的骰子为\((1,2,2,1,0,0)\),上一家喊的是10个2,风险期望是0.5,那我应该质疑上一家。

结论

对手在非理性的情况下的决策模型约等于理性情况,这边就不做展示了。

本决策系统没有考虑对手喊1的情况,也就是没有考虑最后一条规则。

整体模型比较粗糙,有几个改进方向。一是可以通过学习对手的以往的游戏来判断对手是非理性还是理性,通过学习游戏习惯来改变自己的策略;二是可以通过以往的游戏判断大家的风险期望,来动态调整p。