D&D Ability Scores: How exceptional is your character?

D&D (and many other RPGs) have ability scores. You are supposed to roll 3d6 for each of them, but most often players want better scores and DMs allow methods that gives better scores.

Each ability has a value in the range 3-18 and it gives a bonus in the range -4 to +4 (for Classic D&D: -3 to +3).

Lets forget about child mortality and assume 3d6 is for those who reach adulthood (without racial ability modifiers). This would give that the average citizen of a fantasy world has a total ability value of 63 (6 x 10.5) and a bonus of 0. It makes sense that the player characters – adventurers – are better than average. Not everyone becomes an adventurer.

If we imagine our D&D town or village not to be so very different from how children grow up today, I think this is a reasonable way to think of it (approximately)

  • 1/4: most talented among siblings
  • 1/10: most talented kid on the street
  • 1/30: most talented in class
  • 1/200: most talented in school
  • 1/1000: most talented person in a village
  • 1/10000: most talented person in town
  • … and beyond that, exceptional and rare talent

How does this match ability scores and bonuses?

Ability Score

6 abilitiesBetter than
6347%
6453%
6558%
6663%
6768%
6873%Most talented sibling
6977%
7081%
7185%
7288%
73Best of 10Most talented kid on street
74Best of 14
75Best of 18
76Best of 24
77Best of 32
78Best of 45
79Best of 64
80Best of 92
81Best of 136
82Best of 205Most talented kid in school
83Best of 316
84Best of 499
85Best of 808
86Best of 1341Most talented person in village
87Best of 2287
88Best of 4009
89Best of 7237
90Best of 13472Most talented person in town

So, a character with an average score of 15 would have a total of 90. On one hand that is a person who would probably have received some attention for her talents when growing up. On the other hand, it is far from unrealistic that such a person exist.

Bonuses

So what about total bonuses?

Total Bonus
6 Abilities
D&D 5e
Better than
Best of
D&D Classic
Better than
Best of
045%41%
+155%59%
+266%75%
+375%87%
+483%/17
+589%/45
+6/15/135
+7/26/473
+8/50/1 924
+9/103/9 075
+10/229/49 840
+11/554/320 497
+12/1 476/2 435 080
+13/4 344/22 166 682
+14/14 251/246 959 710
+15/52 658/3 481 658 707
+16/221 926/65 692 122 012
+17/1 083 583/1 846 464 029 852
+18/6 251 574/101 983 687 214 006

What I find interesting with this is that it can somehow be connected to the background story of the character. If the character is just an unknown nobody who left her village for adventuring a total bonus of +6 (or +4 for Classic) is on the high side (a best-in-class-character would probably have recieved som attention). If, on the other hand, it is understood that the character is somehow distinguished, priviliged or meant for great deeds, it is not at all unreasonable with a total bonus of +12 (+8 in classic).

Even higher bonuses than that are very possible as long as it is understood that the characters are extraordinarily talented individuals in their society.

Other Rolling Methods

What if the DM allows other methods of rolling (Classic in parenthesis)?

~1/10 bad rollAverage roll~1/10 good roll
3d6-5 (-2)+0 (0)+5 (+2)Average person
2d6+6+4 (+2)+8 (+5)+12 (+8)Best in large class (~50)
4d6 drop worst+1 (+1)+6 (+4)+10 (+7)Best kid on street (~15)
5d6 drop worst 2+5 (+3)+9 (+6)+13 (+8)Best in small school (~100)
5e: [15, 14, 13, 12, 10, 8]+5 (+2)Best sibling
5e: My House Rule+6Best kid on street (~15)

What this table says, roughly, is that if the player rolls once using this method it is likely she will get the average outcome. Only about 1/10 she will get the bad result from the bad column, or the good result from the good column. It is not at all likely that any of your players will roll a +14 (+9) character (which would correspond to best one of 10 000, or most talented in town) with any of the methods.

Conclusion

From this little experiment, two things came to me.

First, when it comes to NPC, villains and heroes (Conan and Robin Hood if you like) sometimes the stats of such figures are published and they can seem ridiculously good. But that is quite fine. They may be one-in-a-million type of humans who really made a footprint in their time.

Second, most established methods for generating abilities will produce characters that are the most talented among their siblings or perhaps the most talented in their school. For a player to roll anything significantly better there are some ways:

  • Very much luck
  • Rolling very many times
  • Cheating
  • Inventing a very powerful method

I think, more than just focusing on the rolling and the methods, it makes sense to talk to they players and ask: who do you reasonably think your character is. If they are the son of Conan, or the only one girl selected by the archmage in the country to study magic because of her extraordinary talents, then very high ability scores are perfectly reasonable.

Source Code

The source code for this is written in JavaScript and runs with Node.js on your computer. It is not quite written to be published, but here it is anyway.

Parameters are hard coded in the top of main().

const objcopy = (obj) => {
  return JSON.parse(JSON.stringify(obj));
};

const num2prop = (n) => {
  if ( n < 0 ) return '' + n;
  if ( 0 < n ) return '+' + n;
  return ' 0';
};

const numcmp = (a,b) => {
  return +a - +b;
};

const abilities = () => {
  const ret = {};
  let i;
  for ( i=3 ; i<=18 ; i++ ) ret[i] = 0;
  return ret;
};

const dice3d6 = () => {
  const ret = abilities();
  let i, j, k;
  for ( i=1;i<=6;i++ ) for ( j=1;j<=6;j++ ) for ( k=1;k<=6;k++ ) ret[i+j+k]++;
  return ret;
};

