Friday, November 20, 2009

RPG Designers: Learn Your Java

So my first draft of the Tankards & Broadswords RPG is pretty much done. There are a couple of optional rules and a few little tidbits I still want to stick in there, but the core rules are pretty much set and written right now.

One of the rules that I've been mulling over for a while now is the special re-roll you get in certain situations, namely when using two weapons or when using automatic fire. Normally in T&B, you roll 2d6 and add your skill value (or whatever static mod you're using), and compare it to a Break Point. Under re-roll circumstances, you roll your 2d6, but if it's not a double result (2/2, 3/3, etc.), you re-roll the lower die and keep the new roll. So for example, say you roll a 2 and a 5. That's normally a seven. But with the re-roll, you roll the 2 again and keep whatever comes up, even if it's a 1 or a 2 again.

So my big question was...does it actually do anything for you? What I wanted was a mechanic that could give you a better result, and on average will give better results, but does not guarantee a better result.

This is where those ol' Java programming skills come in oh so handy. First, thanks to my good friend and Java guru Masakari for whipping up something on his own that proved my theory correct; on average the most common roll shifted from a 7 to 8, and there is a corresponding shift in the bell curve, with 12's showing up 2-3 times more frequently than 2's (2's averaged between 2.5 and 3%, while 12's were between 7.5 and 8%).

So this morning I whipped up my own little program, and ran some numbers. Here's a good representative spread from a cycle of 10,000 dice rolls:

Twos Rolled: 300.0, or 3.0%
Threes Rolled: 100.0, or 1.0%
Fours Rolled: 560.0, or 5.6%
Fives Rolled: 555.0, or 5.55%
Sixes Rolled: 1197.0, or 11.97%
Sevens Rolled: 1350.0, or 13.5%
Eights Rolled: 1702.0, or 17.02%
Nines Rolled: 1224.0, or 12.24%
Tens Rolled: 1415.0, or 14.15%
Elevens Rolled: 848.0, or 8.48%
Twelves Rolled: 749.0, or 7.49%
While on the other hand, here's a pretty representative spread of 10,000 rolls using a straight 2d6 die-throw:

Twos Rolled: 272.0, or 2.72%
Threes Rolled: 591.0, or 5.91%
Fours Rolled: 836.0, or 8.36%
Fives Rolled: 1072.0, or 10.72%
Sixes Rolled: 1334.0, or 13.34%
Sevens Rolled: 1740.0, or 17.4%
Eights Rolled: 1390.0, or 13.9%
Nines Rolled: 1101.0, or 11.01%
Tens Rolled: 837.0, or 8.37%
Elevens Rolled: 539.0, or 5.39%
Twelves Rolled: 288.0, or 2.88%
Of course, if you're good at math, you could have probably worked this out on your own. However, having a little skill at programming in something fairly simple like Java means you can whip up a little testing program in a few minutes and generate some good data that supports whatever theories you might have about the mechanics of your RPG.

I've also found it useful for feeding in character data and testing various theories about the progression of lethality as you add skill and / or better weapons to a character. For example, I learned that a PC with a Melee of 0 (unskilled), using a Light weapon (+0 damage modifier), rolling against a dead average unskilled defense roll of 7, will take about 10 combat rounds to kill another PC with a health of 24 (which all PCs start out with). On the other hand, a PC with maximum Melee skill plus skill focus bonus, wielding a Heavy weapon, can do the same job in 2-3 combat rounds.

Of course, this is no substitute for actual playtesting; putting the rules in the hands of real players is the only way to make sure your game "works". But being able to try out the mechanics of your rules and make sure that something does what you want it to do is very nice, and can solve a lot of headaches down the road.


Lior said...

Tip: don't simulate what you can calculate exactly.

Note you that you can always roll 3 differently-coloured dice rather than rolling two and then rolling the third. The outcome of the roll is the sum of dies 1,2 (if they are the same) or the sum of 3 and the higher of 1,2 (if 1,2 have different values). This defines a function from the 216 possible rolls of three dice to numbers between 2-12.

Since all 216 possibilities are equally probably what you need to do is, for each number between 2-12 count how many of the 216 possibilities add up to that number. For bonus you can also count the number of times the sum of the first two dice adds up to that number, to get the usual distribution.

This is easy to write and will give you exact probabilities. All you need is:

int main()
int i,j,k,counts[13];
for (i=2; i<=12; i++) counts[i] = 0;

for (i=1; i<=6; i++)
for (j=1; j<=6; j++)
for (k=1; k<=6; k++)
if (i=j) (counts[i+j])++;
else if (i>j) (counts[i+k])++;
else counts(counts[j+k]++);

for (i=2; i<=12; i++)
/* print counts[i]/216; */
return 0;

Ragnorakk said...

yeah - my practical math skills are none so good, so C and Perl have come in handy
in this capacity before.

Badelaire said...


I still like running simulated tests. Mostly because for something like this, I can scale how many iterations it runs and see its affect over a short number of dice throws vs. a long number of throws. Especially because this isn't the sort of rule that always benefits you, it'll allow me to see the relative effect over 10 throws, 20 throws, etc., vs a more exact 10,000 throws.

With a little tidying, however, I ran your code (with Masakari's help - thanks dude) and generated the following:

2: 2.7777777777777777
3: 0.9259259259259258
4: 5.555555555555555
5: 5.555555555555555
6: 12.037037037037036
7: 13.88888888888889
8: 16.666666666666664
9: 12.962962962962962
10: 13.88888888888889
11: 8.333333333333332
12: 7.4074074074074066

Good enough for what I'm trying to accomplish.

Thanks again!

Masakari said...

It strikes me that Lior's way of doing things offers a good way to test the relative accuracy of whatever dice-simulation program you're using (just compare the simulated results to the exactly-calculated "ground truth").

Lior said...

You can also explicitly calculate the expect deviation between the empirical distribution after N rolls and the true probabilities. If you're interested I can add a comment about how to do this.

Lior said...

PS: the code up there has a typo: in the case j>i there is a spurious "counts" -- the code is simply supposed to increment counts[j+k].

misterecho said...

Wow, some guys who really enjoy the math part of RPGs. :)

Badelaire said...

More like, guys who like the "rules working the way they want them to" part of their RPGs...

I'm no super math-loving nerd and I also don't go hog-wild with the number crunching. But I do think a game designer has an obligation to make sure that a rule mechanic does actually do, mathematically, what's being supposedly accomplished.

So no offense taken, intended or not, but I'm not just doin' this cuz I like math...

modred11 said...

I prefer spreadsheets and C++ / Python / PHP.

Anything but JAVA!! :)

[I've just never used it before]