历史模拟与蒙特卡洛模拟_骰子战中的蒙特卡洛模拟可能会使80位攻击者与20位防御者...

历史模拟与蒙特卡洛模拟

统计教程 (Statistics, Tutorial)

In this tutorial, I will explore the domain of Stochastic processes and simulations by performing 1 Million simulations of the same exact battles to see what is the best vs. worst-case scenario.

在本教程中,我将通过对相同的精确战斗进行一百万次模拟,探索随机过程和模拟的领域,以了解最佳情况与最坏情况的比较。

This will be the final result:

这将是最终结果:

Image for post
100.000 simulations, full battle reported
100.000模拟,完整战斗报告

SPQRisk (SPQRisk)

The rules of this experiment have been taken from SPQRisk, one version of the RISK game but set in the Roman Empire. It was one of the most fun games I was playing when growing up with my friends.

此实验的规则取自SPQRisk,这是RISK游戏的一个版本,但设定在罗马帝国。 这是我和朋友一起成长时最有趣的游戏之一。

Image for post

战斗就是这样 (This is how a battle works)

The game is organized in turns. Every player has at his disposal a number troops that he can use to attack the other players (then make alliances, betray, threat…).

游戏是轮流组织的。 每个玩家都可以使用一定数量的部队来攻击其他玩家(然后结成同盟,背叛,威胁……)。

When a player deploys his troops in hostile territory, the battle begins.

当玩家在敌对地区部署部队时,战斗便开始了。

  • Each player can as many dice as the number of available troops in the battle up to a limit of 3

    每个玩家最多可以将3个骰子与战斗中的可用兵力数量相提并论
Image for post
Dice limit
骰子限制

Meaning that if the defender has 2 troops and the enemy 3, he can roll a maximum of 2 dice, while the attacker, having 3 troops, can throw 3. In another example, if the defender has 3 troops and the enemy 5, both the defender and the attacker will use 3 dice.

也就是说,如果防御者有2个部队,而敌人3个,则他最多可以掷2个骰子,而拥有3个部队的攻击者可以掷3个骰子。在另一个示例中,如果防御者有3个部队,而敌人5个,则两个防御者和攻击者将使用3个骰子。

Image for post
First Roll, dice not ordered yet
第一卷,骰子尚未订购
  • After rolling the dice, each player will sort them in a descending order

    掷骰子后,每个玩家将按降序对其进行排序
Image for post
Dice in ascending order
骰子升序
  • The results are compared: the lower dice score will lose, and the troop of owned by the player who rolled the dice with the lower score will be killed.

    比较结果:骰子得分较低的人将丢失,掷骰子较低的骰子的玩家拥有的队伍将被杀死。
  • Because defense has the advantage, in case of equal score defense wins

    因为防守有优势,所以在得分相等的情况下防守获胜
Image for post
  • In case of a disparity in the dice numbers, AFTER the sorting has taken place, the extra dice are excluded

    如果骰子数量不一致,则在排序之后,多余的骰子将被排除在外
Image for post
  • The match ends either when one of the players has no more spare troops or the attacker retreats

    当其中一名球员没有更多的备队或攻击者撤退时,比赛结束

So far so good. As you can see, calculating the probabilities of the outcome of a battle is a very complex problem. It cannot be done by handwritten formulas (at least I am not there yet). The purpose of this article is to create an algorithm capable of performing up to million of simulations following the rules above:

到目前为止,一切都很好。 如您所见,计算战斗结果的概率是一个非常复杂的问题。 这不能通过手写公式来完成(至少我还不存在)。 本文的目的是根据上述规则创建一种算法,能够执行多达百万次仿真:

  • To approximate at best the probability distribution of an event

    尽可能近似地估计事件的概率分布
  • To give us an idea of HOW MUCH disadvantaged or advantaged we have before making any decision

    为了让我们了解在做出任何决定之前我们有多少劣势或优势

导入库 (Importing libraries)

import random
import pandas as pd

骰子辊 (Dice roller)

def dice_roll():
return random.randint(1, 6)

模拟单场比赛 (Simulating a single match)

Every attack can have an indefinite number of men. Both parties can have 3 men and finish the entire match by rolling the dice once. However, what if the players both have 10 troops? Simple, they will keep rolling their dices until one of them finishes the troop or the attacker decides to retreat. Therefore, every battle is a sequence of different matches where the dice are rolled each and every time with casualties for at least one of the parties.

