[數據] 吸收馬可夫鏈狼人殺勝率計算

一些废话

<狼人杀英雄联赛>是狼人杀官方举办的最高规格赛事, 统计数据显示自开赛以来, 狼人阵营胜率拥有压倒性的优势.
尤其是今年的比赛, 决赛中狼人阵营直接九连胜零封了村民阵营.

按道理来说, 官方狼人杀经过了多年的发展, 板型平衡规则完善, 本身常规对局就很少有出现一边倒的情况.
在一些面杀的狼人杀赛事中, 甚至经过屡次削弱好人加强狼人才维持了平衡.

霸天猜一猜为什么狼人杀英雄联赛好人这么难赢

  1. 虽然打职业比赛的肯定都是逻辑高手, 但意味着狼人也是高手.
    狼人可以评估场上局势, 选择倒钩冲锋骗好人, 选择在合适的时机自爆, 努力使狼人收益最大化.
  2. 和面杀不一样, 因为不见面而且还都是陌生人, 不能抿牌也打不了人性流.
    大家都是格式化发言, 这就导致村民阵营的好人能获取的信息就大大减少了.
  3. 因为是职业比赛, 所以必须遵守发言规范.
    霸天的意思也不是这样做的对, 但常规对局的时候好人的贴脸和擦边发言, 力度是非常大的.

计算

就假如啊, 如果狼人发言也很好和好人一样好, 再把所有神牌技能都拔掉, 换成屠城规则, 算算好人的获胜概率是多少.

以预女猎守举例, 假如双爆把预言家砍了, 那就还剩下9个人, 2匹狼.
因为狼人发言和好人一样好, 那么白天就随机出局一个人, 晚上狼人袭击再出局一个好人, 看看全部狼先出局的概率和狼人屠城的概率.

这种无记忆又离散的状态转换问题, 当然是用吸收马可夫链了.
好人数量和狼人数量表示当前场上状态, 好人有7种情况狼人有3种情况就是21种状态.
21种状态中, 有些状态是永远无法到达的, 以下6种状态属于吸收状态.

好人数量狼人数量对局结果
50好人获胜
40好人获胜
30好人获胜
22狼人获胜
20好人获胜
11狼人获胜

咋一看好像是好人赢面大, 具体怎么样就来实际编程算一算吧.
发现一个问题, 因为场上存活人数不同, 状态转换的概率也不一样, 所以建立21*21的矩阵就需要算441次, 要霸天手算441次那也太折磨了.
除非再编程做到输入好人和狼人数量自动生成矩阵, 否则就只能算一次, 不然就是在浪费时间.
所以就只算一次就好了.

推错零次3.70%好人获胜
推错一次8.25%好人获胜
推错俩次18.41%好人获胜
推错三次70.15%狼人获胜

有0.51%的偏差, 但大概就是这么多吧.
看得出来只要发言好, 狼人是很好赢的.
就算考虑神牌跳出来的情况, 互相见面的狼人也可以和队友冲票增大好人出局的概率.

原始码

import numpy as np
#                    [1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21]
intilize = np.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
transmit = np.array([[0, 0, 0, 0,2/9,0,7/9,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''2'''              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''3'''              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''4'''              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''5'''              [0, 0, 0, 0, 0, 0, 0, 0,1/6,0,6/7,0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''6'''              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''7'''              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0,2/7,0,5/7,0, 0, 0, 0, 0, 0, 0, 0],
'''8'''              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''9'''              [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''10'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''11'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1/5,0,4/5,0, 0, 0, 0],
'''12'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''13'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,2/5,0,3/5,0, 0],
'''14'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''15'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
'''16'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'''17'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1/3,0,2/3,0],
'''18'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
'''19'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
'''20'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
'''21'''             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
result = intilize.dot(np.linalg.matrix_power(transmit, 10))
print(result)