• The VOIDRUNNER'S CODEX is LIVE! Explore new worlds, fight oppressive empires, fend off fearsome aliens, and wield deadly psionics with this comprehensive boxed set expansion for 5E and A5E!

Probability in Char. Gen.

Charwoman Gene

Adventurer
Asmor said:
Heh. Light-hearted ribbing or back-handed compliment? :p :) I got the same result as you running your program on my computer, but I did notice that even after commenting out the extraneous reporting to System.out, it was significantly slower than mine... ;)

Both. My program is full of cruft and is overengineered. I'd rewrite my program, except well, you already wrote the better faster version of mine. I'm a better software engineer than a coder...

Red Shirt will fall into line with the correction of the Chrarisma error, most likely extremely close.

CRG, these leaves your algorithm which I think is calculating a different problem.

All I know is asmor's final version pretty much how my algorithm looks in my head.
 

log in or register to remove this ad

The Souljourner

First Post
Friend of mine has a Master's Degree in math and works at The Mathworks. He whipped the following monte carlo algorithm in MATLAB:

Code:
function [ppc, P, A, S] = points_versus_4d6(n)

P = 0; A = 0;

PV = [(1:14)-8 8 10 13 16];

S = zeros(n, 6);

 

while A < n

    r = ceil(6*rand(6, 4));

    r2 = sum(r, 2)-min(r, [], 2);

    if any(r2>=14) && sum(floor((r2-10)/2)) >= 0

        % Acceptable character

        A = A + 1;

        P = P + sum(PV(r2));

        S(A, :) = r2';

    end

end

ppc = P / A;

The result was a little over 30 points on average.

I don't know matlab, but I can still read the programming fairly well and it looks pretty good. Just thought I'd add another data point.

-The Souljourner
 

RedShirtNo5

First Post
Final Result: 30.4633219726944

Total Characters Accepted: 7790570
Total Characters Rejected: 8986646

Frequency of 3: 1260600
Frequency of 4: 6470160
Frequency of 5: 16175400
Frequency of 6: 42342300
Frequency of 7: 76619400
Frequency of 8: 151608600
Frequency of 9: 222522300
Frequency of 10: 352479960
Frequency of 11: 427598640
Frequency of 12: 556140060
Frequency of 13: 572790960
Frequency of 14: 613664640
Frequency of 15: 502437924
Frequency of 16: 401485656
Frequency of 17: 230640696
Frequency of 18: 98128044


Code:
[size=2]Private Sub Command1_Click()[/size]
 
[size=2]Dim V As Double
Dim P As Double
Dim A As Double
Dim Value As Double
Dim Probability As Double
Dim S As Integer
Dim D As Integer
Dim Co As Integer
Dim I As Integer
Dim W As Integer
Dim Ch As Integer
Dim TotalAccepted As Long
Dim TotalRejected As Long
Dim T(18) As Long
Dim Result As String[/size]
 
[size=2]V = 0#
P = 0#[/size]
 
[size=2]TotalAccepted = 0&
TotalRejected = 0&[/size]
 
[size=2]' We will cycle through each possible stat array that could be created
For S = 3 To 18
For D = 3 To 18
For Co = 3 To 18
For I = 3 To 18
For W = 3 To 18
For Ch = 3 To 18[/size]
 
[size=2]' If the stat array is a "worthless character", then skip it
If S > 13 Or D > 13 Or Co > 13 Or I > 13 Or W > 13 Or Ch > 13 Then
	If Int((S - 10) / 2) + Int((D - 10) / 2) + Int((Co - 10) / 2) + Int((I - 10) / 2) + Int((W - 10) / 2) + Int((Ch - 10) / 2) > 0 Then[/size]
 
[size=2]' If the stat array is not a worthless character, calculate the point buy value (PBV)
' and the probability of that character occurring
	Value = PBV(S) + PBV(D) + PBV(Co) + PBV(I) + PBV(W) + PBV(Ch)
	Probability = prob(S) * prob(D) * prob(Co) * prob(I) * prob(W) * prob(Ch)[/size]
 
[size=2]' Add the weighted point buy value to a running total
	V = V + Value * Probability
 
' Track the total probability (P) of getting a "non-worthless" character for normalization below
	P = P + Probability[/size]
 
[size=2]' Track the total number of accepted characters
	TotalAccepted = TotalAccepted + 1&
 
' Track chances of each number
	T(S) = T(S) + ways(S)
	T(D) = T(D) + ways(D)
	T(Co) = T(Co) + ways(Co)
	T(I) = T(I) + ways(I)
	T(W) = T(W) + ways(W)
	T(Ch) = T(Ch) + ways(Ch)
 
	Else
 
	TotalRejected = TotalRejected + 1&
 
	End If[/size]
 
