加速风险战争游戏骰子滚动模拟

1 投票
3 回答
1525 浏览
提问于 2025-04-18 07:41

我和朋友们在想,在桌游《风险》中,不同情况下胜利的概率有多大。这款游戏是围绕一个类似地球的棋盘进行的世界大战,棋盘被划分成不同的领土。

对于不熟悉《风险》的人来说:

在游戏的每一轮中,攻击者可以选择入侵防守者的国家。攻击者和防守者会选择使用多少个骰子来进行战斗。例如,攻击者在某些情况下可以选择用3个骰子攻击,而防守者可能选择用2个骰子来防守。

Attacker rolls 3 dice and gets 6,4,1
Defender rolls 2 dice and gets 6,3

If the same number is rolled, defender always wins, so in this situation,
 we compare the attacker's two highest rolls against the defender's.
The defender's 6 beats the attacker's 6 and
 the attacker's 4 beats the defender's 3.

在这种情况下,每一方都会损失1个军队。这个过程可以继续进行,直到攻击者停止攻击或者军队用完为止,但我现在只对单次掷骰子的胜率感兴趣。

所以我写了一个程序来模拟很多次,而不是进入那些让人困惑的概率世界。因为我对编程还比较陌生,所以我在寻找一些提高效率的建议。现在我还不太懂多进程的概念,所以暂时不考虑这个。

我很惊讶进行一百万次模拟竟然花了大约20秒,我在想是不是我做错了什么,或者说这样的时间是正常的。

这是模拟的部分:

from random import randint

  # Pass in number of each dice and number of sim's
def simulate(attDice,defDice,rolls):
    attLosses = 0
    defLosses = 0
    for roll in rolls:               # Number of simulations
        attRolls = []                # List holding attack dice 'scores'
        for die in range(attDice):   # Number of attack dice rolled
            attRolls.append(randint(1,6))
        defRolls = []                #  List holding defence dice 'scores'
        for die in range(defDice):   # Number of defence dice rolled
            defRolls.append(randint(1,6))
        while len(attRolls) and len(defRolls): # For each 
            if max(attRolls) > max(defRolls):  # Att's must beat def's
                defLosses += 1
            else:
                attLosses += 1
            # Delete the highest number from each list
            del(attRolls[attRolls.index(max(attRolls))])  # This seems clumsy
            del(defRolls[defRolls.index(max(defRolls))])  # which is what makes me
                                                      # think there's a better way
    return attLosses,defLosses
# We then go and work out percentages etc.

3 个回答

0

这是一个使用numpy库进行100万次掷骰子的实现的开头。

我把这个内容标记为社区维基,欢迎大家扩展和编辑。

有3个攻击骰子和2个防御骰子。

import numpy
A = numpy.random.randint(1,6,size=(1000000,3)).sort()
D = numpy.random.randint(1,6,size=(1000000,2)).sort()
Ahigh = A[:,2]
Dhigh = D[:,2]
Awins1st = (Ahigh > Dhigh)
A2nd = A[:,1]
D2nd = D[:,1]
Awins2nd = (A2nd > D2nd)
# needed: tabulate results, remove smelly repeating code, generalize

Awins1stAwins2nd是各包含1000000个元素的真假数组,表示攻击者在比较3个攻击骰子和2个防御骰子时,是否赢得了第一次或第二次“战斗”。

1

这段话说得有点模糊,完全没有严谨的分析。

我对一百万次循环大约20秒的结果并不感到惊讶。我在Python中做过类似的实验/模拟,结果也是这样。其实,那些实验并不是特别需要快速完成,所以我没有进行优化。

而且,Python本身就不是以速度著称的。也许你习惯用一些底层语言。如果你在Java中做同样的实验,我会感到惊讶,如果它花的时间和Python一样。

(如果你真的想加快速度,或许在你的问题中提供一些背景信息会有帮助?看起来你的目的比较随意,所以除了做一些常量时间的加速和可能的一些微小优化,我觉得没什么太大的改变。)

0

因为你实际上是在对骰子列表进行排序,所以使用Python的排序功能会更好,这个功能很快。

生成随机数的速度也比较慢。如果只为所有骰子生成一个随机数,可以稍微加快这个过程。

在我的电脑上,如果attDice和defDice的数量比较少(小于等于5),进行一百万次掷骰子大约需要5秒钟。(如果用PyPy的话,只需要1秒。)

def freqs(attDice, defDice, rolls):
    m = min(attDice, defDice)
    freq = [0]*(m+1)
    for i in range(rolls):
        ar = random.randrange(6**attDice)
        dr = random.randrange(6**defDice)
        ad = [(ar / 6**j) % 6 for j in range(attDice)]
        ad.sort(reverse=True)
        dd = [(dr / 6**j) % 6 for j in range(defDice)]
        dd.sort(reverse=True)
        aws = sum(j > k for j, k in zip(ad, dd))
        freq[aws] += 1
    return freq

它会返回一个攻击者获胜的频率表。

撰写回答