每次攻击都可以有无限数量的人员。 双方都可以有3个人,并通过掷骰子一次完成整个比赛。 但是,如果双方都拥有10名士兵怎么办? 很简单,他们将继续掷骰子,直到其中一个完成兵力或攻击者决定撤退为止。 因此,每场战斗都是一系列不同的比赛,每一次掷骰子都会给至少一个当事方带来人员伤亡。

To simulate a match, I will begin by simulating a single battle. According to the number of dice of each player, the rolling takes place and the troops are being eliminated at every turn. Then, I will call the battle again in a recursive process, passing as variables the number of men who are still standing.

为了模拟一场比赛,我将从模拟一场战斗开始。 根据每个玩家的骰子数量,进行掷骰,并且每回合都会消灭部队。 然后,我将在递归过程中再次调用战斗,将仍然站立的人数作为变量传递。

def simulate_battle(annalis, iteration, white_army, black_army, return_winner):
annalis.append([iteration, white_army, black_army])
iteration += 1if white_army == 0 or black_army == 0:
if return_winner == 'all_data':
#return entire battle
return annaliselif return_winner == 'only_winner':
#print(white_army, black_army)
#only return winner
if white_army > black_army:
return 1
else:
return 0elif return_winner == 'man_killed':
return white_army - black_army

With the conditions above, the algorithm will keep running until one of the players has finished its troops. For example, if I pass in the white_army and black_army variables as 10, 10 (10 men for each player), the first simulation will have casualties resulting in 1 men lost for the defender and 2 for the attacker. Therefore, the function will call itself again, but this time with the inputs: 9, 8.

在上述条件下,该算法将继续运行,直到其中一名玩家完成其部队为止。 例如,如果我将white_army和black_army变量传递为10、10(每个玩家10个人),则第一个模拟会造成人员伤亡,导致防守者损失1个人,攻击者损失2个人。 因此,该函数将再次调用自身,但是这次使用以下输入:9、8。

  
#make rolls for white and black according to the quantity of their men
white_rolls = []
if white_army >= 3:
#then make 3 rolls
for _ in range(0, 3):
white_rolls.append(dice_roll())
elif white_army == 2:
#then only 2 rolls
for _ in range(0, 2):
white_rolls.append(dice_roll())
elif white_army == 1:
#the only 1 roll
for _ in range(0, 1):
white_rolls.append(dice_roll())black_rolls = []
if black_army >= 3:
#then make 3 rolls
for _ in range(0, 3):
black_rolls.append(dice_roll())
elif black_army == 2:
#then only 2 rolls
for _ in range(0, 2):
black_rolls.append(dice_roll())
elif black_army == 1:
#the only 1 roll
for _ in range(0, 1):
black_rolls.append(dice_roll())#sort both of them
white_rolls, black_rolls = sorted(white_rolls, reverse=True), sorted(black_rolls, reverse=True)
#equal size
minimum = min(len(white_rolls), len(black_rolls))
white_rolls, black_rolls = white_rolls[:minimum], black_rolls[:minimum]

保存每次迭代 (Save every iteration)

After every battle, I will keep track of the casualties for each iteration.

在每场战斗之后,我将跟踪每次迭代的人员伤亡情况。

#kill men
#white = defender
#black = attacker
for fighter in range(0, 3):
try:
if white_rolls[fighter] >= black_rolls[fighter]:
#kill black
black_army -= 1
else:
white_army -= 1
except:
pass
#print(str(white_army) + " defenders remaining", str(black_army) + " attackers remaining")

Now a single battle has ended. I will need to repeat the process until one of the two players has no more troops to attack or defend. I will use recursion by calling the function again until this condition is met.

现在,一场战斗已经结束。 我将需要重复此过程,直到两个玩家之一不再有要进攻或防御的部队为止。 我将通过再次调用函数来使用递归,直到满足此条件为止。

  return simulate_battle(annalis, iteration, white_army, black_army, return_winner)

模拟整个战斗 (Simulating an entire battle)

Now, I just need to call the function. I can freely choose which indicators to extract from the simulation.

现在,我只需要调用该函数。 我可以自由选择要从模拟中提取哪些指标。

归还整个战斗 (Returning an entire battle)

By running the following code, I am able to simulate an entire battle by even looking at the results for each turn.

通过运行以下代码,我什至可以查看每回合的结果来模拟整个战斗。

pd.DataFrame(simulate_battle(list(), 0, 10, 10, ‘all_data’))
  • Column 0: iteration

    第0列:迭代
  • Column 1: defenders

    专栏1:后卫
  • Column 2: attackers

    专栏2:攻击者
Image for post
SImulation of an entire battle, returning DataFrame
模拟整个战斗,返回DataFrame

返回伤亡人数 (Returning the number of casualties)

