Skip to content

daveisagit/dice_bingo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dice Bingo

A Mathsjam puzzle posed in May 22

png

This a jupyter notebook to better understand what is going on

# all the possible rolls
possible_values = [2, 3, 4, 5, 6]

# the weighted distribution of them
distribution = [1 , 4 , 10 , 12 , 9 ]
probabilities = [x/sum(distribution) for x in distribution]

# Keep a history of what we actually rolled
actual_distribution = [0] * 5

# For each partition we want to assign the best probable choice
priority = [5,4,6,3,2]

# ignore results over this many rolls
max_rolls = 35 

# number of cards to do for each layout
sample_size=100000

# number of competition rounds
competition_rounds = 100000
import collections
import numpy as np
import matplotlib.pyplot as plt

card_layouts = []

# it seems to perform better if the rolls are pre-determined
number_of_rolls_up_front = 1000000
rolls_up_front = np.random.choice(possible_values, number_of_rolls_up_front, p=probabilities)
roll_index = 0

def partition(number):
    """ Return all the partitions of 5 """
    answer = set()
    answer.add((number, ))
    for x in range(1, number):
        for y in partition(number - x):
            answer.add(tuple(sorted((x, ) + y, reverse=True)))
    return answer

def roll_the_dice():
    """ Return a dice roll and record what is was """
    global roll_index, rolls_up_front
    
    # we might need more along the way
    if roll_index >= number_of_rolls_up_front:
        roll_index = 0
        rolls_up_front = np.random.choice(possible_values, number_of_rolls_up_front, p=probabilities)
        
    r = rolls_up_front[roll_index]
    roll_index += 1
    actual_distribution[r-2] += 1
    return r
    

class CardLayout:
    def __init__(self, layout):
        self.layout = layout
        self.test_results = None
        self.add_test_results()
        
    def add_test_results(self):

        results = [0] * (max_rolls + 1)
        
        # run it for a given number of solo games
        for i in range(sample_size):
            
            # initialise the card from the template layout (one of the 7)
            # and roll count
            card = self.layout.copy()
            roll_count = 0

            # Bingo when our card is all zero entries
            # or quit if over the roll limit
            while roll_count < max_rolls and sum(card) > 0:

                # roll the dice
                rolled = roll_the_dice()
                roll_count += 1

                # mark the card
                if card[rolled - 2] > 0:
                    card[rolled - 2] -= 1

            # Bingo ?
            if sum(card) == 0:
                results[roll_count] += 1

        # only want from 5 to max_rolls
        results = results[5:max_rolls+1]
        
        # convert to %
        results = [r*100/sample_size for r in results]
        
        self.test_results = results
def setup():
    """ Create all 7 card layouts from the partitions of 5 and set the starting amounts based on 
    the priority of the distribution. i.e. largest partition gets the best probability
    """
    card_layouts.clear()
    for layout in partition(5):
        card_layout = [0] * 5
        for i,n in enumerate(layout):
            p = priority[i]
            card_layout[p-2] = n
        card_layouts.append(CardLayout(card_layout))
        
def run_competition(rounds=1000):
    """ Simulate a competition between all 7 layouts
    """
    # initialise the roll distribution
    dice_rolls = np.random.choice(possible_values, number_of_rolls_up_front, p=probabilities)
    
    # store how many wins for each card layout
    results = [0] * len(card_layouts)
    p = -1
    
    for _ in range(rounds-1):
        cards = [card.layout.copy() for card in card_layouts]
        bingo = False
        while not bingo:
            
            # get the next dice roll
            if p >= number_of_rolls_up_front:
                p = -1
                dice_rolls = np.random.choice(possible_values, number_of_rolls_up_front, p=probabilities)
                
            p += 1
            rolled = dice_rolls[p]
            
            # mark all the cards
            for i in range(len(cards)):
                card = cards[i]
                if card[rolled - 2] > 0:
                    card[rolled - 2] -= 1
                cards[i] = card
                
                # stop if a card has won
                if sum(card) == 0:
                    bingo = True
        
        # possibly more than 1 winner ?
        for i, card in enumerate(cards):
            if sum(card) == 0:
                results[i] += 1
    return results
            
            
        
setup()

expected_distribution_pct = [x*100/sum(probabilities) for x in probabilities]
actual_distribution_pct = [x*100/sum(actual_distribution) for x in actual_distribution]
actual_distribution_36 = [x*36/sum(actual_distribution) for x in actual_distribution]
%matplotlib inline

print("Expected distribution", expected_distribution_pct)
print("Actual distribution", actual_distribution_pct)

x = np.arange(len(possible_values))  # the label locations
width = 0.35  # the width of the bars

# Report the distrubition of rolls

plt.figure(figsize=(10, 4))
b1 = plt.bar(x - width/2, distribution, width, label='Expected')
b2 = plt.bar(x + width/2, actual_distribution_36, width, label='Actual')
plt.bar_label(b1, padding=6)
plt.bar_label(b2, padding=6)

plt.title(f"Roll distribution (total rolls={sum(actual_distribution):,})")
plt.xlabel("Roll")
plt.xticks(x, possible_values)
plt.ylabel(f"Out of 36")
plt.legend()
plt.margins(y=0.2)
plt.show()
Expected distribution [2.7777777777777777, 11.11111111111111, 27.77777777777778, 33.33333333333333, 25.0]
Actual distribution [2.777303192548298, 11.103493169659362, 27.806812328635818, 33.32178177669963, 24.99060953245689]

png

labels = list(range(5, max_rolls+1))
x = np.arange(len(labels))  # the label locations
line_colours = []

plt.figure(figsize=(20, 8))
for i,card in enumerate(card_layouts):
    p = plt.plot(labels,card.test_results, label = card.layout, linewidth=5)
    line_colours.append(p[-1].get_color())
    
plt.legend(loc=1, prop={'size': 20})
plt.grid()
plt.title(f"Sample size = {sample_size:,} (i.e. each layout tested this number of times)")
plt.xticks(labels)
plt.xlabel("Number of rolls taken")
plt.ylabel(f"% of the sample ({sample_size:,})")
plt.show()

png

%matplotlib inline
competition_results = run_competition(competition_rounds)
competition_results_pct = [x*100/sum(competition_results) for x in competition_results]
plt.figure(figsize=(20, 4))
b=plt.bar([str(c.layout) for c in card_layouts],competition_results_pct, color=line_colours)
plt.title("All vs All Winners")
plt.xlabel("Card Layout")
plt.ylabel(f"% of the rounds won ({competition_rounds:,})")
plt.grid(axis='y')
plt.bar_label(b, padding=6)
plt.margins(y=0.2)
plt.show()

png

About

Jupyter notebook for Dice Bingo

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published