3 Dice Attacking | 2 Dice Attacking | 1 Die Attacking | |
---|---|---|---|
2 Dice Defending | 37% / 34% / 29% | 23% / 32% / 45% | 25% / 75% |
1 Die Defending | 66% / 34% | 58% / 42% | 42% / 58% |
import itertools from enum import Enum class Outcome(Enum): ATT_LOSES = 0 DEF_LOSES = 1 ONE_EACH = 2 def eval_roll(attack_dice, defense_dice): attack_dice_sorted = tuple(sorted(attack_dice, reverse=True)) defense_dice_sorted = tuple(sorted(defense_dice, reverse=True)) att_loses = def_loses = 0 num_comparisons = min(len(attack_dice), len(defense_dice)) for i in range(num_comparisons): if attack_dice_sorted[i] > defense_dice_sorted[i]: def_loses += 1 else: att_loses += 1 if att_loses == num_comparisons: return Outcome.ATT_LOSES elif def_loses == num_comparisons: return Outcome.DEF_LOSES else: return Outcome.ONE_EACH def calculate_roll_probabilities(num_attack_dice, num_defense_dice): total_num_dice = num_attack_dice + num_defense_dice roll_combinations = list(itertools.product(range(1, 7), repeat=total_num_dice)) num_rolls = len(roll_combinations) roll_results = {} for combination in roll_combinations: attack_dice = combination[:num_attack_dice] defense_dice = combination[num_attack_dice:] outcome = eval_roll(attack_dice, defense_dice) if outcome.name in roll_results: roll_results[outcome.name] += 1 else: roll_results[outcome.name] = 1 for outcome in roll_results: roll_results[outcome] = roll_results[outcome] / num_rolls * 100 return roll_results dice_num_combinations = list(itertools.product(range(1, 4), range(1, 3))) for dice_combo in dice_num_combinations: results = calculate_roll_probabilities(dice_combo[0], dice_combo[1]) print(f'A: {dice_combo[0]} - D: {dice_combo[1]} ==> {results}')
The following graph shows the probability of an attacker beating a defender given a certain number of attacking and defending units. Note that the table does not take into account the attacker that stays behind to hold the territory.
These values were not calculated theoretically but through simulation. A program ran 10000 battle simulations for each combination of attackers and defenders, and counted how many of them won. The built-in assumption is that every battle runs until one of the armies is wiped out. Of course, in the game, players often decide to break off battles before they reach the end if they turn out to be unfavorable, and that is not captured in these values.
The more the square is red, the more the attacker is favored. The more it is blue, the more the defender is favored.
Some interesting things to note are