Winning games with Python

Hasbro Risk Part 1: Mechanics and basic probabilities

GABRIEL MANUEL SIDIK
Tech Front
Published in
6 min readMay 19, 2020

--

Risk: The World Conquest Game
(Image taken from Hasbro’s official rule book)

In my own spare time during this circuit breaker (a.k.a social distancing) period, I sometimes fall back to explore online versions of traditional board games I used to play. One of which is Hasbro’s Risk.

As someone who likes to understand the mechanics of games, I decided to dig deeper to understand this beloved childhood game I used to play. Looking online, I found quite a few resources (and even journal articles!) of analyses done on this well-loved game (this article on Business Insider is an easy read, or if you feel especially technical, you can check out these deeper analyses done by folks at MIT and Stanford)

I’ve had initially intended to build an interactive web application for exploring various scenarios and probabilities in Risk. However, while planning out what I would like to explore, I’ve realized that there are quite a few interesting concepts that pop up (Hint: probability, statistics, recursion, memoization, game theory, even a bit of finance etc.). Therefore, I’ve decided to write a series to walkthrough my personal journey in learning more about this game — hopefully also highlighting and teaching some of these relevant concepts along the way.

An Introduction to Risk

In the classic “World Domination RISK®” game of military strategy, you are battling to conquer the world. To win, you must launch daring attacks, defend yourself on all fronts, and sweep across vast continents with boldness and cunning. But remember, the dangers, as well as the rewards, are high. Just when the world is within your grasp, your opponent might strike and take it all away!

Risk is a turn-based game centered around a world map —

(Image from Wiki Commons here)

The world map is split into continents (each one colored differently in the figure above), which is further split into individual territories.

In one common variant, players place units one-by-one on an unoccupied territory (thus owning that territory). Once all territories are occupied, players will begin to “reinforce” their existing territories by adding more units to their existing territories until all players have placed their initial allocated number of units.

At the start of each player’s turn, the player will receive some units to reinforce current territories (this is based on the number of territories occupied, continents fully under their control and trading in of cards).

Subsequently, the player will be able to choose to attack neighbouring territories owned by other players. This attack phase will be a key component of the analysis that we will explore in this article and the next subsequent few articles of this series!

To find out more details about the official rules, feel free to check out Hasbro’s official rule book here!

The Attack Phase

A couple basic rules about attacking -

  • One can only attack adjacent territories
  • The territory you are attacking from must retain at least 1 “garrison unit” — i.e. if you have 2 units in a territory and intend to attack an opposing territory, 1 unit must remain as “garrison” for the current territory and only 1 unit can attack.
  • A maximum of 3 units can participate in an “attack” (represented by dice rolls) and a maximum of 2 units can “defend” (defender dice rolls) in a particular battle.
  • To resolve a battle, line up the dice rolls in descending order — the highest “attacker” vs “defender” pair of dice will be compared and likewise for the 2nd pair. In the case of a tie — the defender would win (a so-called “defenders’ advantage”)
(Image taken from Hasbro’s official rule book)

To find out more details about the official rules, feel free to check out Hasbro’s official rule book here!

Let’s jump into analysing this with Python!

The first order of business then would be to understand the various probabilities involved in different kind of “battle scenarios”! In Risk, there would be a total of 6 attacker-defender scenarios - (1v1, 1v2, 2v1, 2v2, 3v1, 3v2).

To decide whether one should attack another territory, we will want to know the probabilities associated with each of these scenarios.

One way of doing this would be to iterate through all the possibilities of dice rolls for each scenario, and determining through comparison the result of the battle (how many units does the attacker and defender lose).

Here is a sample Python code that does this for the 3v2 scenario:

There are quite a few things going on here:

Generating all the possible combinations of dice rolls

Firstly, I used the product function from the Python itertools library to generate all the possible dice rolls in a 3v2 scenarios (line 30–37). As the 3v2 scenario would have a total of 5 dice, and each die roll can take on a value between 1–6, the product function, when taking in this range of numbers (1-6) and the repeat keyword set to 5, all possible dice rolls possible will be enumerated.

Simulating the outcome of the battle from the dice rolls

For each of the possible combinations, I call a function determine_win_of_attacker_3v2 to… determine the number of units of the attacker which will win.

Within the determine_win_of_attacker_3v2 function, I use the in-built Pythonsorted function to sort the dice combinations in descending order.

And subsequently check what the battle outcome is for each pair of dice rolls.

I store the result of each dice roll in the dictionary probabilities_3v2 (to be exact these are not probability measures, but rather the number of dice roll combinations which result in a particular battle outcome).

Printing out the output dictionary we get:

{'win2': 2890, 'win1': 2611, 'win0': 2275}

This means that out of the 6⁵ possible dice states, there are 2890 states in which 2 attackers will win, 2611 states in which 1 attacker will win, and 2275 states in which the 0 attackers will win. To get the sense of the probabilities — we can use a normalize function:

This function divides each of the possible end states by the total number of states possible — giving us the probabilities associated with the outcome of the battle.

We obtain the normalized probabilities as such:

{'win2': 0.37165637860082307, 'win1': 0.3357767489711934, 'win0': 0.2925668724279835}

We can do this analysis for all possible battle scenarios and obtain the respective results:

NUMBERSSSSSSSSS!!!! (Forewarning: Python does some rounding off of these numbers, this may or may not be a concern in our later analyses 😜)

A couple things that clearly stand out — in 3v1, 2v1, 1v2, 1v1 battles, as there is only ultimately 1 “skirmish” (die vs die comparison) there is no way for the attacker to win 2 skirmishes.

We have also shown the underlying intuition for individual battle scenarios — the more dice we have rolling, the better the odds are. Compare the 3v1 battle, where there is almost a 1/3 chance of winning outright against the 1v2 battle — where there is a 3/4 chance of losing.

Also — a (slightly more sane one) for your reference if you ever need it for board game night.

Conclusion

So that’s our first rule-of-thumb for World Domination - attack with as many (3) units if you can, local superiority is key!* — next, we will be exploring the probabilities of extended battles (when we have much more than 3 attackers against much more than 2 attackers)

I’ve also shown how to use the itertools Python library, as well as some basic data structures (dictionaries)that we used to store information in. On the programming side of things, we’ll be exploring some recursion and memoization moving on and a bit more probability/statistics.

*Unless… For reasons we will explore further down the road

Resources and further readings

  1. Can’t wait for the next article of the series to find out more? — DataGenetics has quite an in-depth analysis on Risk (in the course of this series, I intend to go much deeper in the analysis though). Also, I feel honor-bound to shout out to them as well! I used the published results from DataGenetics to make sure my computed probabilities are not incorrect. Because bugs.

--

--

GABRIEL MANUEL SIDIK
Tech Front

New guy exploring all fields of technology. Senior of SMU Business Intelligence and Analytics Club