My post on probabilities of Marvel Heroic Roleplaying dice pools has gotten a lot of attention, so I’ve decided to share some code that can be used to compute the expected results of dice pools for Cortex+ games. The code is in C# and was written as a very basic console application that prints out the results for a hard-coded set of dice (I was too lazy to add command line support since I had originally intended the code to be a throwaway project that I was just using to crunch numbers while writing the mixed dice pools post).

Here’s the code:

using System;
using System.Collections.Generic;

namespace CortexPlusMath
{
    class Program
    {
        static void Main(string[] args)
        {
            // Captain America: 
            // Solo, Enhanced Strength, Weapon, Combat Master
            int[] dice =  { 6, 8, 8, 10 }; 
            //int[] dice = { 6, 8, 8, 10, 4 }; 
            //int[] dice = { 6, 8, 8, 10, 8 }; 
            //int[] dice = { 6, 8, 10, 10 }; 

            double average = 
                ComputeAverageResult(dice);
            double meanComplications = 
                ComputeAverageComplications(dice);
            double chanceOfComplication = 
                ComputeChanceOfComplications(dice);
            double averageEffectDie = 
                ComputeAverageEffectDie(dice);

            string diceString = "";
            foreach (int d in dice)
            {
                diceString += "d" + d.ToString() + " "; 
            }

            Console.WriteLine("Cortex+ Average");
            Console.WriteLine("  Dice Pool: " 
                + diceString);
            Console.WriteLine("  Average Result: " 
                + average.ToString());
            Console.WriteLine("  Average Effect Die: " 
                + averageEffectDie.ToString());
            Console.WriteLine("  Average Number of Complications: " 
                + meanComplications.ToString());
            Console.WriteLine("  Probability of 1+ Complications: " 
                + chanceOfComplication.ToString());
        }

        static private List GetResultsTable(int[] dice)
        {
            List possibleResults = new List();
            foreach (int sides in dice)
            {
                List clonedResults = new List();

                if (possibleResults.Count == 0)
                {
                    for (int i = 1; i <= sides; i++)
                    {
                        DicePool newPool = new DicePool();
                        newPool.AddDieRoll(new DieRoll(sides, i));
                        clonedResults.Add(newPool);
                    }
                }
                else
                {
                    for (int i = 1; i <= sides; i++)
                    {
                        foreach (DicePool oldPool in possibleResults)
                        {
                            DicePool newPool = oldPool.Clone();
                            newPool.AddDieRoll(new DieRoll(sides, i));
                            clonedResults.Add(newPool);
                        }
                    }
                }

                possibleResults = clonedResults;
            }
            return possibleResults;
        }

        static double ComputeAverageResult(int[] dice)
        {
            List possibleResults = GetResultsTable(dice);

            int possibilities = possibleResults.Count;
            int total = 0;
            foreach (DicePool result in possibleResults)
            {
                total += result.GetResult();
            }

            double average = (double)total / (double)possibilities;

            return average;
        }

        static double ComputeAverageComplications(int[] dice)
        {
            List possibleResults = GetResultsTable(dice);

            int possibilities = possibleResults.Count;
            int total = 0;
            foreach (DicePool result in possibleResults)
            {
                total += result.GetComplications();
            }

            double average = (double)total / (double)possibilities;

            return average;
        }

        static double ComputeChanceOfComplications(int[] dice)
        {
            List possibleResults = GetResultsTable(dice);

            int possibilities = possibleResults.Count;
            int total = 0;
            foreach (DicePool result in possibleResults)
            {
                if (result.GetComplications() > 0)
                {
                    total++;
                }
            }

            double probability = (double)total / (double)possibilities;

            return probability;
        }

        static double ComputeAverageEffectDie(int[] dice)
        {
            List possibleResults = GetResultsTable(dice);

            int possibilities = possibleResults.Count;
            int total = 0;
            foreach (DicePool result in possibleResults)
            {
                total += result.GetEffectDie();
            }

            double average = (double)total / (double)possibilities;

            return average;
        }
    }

    class DieRoll
    {
        public DieRoll(int sides)
        {
            Sides = sides;
            Randomize();
        }

        public DieRoll(int sides, int result)
        {
            Sides = sides;
            Result = result;
        }

        public int Sides { get; set; }
        public int Result { get; set; }

        public void Randomize()
        {
            Result = s_rand.Next(1, Sides + 1);
        }

        private static Random s_rand = new Random();
    }

    class DieRollComparer : IComparer
    {

        public int Compare(DieRoll x, DieRoll y)
        {
            if (x.Result > y.Result)
            {
                return -1;
            }
            else if (x.Result < y.Result)
            {
                return 1;
            }
            else
            {
                if (x.Sides < y.Sides)                 
                {
                     return -1;
                }
                else if (x.Sides > y.Sides)
                {
                    return 1;
                }
                else
                {
                    return 0;
                }
            }
        }
    }

    class DicePool
    {
        public DicePool()
        {
            _dice = new List();
        }

        public void AddDie(int sides)
        {
            DieRoll roll = new DieRoll(sides);
            _dice.Add(roll);
        }

        public void AddDieRoll(DieRoll roll)
        {
            _dice.Add(roll);
        }

        public int GetResult()
        {
            SortDice();
            if (_dice.Count >= 2)
            {
                int result = 0;
                if (_dice[0].Result != 1)
                {
                    result += _dice[0].Result;
                }
                if (_dice[1].Result != 1)
                {
                    result += _dice[1].Result;
                }
                return result;
            }
            else if (_dice.Count == 1)
            {
                if (_dice[0].Result == 1)
                {
                    return 0;
                }
                else
                {
                    return _dice[0].Result;
                }
            }
            else
            {
                return 0;
            }
        }

        public int GetComplications()
        {
            int complications = 0;
            foreach (DieRoll d in _dice)
            {
                if (d.Result == 1)
                {
                    complications++;
                }
            }
            return complications;
        }

        public int GetEffectDie()
        {
            SortDice();
            if (_dice.Count < 3 ||
                _dice[0].Result == 1 ||
                _dice[1].Result == 1 ||
                _dice[2].Result == 1)
            {
                // Default to d4 effect die
                return 4;
            }
            else
            {
                List remainingDice = new List();
                for (int i = 2; i < _dice.Count; i++)
                {
                     if (_dice[i].Result != 1)
                     {
                         remainingDice.Add(_dice[i].Sides);
                     }
                } 

                if (remainingDice.Count >= 2)
                {
                    remainingDice.Sort();
                    remainingDice.Reverse();
                }

                return remainingDice[0];

            }
        }

        public DicePool Clone()
        {
            DicePool clone = new DicePool();
            foreach (DieRoll d in _dice)
            {
                clone._dice.Add(new DieRoll(d.Sides, d.Result));
            }
            return clone;
        }

        private void SortDice()
        {
            _dice.Sort(new DieRollComparer());
        }

        private List _dice;

    }
}
Advertisements

3 thoughts on “Cortex+ Dice Pool Helper

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s