[size=2]Else[/size]
 
[size=2]	TotalRejected = TotalRejected + 1&
 
End If[/size]
 
[size=2]Next
Next
Next
Next
Next
Next[/size]
 
[size=2]' We know the probability (1-P) of rolling a worthless character, and we calculated the average point buy value (V) that results from a single roll of 4d6dl. Since you keep rolling until you get a non-worthless character, the percentage chance (1-P) of a worthless character gets distributed across the possible non-worthless characters. Thus, we simply need to normalize the result.
A = V / P[/size]
 
[size=2]' Display results
Result = "Final Result:" + Str(A) + Chr(13)
Result = Result + "Total Accepted: " + Str(TotalAccepted) + Chr(13)
Result = Result + "Total Rejected: " + Str(TotalRejected) + Chr(13)
For j = 3 To 18
Result = Result + "Frequency of " + Str(j) + ": " + Str(T(j)) + Chr(13)
Next[/size]
 
[size=2]Text1.Text = Result[/size]
 
[size=2]End Sub
Function PBV(x)
' This assumes that point buy for stats below 8 scales linearly
 
If x = 3 Then PBV = -5#
If x = 4 Then PBV = -4#
If x = 5 Then PBV = -3#
If x = 6 Then PBV = -2#
If x = 7 Then PBV = -1#
If x = 8 Then PBV = 0#
If x = 9 Then PBV = 1#
If x = 10 Then PBV = 2#
If x = 11 Then PBV = 3#
If x = 12 Then PBV = 4#
If x = 13 Then PBV = 5#
If x = 14 Then PBV = 6#
If x = 15 Then PBV = 8#
If x = 16 Then PBV = 10#
If x = 17 Then PBV = 13#
If x = 18 Then PBV = 16#
End Function[/size]
 
[size=2]
Function prob(x)
'These percentages taken from dcollins ([url="http://javascript<b></b><img%20src="http://www.enworld.org/images/smilies/redface.gif"%20border="0"%20alt=""%20title="Embarrassment"%20smilieid="3"%20class="inlineimg"%20/>l('http://superdan.net.home.comcast.net/dndmisc/4d6curve.html');"][color=#0000ff]http://superdan.net.home.comcast.net/dndmisc/4d6curve.html[/color][/url])
If x = 3 Then prob = 1# / 1296#
If x = 4 Then prob = 4# / 1296#
If x = 5 Then prob = 10# / 1296#
If x = 6 Then prob = 21# / 1296#
If x = 7 Then prob = 38# / 1296#
If x = 8 Then prob = 62# / 1296#
If x = 9 Then prob = 91# / 1296#
If x = 10 Then prob = 122# / 1296#
If x = 11 Then prob = 148# / 1296#
If x = 12 Then prob = 167# / 1296#
If x = 13 Then prob = 172# / 1296#
If x = 14 Then prob = 160# / 1296#
If x = 15 Then prob = 131# / 1296#
If x = 16 Then prob = 94# / 1296#
If x = 17 Then prob = 54# / 1296#
If x = 18 Then prob = 21# / 1296#
End Function[/size]
 
[size=2]Function ways(x)
'These numbers taken from dcollins ([url="http://javascript<b></b><img%20src="http://www.enworld.org/images/smilies/redface.gif"%20border="0"%20alt=""%20title="Embarrassment"%20smilieid="3"%20class="inlineimg"%20/>l('http://superdan.net.home.comcast.net/dndmisc/4d6curve.html');"][color=#0000ff]http://superdan.net.home.comcast.net/dndmisc/4d6curve.html[/color][/url])
If x = 3 Then ways = 1&
If x = 4 Then ways = 4&
If x = 5 Then ways = 10&
If x = 6 Then ways = 21&
If x = 7 Then ways = 38&
If x = 8 Then ways = 62&
If x = 9 Then ways = 91&
If x = 10 Then ways = 122&
If x = 11 Then ways = 148&
If x = 12 Then ways = 167&
If x = 13 Then ways = 172&
If x = 14 Then ways = 160&
If x = 15 Then ways = 131&
If x = 16 Then ways = 94&
If x = 17 Then ways = 54&
If x = 18 Then ways = 21&
[/size]
 

CRGreathouse

Community Supporter
Charwoman Gene said:
Red Shirt will fall into line with the correction of the Chrarisma error, most likely extremely close.

CRG, these leaves your algorithm which I think is calculating a different problem.

All I know is asmor's final version pretty much how my algorithm looks in my head.

Oh, there's no doubt in my mind that mine is coming up with the wrong answer; there hasn't been doubt since I posted it, really. The point buy on mine is just too high.