For example, ‘man_killed’ returns the number of defenders killed for every simulation.

例如,“ man_killed”返回每次模拟被杀死的防御者的数量。

simulate_battle(list(), 0, 10, 10, 'man_killed')

If the result of the simulation is -3, it means that by logic, the simulation has ended when one the defenders has lost all his 10 men, and there are still 3 man standing for the attacker.

如果模拟的结果是-3,则从逻辑上讲,当一名防御者失去了他的10个人,而仍然有3个人代表攻击者时,模拟结束。

Instead, if the function returns +3, the opposite is valid: the attacker has lost 10 men, while there are still 3 men standing for the defender.

相反,如果函数返回+3,则反之有效:攻击者失去了10个人,而仍有3个人代表捍卫者。

输赢 (Returning win vs. loss)

#defender wins vs. defender loss
defender = 0
attacker = 0
for _ in range(0, 1000000):
out = simulate_battle(list(), 0, 10, 10, 'only_winner')
if out == 1:
defender += 1
else:
attacker += 1print(defender, attacker, defender/attacker)
809686 190314 4.2544741847683305

In a 10 vs. 10 battle, the defender wins more than 4x times the attacker.

在10对10的战斗中,防御者的胜利是攻击者的4倍以上。

80位攻击者与20位防御者 (80 attackers vs. 20 defenders)

When I was a kid I remember almost surviving a battle against one of my friends who attacked my 20 men with his vast army composed of 80 troops. After the glorious battle, he was left with 5. I have always been curious about how to calculate the probabilities for the outcome of that battle.

当我还是个孩子的时候,我记得几乎和我的一位朋友幸存下来,他们用他的一支由80名士兵组成的庞大军队袭击了我的20个人。 在光荣的战斗之后,他剩下5人。我一直很好奇如何计算战斗结果的概率。

#man killed per simulation
man_killed = list()
for _ in range(0, 1000000):
man_killed.append(simulate_battle(list(), 0, 20, 80, 'man_killed'))

Because the complexity of the problem prevents me from calculating the probabilities by formulas, I can obtain the same result with a great number of simulations. To obtain the result I am searching for I will need to find out, among the overall number of simulations, which ones returned the indicator ‘man_killed’ < 5. Doing so, I will isolate all the cases in my favor. Dividing this number by the total number of simulations I can find my probability of having AT LEAST the same degree of success I had in that game when I was a kid.

因为问题的复杂性使我无法通过公式来计算概率,所以我可以通过大量的模拟获得相同的结果。 为了获得我要搜索的结果,我需要在所有模拟中找出哪些模拟返回了指标“ man_killed” <5。这样做,我将对所有情况进行隔离。 用这个数字除以模拟总数,我可以发现我获得最小的成功的可能性,就像我小时候一样。

#number of points after a treshold
man_killed_ = pd.DataFrame(sorted(man_killed, reverse=True))
man_killed_ = man_killed_.loc[man_killed_[0] >= -5]#successful simulations where defender has won or lost, but attackers remained with less or equal 5
print(len(man_killed)-man_killed_.shape[0], man_killed_.shape[0]/len(man_killed))
997746 0.002254

图形结果 (Graphing results)

绘制PDF(概率分布函数) (Graphing the PDF (Probability Distribution Function))

pd.DataFrame(man_killed).hist(bins=87, figsize=(15, 9))
Image for post

绘制时间序列 (Graphing the time series)

So far I have only graphed the results of the simulations. Because in the main function I have allowed myself to store the results of every simulation, I can graph them as time-series.

到目前为止,我只绘制了仿真结果的图形。 因为在主要功能中,我已经允许自己存储每次仿真的结果,所以我可以将它们绘制为时间序列。

#for one simulation only record white remaining
all_battles = list()
for _ in range(0, 100000):
all_battles.append([x[1] for x in simulate_battle(list(), 0, 20, 80, 'all_data')])
pd.DataFrame(all_battles)all_battles = pd.DataFrame(all_battles)
all_battles = all_battles.drop_duplicates()
all_battlesall_battles = all_battles.transpose()#graph all stocks
import matplotlib.pyplot as pltplt.figure(figsize=(28, 14))
fig = plt.plot(all_battles.index, all_battles, lw=1, alpha=1)
Image for post
100.000 simulations, full battle reported (typo: should be defender)
100.000模拟,完整的战斗报告(典型值:应为后卫)

翻译自: https://medium.com/towards-artificial-intelligence/monte-carlo-simulation-on-dice-battles-risk-80-attackers-vs-20-defenders-df6700e44763

历史模拟与蒙特卡洛模拟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值