Add files via upload
This commit is contained in:
1 parent
e61f2070a3
commit
f63b403872
1 file changed
+34
-30
+34
-30
@@ -197,7 +197,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[3, 3, 6]"
|
||||
"([5, 5, 4], [5, 1], (1, 1))"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
@@ -206,7 +206,10 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"random_roll(3) # Example roll"
|
||||
"# Example battle\n",
|
||||
"A_dice = random_roll(3)\n",
|
||||
"D_dice = random_roll(2)\n",
|
||||
"A_dice, D_dice, battle_deaths(A_dice, D_dice)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -256,7 +259,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(9,)"
|
||||
"(11,)"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
@@ -272,7 +275,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"That final state says that the attackers won—there are 9 attackers and no defenders left. \n",
|
||||
"That final state says that the attackers won—there are 11 attackers and no defenders left. \n",
|
||||
"\n",
|
||||
"# Predicting the Unfinished Game: Estimating Probabilities\n",
|
||||
"\n",
|
||||
@@ -354,15 +357,14 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"ProbDist({(4,): 0.1,\n",
|
||||
" (15,): 0.1,\n",
|
||||
" (20,): 0.1,\n",
|
||||
" (1, 1, 5, 1): 0.1,\n",
|
||||
" (16,): 0.2,\n",
|
||||
"ProbDist({(5,): 0.2,\n",
|
||||
" (8,): 0.2,\n",
|
||||
" (22,): 0.1,\n",
|
||||
" (21,): 0.1,\n",
|
||||
" (11,): 0.1,\n",
|
||||
" (6,): 0.1,\n",
|
||||
" (12,): 0.1,\n",
|
||||
" (8,): 0.1})"
|
||||
" (0, 5, 1): 0.1,\n",
|
||||
" (13,): 0.1,\n",
|
||||
" (1, 1, 1): 0.1})"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
@@ -400,7 +402,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"0.7960000000000003"
|
||||
"0.8190000000000004"
|
||||
]
|
||||
},
|
||||
"execution_count": 16,
|
||||
@@ -427,7 +429,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"0.8300000000000003"
|
||||
"0.8130000000000003"
|
||||
]
|
||||
},
|
||||
"execution_count": 17,
|
||||
@@ -458,11 +460,11 @@
|
||||
"\n",
|
||||
"In deciding whether to use the exact calculation approach, there are three considerations:\n",
|
||||
"\n",
|
||||
"- **Is it worth the effort?** If I only wanted to know whether the attackers chance of winning is more or less than 50%, then the Monte Carlo approach is the simplest way to answer the question. The code will be simple and straightforward. If I need 0.0001% precision on the win probability, then exact calculation is called for.\n",
|
||||
"- **Is it worth the effort?** If I only wanted to know whether the attackers chance of winning is more or less than 50%, then the Monte Carlo approach is the quickest way to answer the question. The code will be simple and straightforward. If I need 0.0001% precision on the win probability, then exact calculation is called for.\n",
|
||||
"- **Is it possible?** The exact calculation approach only works for **finite games**. \n",
|
||||
"- **Is it feasible?** Computation may take too long if there are too many possible states of the game.\n",
|
||||
"\n",
|
||||
"**Worth the effort?** In general, the code for an exact calculation is more complex than for a Monte Carlo simulation, and thus it will take more time to write the code, and there will be more opportunity for bugs to sneak in. Monte Carlo code deals with the single current state of the simulation, for example: \n",
|
||||
"**Worth the effort?** In general, the code for an exact calculation is more complex than for a Monte Carlo simulation, and thus it will take more time to write and debug the code. Monte Carlo code deals with the single current state of the simulation, for example: \n",
|
||||
"\n",
|
||||
" while not terminal(state):\n",
|
||||
" \n",
|
||||
@@ -470,13 +472,13 @@
|
||||
"\n",
|
||||
" while any(not terminal(state) for state in probdist):\n",
|
||||
" \n",
|
||||
"Adding all these loops to the code makes it more complex.\n",
|
||||
"Adding all these loops to the code makes it more complex. After the shared basics of representing states and battle outcomes, we need just just 10 non-comment lines of code to implement the Monte Carlo method (in `simulate_campaign`, `monte_carlo`, and `random_roll`), but 22 lines to implement exact calculation (in `campaign_probdist`, `battle_deaths_probdist`, `battle_update_probdist`, and `all_rolls`).\n",
|
||||
"\n",
|
||||
"**Possible?** Imagine a *Risk* rule change where if the attackers roll three 6s and the defneders roll two 6s, then both sides *gain* an army. For the Monte Carlo simulation it would be trivial to add a single `if` statement to handle this rule, and the expected run time of the program would hardly change. But with the exact calculation everything has changed, because we now have an **infinite** game, where there will always be some possible state that has not terminated. (To deal with these we would have to make some compromises, such as stopping the calculation when there are still some nonterminal states, as long as they have very low probability.)\n",
|
||||
"**Possible?** Imagine a *Risk* rule change where if the attackers roll three 6s and the defneders roll two 6s, then both sides *gain* an army. For the Monte Carlo simulation it would be trivial to add a single `if` statement to handle this rule, and the expected run time of the program would hardly change. But with the exact calculation everything has changed, because we now have an **infinite** game: no matter how far ahead we look, there will always be some possible states that have not terminated. (To deal with these we would have to make some compromises, such as stopping the calculation when there are still some nonterminal states, as long as they have very low probability in total.)\n",
|
||||
"\n",
|
||||
"**Feasible?** Imagine that before each battle, every army independently has the option to take one of ten possible actions (e.g. to move to a safe neighboring territory). Then with 100 armies, the very first move has $10^{100}$ outcomes, meaning that the probability distribution requires more states than the number of atoms in the universe. (To deal with this we would either stick with the Monte Carlo simulation, or a varioant of it such as [particle filtering](https://en.wikipedia.org/wiki/Particle_filter) in which we maintain a **sample** of several possible states–more than a single state, but less than the complete state distribution.)\n",
|
||||
"**Feasible?** Imagine a rule change where before each battle, every army independently has the option to take one of ten possible actions (e.g. to move to a safe neighboring territory). Then with 100 armies, the very first move has $10^{100}$ outcomes, meaning that the probability distribution requires more states than the number of atoms in the universe. (To deal with this we would either stick with the Monte Carlo simulation, or a variant of it such as [particle filtering](https://en.wikipedia.org/wiki/Particle_filter) in which we maintain a **sample** of several possible states–more than a single state, but less than the complete state distribution.)\n",
|
||||
"\n",
|
||||
" The *Risk* campaign problem as it stands leads to a very efficient exact probability calculation (possible, feasible, and worth it, IMHO). If there are $n$ total armies to start, then there are fewer than $n^2$ possible states, and the game can last no more than $n$ moves. With $n$ in the range of a few hundred, computation takes less than a second; much faster and more accurate than doing 100,000 simulations."
|
||||
" The *Risk* campaign problem as it stands leads to a very efficient exact probability calculation (possible, feasible, and, IMHO, worth it). If there are $n$ total armies to start, then there are fewer than $n^2$ possible states, and the game can last no more than $n$ moves. With $n$ in the range of a few hundred, computation takes less than a second; much faster and more accurate than doing 100,000 simulations."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -487,7 +489,7 @@
|
||||
"\n",
|
||||
"The function `battle_deaths` was defined above to return the specific death counts for a specific dice roll. \n",
|
||||
"\n",
|
||||
"Now we'll define `battle_deaths_probdist` to give a probability distribution over all possible battle outcomes, corresponding to all possible dice rolls. The input to `battle_deaths_probdist` is a state giving the number of attacking and defending armies for just this one battle (not the total number of attacking and defending armies). Thus, the attackers will always be 3, 2, or 1, and the defenders will always be 2 or 1. I define it this way, rather than passing in the full state of the campaign, so that I can **cache** the results and reuse them in subsequent battles, for efficiency."
|
||||
"Now we'll define `battle_deaths_probdist` to give a probability distribution over all possible battle outcomes, corresponding to all possible dice rolls. The input to `battle_deaths_probdist` is a state giving the number of attacking and defending armies for just this one battle (*not* the total number of attacking and defending armies in the current state). Thus, the attackers will always be 3, 2, or 1, and the defenders will always be 2 or 1. I define it this way so that I can **cache** the results and reuse them in subsequent battles, for efficiency."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -498,12 +500,13 @@
|
||||
"source": [
|
||||
"@lru_cache()\n",
|
||||
"def battle_deaths_probdist(state) -> ProbDist:\n",
|
||||
" \"\"\"A probability distribution of deaths in a battle.\"\"\"\n",
|
||||
" \"\"\"A probability distribution of deaths in a battle.\n",
|
||||
" State is (A, D) where 1 <= A <= 3 and 1 <= D <= 2.\"\"\"\n",
|
||||
" return ProbDist(battle_deaths(A_dice, D_dice)\n",
|
||||
" for A_dice in rolls(A(state))\n",
|
||||
" for D_dice in rolls(D(state)))\n",
|
||||
" for A_dice in all_rolls(A(state))\n",
|
||||
" for D_dice in all_rolls(D(state)))\n",
|
||||
"\n",
|
||||
"def rolls(n) -> Iterable[Dice]: \n",
|
||||
"def all_rolls(n) -> Iterable[Dice]: \n",
|
||||
" \"\"\"All possible rolls of `n` dice.\"\"\"\n",
|
||||
" return itertools.product(die, repeat=n)"
|
||||
]
|
||||
@@ -536,9 +539,9 @@
|
||||
"source": [
|
||||
"# Exact Probability Distribution over a Campaign\n",
|
||||
"\n",
|
||||
"The function `campaign_probdist` mimics `campaign`, except that the former updates a `ProbDist` whereas the later updates a `State`. We start with certainty: there is a 100% chance that we are initially in the `start` state. But the [fog of war](https://en.wikipedia.org/wiki/Fog_of_war) means that uncertainty arises: we don't know how the dice will land, so we don't know the outcome of the very first battle. Subsequent battles add more uncertainty. \n",
|
||||
"Our old function `campaign` is mimiced by `campaign_probdist`, except that the former updates a `State` whereas the later updates a `ProbDist`. We start with certainty: there is a 100% chance that we are initially in the `start` state. But the [fog of war](https://en.wikipedia.org/wiki/Fog_of_war) means that uncertainty soon arises: we don't know how the dice will land, so we don't know the outcome of the very first battle. Subsequent battles add more uncertainty. \n",
|
||||
"\n",
|
||||
"The function `campaign_probdist` calls the function `battle_update_probdist` to update the probability distribution to account for one battle, in all the possible ways it can turn out. The key line in `battle_update_probdist` is\n",
|
||||
"`campaign_probdist` calls `battle_update_probdist` to update the probability distribution to account for one battle, in all the possible ways it can turn out. The key line in `battle_update_probdist` is\n",
|
||||
"\n",
|
||||
" outcomes[update(state, dead)] += probdist[state] * dead_probdist[dead]\n",
|
||||
" \n",
|
||||
@@ -795,6 +798,7 @@
|
||||
" plt.figure(figsize=(10, 5)); plt.grid()\n",
|
||||
" plt.title('Each line: attackers with Δ more armies than defenders')\n",
|
||||
" plt.xlabel('Number of Defenders'); plt.ylabel('Win Probability for Attackers')\n",
|
||||
" plt.yticks([i / 10 for i in range(11)]); plt.xticks(range(0, 61, 5))\n",
|
||||
" for delta in deltas:\n",
|
||||
" winprobs = [attacker_win_probability(campaign_probdist((max(0, d + delta), d))) \n",
|
||||
" for d in defenders]\n",
|
||||
@@ -809,7 +813,7 @@
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 720x360 with 1 Axes>"
|
||||
]
|
||||
@@ -828,7 +832,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Note that the purple line (fourth from bottom), where the number of attackers exactly equals the number of defenders, gives a low win probability for a small attacking force, but reaches 50% for 12-on-12, and 73% for 60-on-60. The red line, where the attackers have one more army than the defenders, dips from one to two defenders but is over 50% for a 6-on-5 attack. Similarly, the green line, where the attackers have a surplus of two armies, dips sharply from 75% to 66% as the number of defenders goes from 1 to 2, dips slightly more for 3 and 4 defenders, and then starts to rise. So overall, an attacker does not need a big advantage in armies as long as there are many armies on both sides. Even when the attacker is at a disadvantage in numbers (as in the bottom grey line where the attacker has five fewer armies), the attacker can still have an advantage in win probability; with 55 against 60 the win percentage is about 57%."
|
||||
"Note that the purple line (fourth from bottom), where the number of attackers exactly equals the number of defenders, gives a low win probability for a small attacking force, but reaches 50% for 12-on-12, and 73% for 60-on-60. The red line, where the attackers have one more army than the defenders, dips from one to two defenders but is over 50% for a 6-on-5 attack. Similarly, the green line, where the attackers have a surplus of two armies, dips sharply from 75% to 66% as the number of defenders goes from 1 to 2, dips slightly more for 3 and 4 defenders, and then starts to rise. So overall, an attacker does not need a big advantage in armies as long as there are many armies on both sides. Even when the attacker is at a disadvantage in numbers (as in the bottom grey line where the attacker has five fewer armies), the attacker still has bettern than 50/50 odds with 45 attackers or more."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -887,7 +891,7 @@
|
||||
"assert battle_deaths_probdist((2, 2)) == ProbDist({(2, 0): 581, (1, 1): 420, (0, 2): 295})\n",
|
||||
"assert battle_deaths_probdist((3, 2)) == ProbDist({(2, 0): 2275, (1, 1): 2611, (0, 2): 2890})\n",
|
||||
"\n",
|
||||
"assert set(rolls(2)) == {(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), \n",
|
||||
"assert set(all_rolls(2)) == {(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), \n",
|
||||
" (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), \n",
|
||||
" (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), \n",
|
||||
" (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), \n",
|
||||
|
||||
Reference in new issue
Block a user