What I'm trying to figure out is if your program is correct. So far the evidence is that a similiarly-written program has the same output, but that's just not that convincing to me. I'm trying to find reason to believe one of these programs is right (and, idealy, rewrite it to use integer math per your request).
 

The Souljourner

First Post
CRGreathouse said:
Oh, there's no doubt in my mind that mine is coming up with the wrong answer; there hasn't been doubt since I posted it, really. The point buy on mine is just too high.

What I'm trying to figure out is if your program is correct. So far the evidence is that a similiarly-written program has the same output, but that's just not that convincing to me. I'm trying to find reason to believe one of these programs is right (and, idealy, rewrite it to use integer math per your request).

I think monte carlo is definitely the way to go here. No need to come up with every single possible character.... just generate 100,000 random characters and average their point buy values. You'll get essentially the same answer, which is what my friend did. I could do the same in C++/C# if people wanted.

-The Souljourner
 

dnabre

First Post
This is an interesting problem.

Firstly, I can confrim dcollin's numbers, not that were in doubt.

When I get to the office (where all my stats books are) I'll take a crack at an analytic solution (time to put that graduate stat course to work for once). If it comes to close to the current numbers, it would be helpful.
 

Asmor

First Post
Well, doing the "monte carlo" approach of just rolling up a huge number of characters (in my case 1,000,000), here's what I got:

30.446762
Total iterations: 1145248
Characters tossed: 145248
Characters kept: 1000000

Here's the code in case anyone's curious, behind a spoiler because there's really nothing new or interesting. If you want to use it to run more tests, just change the variable iterations.

Code:
package com.asmor.dnd;

public class montecarlo {

private static int d6() {
    return (int) Math.floor(Math.random()*6)+1;
}

private static int stat(int r1, int r2, int r3, int r4) {
    int dice[]=new int[4], smallest=0, i, toReturn=0;
    dice[0]=r1;
    dice[1]=r2;
    dice[2]=r3;
    dice[3]=r4;

    for (i=1; i<4; i++) {
        if (dice[i]<dice[smallest]) {
            smallest=i;
        }
    }
    for (i=0; i<4; i++) {
        if (i!=smallest) {
            toReturn+=dice[i];
        }
    }
    return toReturn;
}
    
public static void main(String[] args) {

int totalValue=0, iterations, iterate=0, tossed=0, kept=0, stats[]=new int[7], mods[]={-5, -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 4}, i, toss, totalMod, pbv, cameUp[]=new int[19];
int PBValues[]={0, 0, 0, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16};

//Number of good character to generate:
iterations=1000000;

while (kept<iterations) {

    iterate++;

    for (i=0; i<6; i++) {
        stats[i]=stat(d6(), d6(), d6(), d6());
    }

    toss=1;
    totalMod=0;
    for (i=0; i<6; i++) {
        if (stats[i]>13) {
            toss=0;
        }
        totalMod+=mods[stats[i]];
    }
    
    if (totalMod<=0) {
        toss=1;
    }
    
    if (toss==0) {
        kept++;
        pbv=0;
        for (i=0; i<6; i++) {
            pbv+=PBValues[stats[i]];
        }
        totalValue+=pbv;
    } else if (toss==1) {
        tossed++;
    }

}

System.out.print((double)totalValue/(double)iterations+"\n");
System.out.print("Total iterations: "+iterate+"\nCharacters tossed: "+tossed+"\nCharacters kept: "+kept+"\n");

}
}

Edit: Err... I tried to make that cool spoiler box thingie that hides everything, but instead it made all the text black. Is (spoiler) not the right tag?
 

Cyberzombie

Explorer
Cool. Objective proof that characters at 25 and 28 point buy suck. Neat! I feel even more justified in giving 43 point buy to my players. :)
 

Thanee

First Post
That ~30.5 result also seems right from an intuitive guess. Much more would be just too much, the chance of rolling up a "worthless character" just cannot be that high. ;)

Bye
Thanee
 

two

First Post
Cyberzombie said:
Cool. Objective proof that characters at 25 and 28 point buy suck. Neat! I feel even more justified in giving 43 point buy to my players. :)

Not quite.

You get to put your stats exactly where you want them in a point buy.

With traditional 4d6 drop the lowest, you can't move stats around.

28 point buy (where you move the stats) or 4d6 drop 1 (30 point buy on average) where you don't move the stats.

Seems pretty equal to me.

It's easy to get the 2 point different have no real meaning for the character in question, i.e. a fighter with a 12 instead of a 10 charisma, or 15 vs. 14 wisdom. With point buy, you can often optimize every single point, or at least as close as possible.
 
Last edited:

Voidrunner's Codex

Remove ads

Top