On Risk Legacy, and failing to do the math

Risk Legacy is a campaign version of Risk. It plays like a house-rules version of Risk initially, but the game changes dramatically over time as players customize it. Not only do players write on the board and put terrain-altering stickers and notes on the board, but the various factions have special abilities that change as well. There are also a number of packets marked “open when [condition] occurs”, which include instructions to change the game even further (such as alterations to the rules). It’s a wild ride.

The basic rules for Risk Legacy combat go like this:

  • The attacker may attack with 1, 2, or 3 troops (assuming they have that many available to move into the territory, should they succeed).
  • The defender may defend with 1 or 2 troops (assuming they have that many in the territory).
  • The attacker rolls 1-3 dice appropriately.
  • The defender rolls 1 or 2 dice appropriately.
  • Both the attacker and the defender line the dice up in order, from highest to lowest.
  • Compare the two highest dice. If the attacker’s die is higher than the defender’s, the attacker wins. Otherwise, the defender wins (so defender wins on ties).
  • (if applicable) Compare the second-highest dice. Same rules on winning.
  • One troop is destroyed for each of the owner’s losses.
  • If the defender has no troops left, the attacker wins.
  • If the attacker hasn’t won, they may either rinse and repeat or back off.

While playing Risk Legacy on Sunday, I noticed that my friend would only use one troop to defend, even when they had more than one available. “What’s up with that?”

“If you only defend with one troop, you’re only risking one troop, so your chances are better.”

I was confident that my friend was wrong, but I couldn’t remember the formulas to prove it with the probabilities. (I also had the gut feeling that the game designers wouldn’t set you up to shoot yourself in the foot, since – in my friend’s scenario – there would never be a good reason to use two troops instead of one. But “It would be better game design if I’m right” is not a convincing argument. Even when “game designer” is on your business cards.)

I was still thinking about this when I got home. I could have looked up the math, and that might have been a more sensible approach – but instead, I wrote a program to demonstrate it through play.

I didn’t write this with Inform 7, but I certainly could have. Parser languages excel at modeling the physical world (it’s the heart of parser, after all!) My approach would have been something like (in a very broad overview):

  • Dice are a kind of object. A die can be black or red (assigning it to the attacker or defender).
  • Rolling is an action applying to nothing. “Roll” will roll all the dice in your inventory.
  • The “carry out” for rolling does the battle math – put a random 1d6 on each die, separate dice by color and sort them in order, then compare highest to highest, compare second highest to second highest, etc, etc.
  • The chalkboard is a thing. Examining the chalkboard shows how many troops are left. Update the chalkboard’s information at the end of rolling the dice.
  • Roll-repeat is an action applying to one value. “Roll N times” will roll all the dice in your inventory N times, probably with some messaging suppression to avoid massive scroll.

The play experience here would be: pick up some dice, roll them a lot of times, then admire the results on the chalkboard.

I opted for C# over I7 because I wanted to iterate over thousands of rolls. I7 could do that, but it would take longer, since I7 would be handling the entire world model. (Also, I7 remains my strongest language, so I opt for C# whenever it’s reasonable. Carnegie Hall approach.)

My C# version is structured instead like this:

  • A die (a Dice, actually) is an object. A die knows its last roll. A die can roll or be compared to a defending die to figure out which last roll was higher.
  • A battle is an object. A battle can fight. When a battle fights, it runs the entire Risk Legacy combat process – pick the number of dice, create the dice, decide how many times to roll the dice, and then roll/compare/destroy troops that many times before reporting results.

Effectively, the program is handling some of the sequencing that the player would have been responsible for in the I7 version. So here, the user just enters the number of dice and the number of repetitions, and the program does the rest.

The C# source is up on GitHub, if you’d like to take a look. But if you’d rather just know the results…

(Datta and an incredibly small spoiler below the image.)


I set up these examples with 100,000 troops on each side at the beginning. The left result is “how many attackers were left at the end” and the right result is “how many defenders were left at the end”.

  • Attack 1, Defend 1: none, 28769
  • Attack 2, Defend 1: 26858, none
  • Attack 3, Defend 1: 48447, none
  • Attack 2, Defend 2: none, 35,657
  • Attack 3, Defend 2: 15225, none
  • Attack 3, Defend 2 with bunker (+1 to highest die, not to go above 6):  none, 20505
  • Attack 3, Defend 2 with ammo shortage (-1 to highest die, not to go below 1):  48817, none

If you rerun this, your mileage may vary a little – random seeds do, after all. But it’s clear that, while the lone troop standing against the horde is dramatic, it’s not as effective as two troops fighting together.

Also, ammo shortages are so much worse than I realized. And at this point in our campaign, the map is littered with ammo shortages, but the Enclave of the Bear is Well-Supplied, and therefore immune to ammo shortages. I think I know what faction I want next time….



Bookmark the permalink.


  1. % echo "1\n1\n1\nn\nn" | mono bin/Debug/RiskLegacyDice.exe 130 ↵ ✭
    Risk Legacy Odds Test

    How many black dice is the attacker using?> How many red dice is the defender using?> How many troops are they starting with (apiece)?> Is there a bunker? (y for yes)> Is there an ammo shortage? (y for yes)>

    Attack dice rolled: 5
    Defend dice rolled: 3
    Attacker 5 vs defender 3. Defender loses.
    * Attacker loses 0, defender loses 1. *

    Attacking troops remaining: 1 (0 destroyed).
    Defending troops remaining: none (1 destroyed).
    Run another battle? (y to continue, anything else to quit)>
    Unhandled Exception:
    System.NullReferenceException: Object reference not set to an instance of an object
    at RiskLegacyDice.MainClass.Main () [0x00000] in :0
    [ERROR] FATAL UNHANDLED EXCEPTION: System.NullReferenceException: Object reference not set to an instance of an object
    at RiskLegacyDice.MainClass.Main () [0x00000] in :0

  2. I think there’s an easy proof that defending with 1 troop is a bad idea, that doesn’t involve any math.

    Suppose we roll one red and one white die, no matter what. If you’re defending with two troops, both count normally. If you’re defending with one troop, only the red die counts on this go-round; the white die counts on the next go-round (but your opponent doesn’t get to see it).

    Now, if you’re defending with the two troops, you match the higher of the two dice with your opponent’s highest die, and the lower with your opponent’s next highest die. If you’re defending with one troop, you match the red die against the opponent’s highest die this time, and the white die against the opponent’s highest die next time.

    No matter how many dice the opponent is rolling, if you’re defending one at a time, your lower roll will be at a disadvantage compared to defending with two. Suppose the white die is lower–if you had defended with two, it would be getting matched with the opponent’s second-best die this time. Since you’re defending with one, it’ll get matched with the opponent’s best die next time. The second-best die this time can be expected to be worse than the best die next time, so this is obviously a disadvantage.

    …at least, I think it’s obvious. These things can be tricky.

Leave a Reply

Your email address will not be published. Required fields are marked *