const dice2d6p6 = () => {
  const ret = abilities();
  let i, j;
  for ( i=1;i<=6;i++ ) for ( j=1;j<=6;j++ ) ret[i+j+6]++;
  return ret;
};

const dice4d6drop = () => {
  const ret = abilities();
  let i, j, k, l;
  for ( i=1;i<=6;i++ )
    for ( j=1;j<=6;j++ )
      for ( k=1;k<=6;k++ )
        for ( l=1;l<=6;l++ ) {
          const a = [i,j,k,l];
          a.sort(numcmp);
          ret[a[1]+a[2]+a[3]]++;
        }
  return ret;
};

const dice5d6drop = () => {
  const ret = abilities();
  let i, j, k, l, m;
  for ( i=1;i<=6;i++ )
    for ( j=1;j<=6;j++ )
      for ( k=1;k<=6;k++ )
        for ( l=1;l<=6;l++ )
          for ( m=1;m<=6;m++ ) {
            const a = [i,j,k,l,m];
            a.sort(numcmp);
            ret[a[2]+a[3]+a[4]]++;
          }
  return ret;
};

const multiplestats = (dist,n) => {
  let ret = objcopy(dist);
  let i;
  for ( i=1 ; i<n ; i++ ) {
    ret = multiplestats_2(ret,dist);
  }
  return ret;
};

const multiplestats_2 = (total,one) => {
  let ret = {};
  let t, o, r;
  for ( t in total ) for ( o in one ) {
    r = num2prop(+t + +o);
    if ( !ret[r] ) ret[r] = 0;
    ret[r] += (total[t] * one[o]);
  }
  return ret;
};

const bonuses = {
  'Classic' : () => {
    return {
      '-3' : 0,
      '-2' : 0,
      '-1' : 0,
      ' 0' : 0,
      '+1' : 0,
      '+2' : 0,
      '+3' : 0
    };
  },
  '5e' : () => {
    return {
      '-4' : 0,
      '-3' : 0,
      '-2' : 0,
      '-1' : 0,
      ' 0' : 0,
      '+1' : 0,
      '+2' : 0,
      '+3' : 0,
      '+4' : 0
    };
  }
};

const ability2bonus = {
  'Classic' : (a) => {
    switch (a) {
    case 3: return '-3';
    case 4: case 5: return '-2';
    case 6: case 7: case 8: return '-1';
    case 9: case 10: case 11: case 12: return ' 0';
    case 13: case 14: case 15: return '+1';
    case 16: case 17: return '+2';
    case 18: return '+3';
    };
  },
  '5e' : (a) => {
    switch (a) {
    case 3: return '-4';
    case 4: case 5: return '-3';
    case 6: case 7: return '-2';
    case 8: case 9: return '-1';
    case 10: case 11: return ' 0';
    case 12: case 13: return '+1';
    case 14: case 15: return '+2';
    case 16: case 17: return '+3';
    case 18: return '+4';
    };
  }
};

const dist_ability2bonus = (distDice,version) => {
  const distBonus = bonuses[version]();
  const getBonus = ability2bonus[version];
  let a;
  for ( a=3 ; a<=18 ; a++ ) distBonus[getBonus(a)] += distDice[a];
  return distBonus;
};

const toPercent = (dist) => {
  const ret = {};
  const keys = Object.keys(dist).sort(numcmp);
  let total = 0;
  let i,k;
  for ( i=0 ; i<keys.length ; i++ ) {
    k = keys[i];
    total += dist[k];
  };
  total = 100 / total;
  for ( i=0 ; i<keys.length ; i++ ) {
    k = keys[i];
    ret[k] = dist[k] * total;
  };
  return ret;
};

const accumulate = (dist) => {
  const ret = {};
  const keys = Object.keys(dist).sort(numcmp);
  let acc = 0;
  let i,k;
  for ( i=0 ; i<keys.length ; i++ ) {
    k = keys[i];
    ret[k] = acc;
    acc += dist[k];
  }
  return ret;
};

const rounddecimals = (dist) => {
  let k,v;
  for ( k in dist ) {
  v = dist[k];
  if ( !Number.isInteger(v) ) {
    if ( v < 10 )
      dist[k] = '1/' + (100/v).toFixed(0);
    else if ( 90 < v )
      dist[k] = '1/' + (100/(100-v)).toFixed(0);
    else
      dist[k] = v.toFixed(1);
    }
  }
};

const main = () => {
//const rolldist = dice3d6();
//const rolldist = dice2d6p6();
//const rolldist = dice4d6drop();
  const rolldist = dice5d6drop();
//const bonus = '5e';
  const bonus = 'Classic';
  const rollcount = 6;
  const percent = true;
  const accumulated = true;
  const round = true;
  const printall = true;
  let workdata = [{ dist:rolldist , lbl:'Roll Dist'}];
  const last = () => { return workdata[workdata.length-1].dist; };
  if ( bonus ) workdata.push({
    dist : dist_ability2bonus(last(),bonus),
     lbl : 'Bonus: ' + bonus
  });
  if ( 1 < rollcount ) workdata.push({
    dist : multiplestats(last(),rollcount),
     lbl : '' + rollcount + ' stats'
  });
  if ( percent ) workdata.push({
    dist : toPercent(last()),
     lbl : 'Percent'
  });
  if ( accumulated ) workdata.push({
    dist : accumulate(last()),
     lbl : 'Accumulated'
  });
  if ( round ) workdata.forEach((w) => { rounddecimals(w.dist) });
  if ( printall )
    console.log(JSON.stringify(workdata,null,4));
  else
    console.log(JSON.stringify(last,null,4));
};

main();

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Time limit is exhausted. Please reload CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.