Sunday, October 9, 2022

[FIXED] How to get sum of probabilities of rolling w dice where one is fair and the other one is unfair?

Issue

I am writing a little program and wanted to ask how I can add the logic of having an unfair dice in the game? Right now, my code produces the sum of probabilities of rolling 2 dices with 6 faces for i times. However, it is treating the dices with a 1/6 probability of rolling a given number. How do I tweak it, so that the unfair dice ONLY shows up in the range of 2-5 but never as 1 or 6? The output should the sum of probs for all numbers in range 2-12 given the fair and unfair dice.

import random
from collections import defaultdict

def main():
    dice = 2
    sides = 6
    rolls = int(input("Enter the number of rolls to simulate: "))
    result = roll(dice, sides, rolls)
    maxH = 0
    for i in range(dice, dice * sides + 1):
        if result[i] / rolls > maxH: maxH = result[i] / rolls
    for i in range(dice, dice * sides + 1):
        print('{:2d}{:10d}{:8.2%} {}'.format(i, result[i], result[i] / rolls, '#' * int(result[i] / rolls / maxH * 40)))


def roll(dice, sides, rolls):
    d = defaultdict(int)
    for _ in range(rolls):
        d[sum(random.randint(1, sides) for _ in range(dice))] += 1
    return d

main()

Output

Enter the number of rolls to simulate: 10000
 2       265   2.65% ######
 3       567   5.67% #############
 4       846   8.46% ####################
 5      1166  11.66% ############################
 6      1346  13.46% ################################
 7      1635  16.35% ########################################
 8      1397  13.97% ##################################
 9      1130  11.30% ###########################
10       849   8.49% ####################
11       520   5.20% ############
12       279   2.79% ######

Solution

Given that the logic of which results are possible is currently being controlled by the line

random.randint(1, sides)

that's the line to change if you want to roll with different bounds. For example, to get 2-5, you could generalize the function:

def main():
    dice = 2
    sides = 6
    unfair_min = 2
    unfair_max = 5
    rolls = int(input("Enter the number of rolls to simulate: "))
    result_unfair = roll_biased(dice, sides, rolls, min_roll=unfair_min, max_roll=unfair_max)
    maxH = max(result_unfair.values()) / rolls

    for i in range(dice, dice * sides + 1):
        print('{:2d}{:10d}{:8.2%} {}'.format(i, result_unfair[i], result_unfair[i] / rolls,
                                             '#' * int(result_unfair[i] / rolls / maxH * 40)))


def roll_biased(dice, sides, rolls, min_roll=1, max_roll=None):
    if max_roll is None:
        max_roll = sides
    d = defaultdict(int)
    for _ in range(rolls):
        d[sum(random.randint(min_roll, max_roll) for _ in range(dice))] += 1
    return d

Which could print:

Enter the number of rolls to simulate: 10000
 2         0   0.00% 
 3         0   0.00% 
 4       632   6.32% ##########
 5      1231  12.31% ###################
 6      1851  18.51% #############################
 7      2480  24.80% ########################################
 8      1873  18.73% ##############################
 9      1296  12.96% ####################
10       637   6.37% ##########
11         0   0.00% 
12         0   0.00% 

You could also generalize this to arbitrary choices (or arbitrary weights) using random.choices() as such:

def roll_from_choices(dice, sides, rolls, allowed_rolls=None):
    if allowed_rolls is None:
        allowed_rolls = list(range(1, sides+1))
    d = defaultdict(int)

    for _ in range(rolls):
        d[sum(random.choices(allowed_rolls, k=dice))] += 1
    return d

which you can call as:

result_unfair = roll_from_choices(dice, sides, rolls, allowed_rolls=[2, 3, 4, 5])


Answered By - kcsquared
Answer Checked By - Gilberto Lyons (PHPFixing Admin)

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.