cprobabilitydice

How would adjusting the rules of Risk affect the game?


I'm posting this here to link to from RPG.stackexchange.

I am running a pathfinder game which is going to be have large armies being thrown against each other. One of the suggestions I've seen to speed things up is to use the rules from Risk, the board game.

But the rules for risk assume that units are of equal value. What happens if change the dice from d6's to d8's for one of the armies?

Answers to similar questions, while very well thought out, but do not lend themselves to "what if" questions with minor rules changes. And I doubt the mathematicians would appreciate the pestering.

(Also, as an aside, trying to learn to program in R is kind hard if you don't have a solid grasp on statistics. It's not like knowing the syntax of R tells you how to fit linear models.)

So, stackoverflow, fetch me a Risk (board game) simulator that I may fiddle with the rule-set until I am appeased with it's calculations.


Solution

  • Yes sir, of course sir, right away sir.

    //Experimenting with Risk variant
    //Because figuring out the actual mathmatics behind this is hard.
    
    #include <time.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #define MIN(a,b) (((a)<(b))?(a):(b))
    #define MAX(a,b) (((a)>(b))?(a):(b))
    
    //#define DISTRUSTRAND 1
    //#define VERBOSE 1
    
    
    int g_rollArray[100];
    
    int compare (const void * a, const void * b)
    {
      return ( *(int*)b - *(int*)a );
    }
    
    
    
    int diceRoll(int dieSize)
    {
      int roll = rand()%(dieSize-1);
      g_rollArray[roll]++;
      return roll+1;
    }
    
    
    // MAIN!
    int main( int argc, char* args[] )
    {
      int seed;
      int maxRound=100000;  //Some arbitrarily large number.
      int round=0;
      int i;
    
      memset(g_rollArray,0,sizeof(int)*100);
    
      //Hmmm, there could be a mix of troops, but right now, let's say it's uniform.
      const int numAtt = 3; //How many troops they bring into the fight, that's how many dice they roll
      const int powAtt = 8; //The size of the dice they roll. Like d4, d6, d8.  
      int rollAtt[numAtt];
    
      const int numDef = 2; //How many troops they bring into the fight, that's how many dice they roll
      const int powDef = 6; //The size of the dice they roll. Like d4, d6, d8.  
      int rollDef[numDef];
    
      int lossAtt=0;  //Assuming a big-ass pool of troops behind them. Whoever runs out of a pool first loses.
      int lossDef=0;
    
    
      seed = time(0);
      srand(time(0));
      printf("seed: %d\n",seed);
    
      #ifdef DISTRUSTRAND
      for(i=0; i<10; i++)
      {
        printf("%d: %d\n",i, rollArray[i]);
      }
      #endif
    
      for(round=0; round<maxRound; round++)
      {
        for(i=0; i<numAtt; i++)
        {
          rollAtt[i] = diceRoll(powAtt);
        }
        for(i=0; i<numDef; i++)
        {
          rollDef[i] = diceRoll(powDef);
        }
    
        qsort (rollAtt, numAtt, sizeof(int), compare);
        qsort (rollDef, numDef, sizeof(int), compare);
    
        #ifdef VERBOSE
          printf("sort Att: ");
          for(i=0; i<numAtt; i++)
          {
            printf("%d ",rollAtt[i]);
          }
          printf("\n");
    
          printf("sort Def: ");
          for(i=0; i<numDef; i++)
          {
            printf("%d ",rollDef[i]);
          }
          printf("\n");
        #endif
    
    
        //The MIN here decrees that armies can only lose the forces they commit to a fight
        for(i=0; i<MIN(numDef,numAtt); i++)
        {
          #ifdef VERBOSE
            printf("Comp: %d Vs %d \n",rollAtt[i], rollDef[i]);
          #endif
          //Defenders win ties
          if(rollAtt[i] > rollDef[i])
          {
            lossDef++;
          }
          else
          {
            lossAtt++;
          }
        }
      }
    
    
      printf("Att losses: %d \n",lossAtt);
      printf("Def losses: %d \n",lossDef);
    
      if(lossAtt > lossDef)
      {
        printf("Odds to win: Defender \nKill ratio: %f\n", (float)lossAtt/(float)lossDef);
      }
      else
      {
        printf("Odds to win: Attacker \nKill ratio: %f\n", (float)lossDef/(float)lossAtt);
      }
    
      #ifdef DISTRUSTRAND
      for(i=0; i<10; i++)
      {
        printf("%d: %d\n",i, rollArray[i]);
      }
      #endif
      return 0;
    }  
    
    
    /* meh, unneeded, mingw's rand()%whatnot works well enough.
    int betterRand(int n)
    {
      return rand() / (RAND_MAX / n + 1);
    }
    
    float betterFRand(float n)
    {
      return (float)rand()/((float)RAND_MAX/n);
    }
    */
    

    While the original risk rule-set only gives the attacker an advantage of about 8% which equates to a kill ratio of about 1:1.06, it turns out that if you change die size, the odds shift quite quickly. Giving the attackers d8's, gives them a kill ratio of 1:3. That is, an army that rolls 1-8 has even odds of beating an army that only rolls 1-6 but is 3 times it's size.

    If you keep the die size even between armies, but increase it, the odds shift slightly to the attacker as the impact of the ties lessens

    Increasing the number of die rolls has a more subtle impact that increasing the size of the die roll. Defenders with 3 d6's are slightly better than attackers with 2 d8's.

    So all in all this is a nice starting point for any DMs out there who want to play with the rules of risk and see what the outcome is.

    And hopefully I'll have a better answer with graphs and stuff once I wrap my head around R.