admin – The Postulate https://thepostulate.com Privatio boni Tue, 09 Feb 2021 03:55:18 +0000 en-US hourly 1 https://wordpress.org/?v=7.0 https://thepostulate.com/wp-content/uploads/2021/01/Favicon-Transparent-150x150.png admin – The Postulate https://thepostulate.com 32 32 Keeping Score: The Beginnings of a Yahtzee Scorecard https://thepostulate.com/keeping-score-the-beginnings-of-a-yahtzee-scorecard/?utm_source=rss&utm_medium=rss&utm_campaign=keeping-score-the-beginnings-of-a-yahtzee-scorecard https://thepostulate.com/keeping-score-the-beginnings-of-a-yahtzee-scorecard/#respond Tue, 09 Feb 2021 03:55:01 +0000 https://thepostulate.com/?p=228 Keeping Score: The Beginnings of a Yahtzee Scorecard Read More »

]]>
Now that I’ve got a YahtzeeHand class that is able to detect all of the common Yahtzee hands, I want to develop a scorecard that will allow me to keep track of each score in the game. The tricky part about Yahtzee is that once you “score” a hand, you cannot score another hand of that type for the rest of the game. For example, once you use the Full House, if you roll another Full House, you have to either re-roll the whole thing, or count a triple or double of some type.

Because of this, I think I’ll build a smart-ish Scorecard class that can keep track of each type of hand and also suggest the “open” items available to still be scored. I’ll start off with making a dictionary of “scores” and a simple method to evaluate a hand and apply the score to this dictionary. We’ll also use a cool feature of Python, specifically the ability to store an actual function as the value in a dictionary item to calculate the scores. Here is what it looks like:

from core.hand import YahtzeeHand

class Scorecard:

    def __init__(self):
        self.scores = {
            "1": None,
            "2": None,
            "3": None,
            "4": None,
            "5": None,
            "6": None,
            "3Kind": None,
            "4Kind": None,
            "FullHouse": None,
            "SmallStraight": None,
            "LargeStraight": None,
            "Yahtzee": None,
            "Chance": None
        }
        self.cur_score = 0

I’ll also create a class variable as a dictionary of functions that can be used to evaluate the score of each type of hand. This is what it looks like:

class Scorecard:
...

    SCORE_METHODS = {
        "1": (lambda hand: hand.get_counts()[0] * 1),
        "2": (lambda hand: hand.get_counts()[1] * 2),
        "3": (lambda hand: hand.get_counts()[2] * 3),
        "4": (lambda hand: hand.get_counts()[3] * 4),
        "5": (lambda hand: hand.get_counts()[4] * 5),
        "6": (lambda hand: hand.get_counts()[5] * 6),
        "3Kind": (lambda hand: hand.get_hand_total_score() if hand.is_triple() else 0),
        "4Kind": (lambda hand: hand.get_hand_total_score() if hand.is_four_of_a_kind() else 0),
        "FullHouse": (lambda hand: 25 if hand.is_full_house() else 0),
        "SmallStraight": (lambda hand: 30 if hand.is_small_straight() else 0),
        "LargeStraight": (lambda hand: 40 if hand.is_large_straight() else 0),
        "Yahtzee": (lambda hand: 50 if hand.is_yahtzee() else 0),
        "Chance": (lambda hand: hand.get_hand_total_score()),
    }

Setting up the scoring like this allows me to take a YahtzeeHand as input and in just a couple of lines of code, output the value of that hand for every item on the scorecard:

    def all_possible_scores(self, hand: YahtzeeHand) -> dict:
        retval = {}
        for (hand_type, score_method) in Scorecard.SCORE_METHODS.items():
            retval[hand_type] = score_method(hand)
        return retval

Using the following code, we get this output which looks like everything is working well:

if __name__ == "__main__":
    hands = [YahtzeeHand() for i in range(10)]
    sc = Scorecard()
    for h in hands:
        print(f"Hand: {h.get()}\nScores: {sc.all_possible_scores(h)}")
Hand: (5, 6, 6, 2, 3)
Scores: {'1': 0, '2': 2, '3': 3, '4': 0, '5': 5, '6': 12, '3Kind': 0, '4Kind': 0, 'FullHouse': 0, 'SmallStraight': 0, 'LargeStraight': 0, 'Yahtzee': 0, 'Chance': 22}
Hand: (4, 5, 3, 4, 5)
Scores: {'1': 0, '2': 0, '3': 3, '4': 8, '5': 10, '6': 0, '3Kind': 0, '4Kind': 0, 'FullHouse': 0, 'SmallStraight': 0, 'LargeStraight': 0, 'Yahtzee': 0, 'Chance': 21}
Hand: (4, 6, 1, 5, 3)
Scores: {'1': 1, '2': 0, '3': 3, '4': 4, '5': 5, '6': 6, '3Kind': 0, '4Kind': 0, 'FullHouse': 0, 'SmallStraight': 30, 'LargeStraight': 0, 'Yahtzee': 0, 'Chance': 19}
Hand: (2, 2, 3, 2, 6)
Scores: {'1': 0, '2': 6, '3': 3, '4': 0, '5': 0, '6': 6, '3Kind': 15, '4Kind': 0, 'FullHouse': 0, 'SmallStraight': 0, 'LargeStraight': 0, 'Yahtzee': 0, 'Chance': 15}
Hand: (1, 6, 5, 4, 2)
...

It’s clear that things are working relatively well — the class appears to be detecting each type of hand, and also scoring the chance correctly. This is good enough for me to move onto the next step — applying the score to the scorecard. Once we finish that, we can start to build an actual Yahtzee game!!

]]>
https://thepostulate.com/keeping-score-the-beginnings-of-a-yahtzee-scorecard/feed/ 0
Yahtzee: The Last Brick in the Foundation https://thepostulate.com/yahtzee-the-last-brick-in-the-foundation/?utm_source=rss&utm_medium=rss&utm_campaign=yahtzee-the-last-brick-in-the-foundation https://thepostulate.com/yahtzee-the-last-brick-in-the-foundation/#respond Mon, 08 Feb 2021 05:27:57 +0000 https://thepostulate.com/?p=223 Yahtzee: The Last Brick in the Foundation Read More »

]]>
In the previous section, I talked about how our strong foundation was letting me write code very easily as I expanded my implementation. I need one last piece of foundational code in my hand class. For the Three of a Kind, Four of a Kind, and Chance rolls, the score for the rolls is the total of all dice in the hand. It seems like if I implement one more simple method to count the total of the individual dice in the hand, I’ll have a very solid foundation. Because of the way we have modeled the SimpleHand, this is a very simple method to write also (especially if we remember that our actual hand is represented as a list of the individual dice in the hand:

class YahtzeeHand(SimpleHand):
...
    def get_hand_total_score(self):
        return sum(self.hand)

I’ve also updated my unit tests for the YahtzeeHand class as follows:

class TestYahtzeeHand(TestCase):
    def test_large_straight(self):
        h = YahtzeeHand(seed=TEST_SEED)
        h._force_new([2, 1, 5, 4, 3])
        self.assertTrue(h.is_large_straight())
        h._force_new([2, 1, 6, 4, 3])
        self.assertFalse(h.is_large_straight())

    def test_small_straight(self):
        h = YahtzeeHand(seed=TEST_SEED)
        h._force_new([2, 1, 5, 4, 3])
        self.assertTrue(h.is_small_straight())
        h._force_new([2, 1, 6, 4, 3])
        self.assertTrue(h.is_small_straight())
        h._force_new([1, 2, 3, 5, 6])
        self.assertFalse(h.is_small_straight())
        h._force_new([1, 2, 3, 3, 4])
        self.assertTrue(h.is_small_straight())

    def test_full_house(self):
        h = YahtzeeHand(seed=TEST_SEED)
        h._force_new([1, 1, 5, 5, 5])
        self.assertTrue(h.is_full_house())
        h._force_new([2, 3, 2, 3, 2])
        self.assertTrue(h.is_full_house())
        h._force_new([1, 2, 3, 5, 6])
        self.assertFalse(h.is_full_house())
        h._force_new([1, 1, 3, 3, 4])
        self.assertFalse(h.is_full_house())

    def test_is_four_of_a_kind(self):
        h = YahtzeeHand(seed=TEST_SEED)
        h._force_new([1, 5, 5, 5, 5])
        self.assertTrue(h.is_four_of_a_kind())
        h._force_new([1, 1, 5, 5, 5])
        self.assertFalse(h.is_four_of_a_kind())
        h._force_new([5, 1, 5, 5, 5])
        self.assertTrue(h.is_four_of_a_kind())

    def test_is_triple(self):
        h = YahtzeeHand(seed=TEST_SEED)
        h._force_new([1, 5, 5, 5, 5])
        self.assertTrue(h.is_triple())
        h._force_new([5, 1, 1, 5, 5])
        self.assertTrue(h.is_triple())
        h._force_new([5, 1, 5, 2, 3])
        self.assertFalse(h.is_triple())

    def test_is_yahtzee(self):
        h = YahtzeeHand(seed=TEST_SEED)
        h._force_new([5, 5, 5, 5, 5])
        self.assertTrue(h.is_yahtzee())
        h._force_new([5, 1, 5, 5, 5])
        self.assertFalse(h.is_yahtzee())
        h._force_new([5, 1, 5, 2, 3])
        self.assertFalse(h.is_yahtzee())

    def test_get_score_for_number(self):
        h = YahtzeeHand(seed=TEST_SEED)
        h._force_new([1, 1, 2, 3, 2])
        self.assertEqual(2, h.get_score_for_number(1))
        self.assertEqual(0, h.get_score_for_number(5))
        self.assertEqual(4, h.get_score_for_number(2))

    def test_get_hand_total_score(self):
        h = YahtzeeHand(seed=TEST_SEED)
        h._force_new([1, 1, 2, 3, 2])
        self.assertEqual(9, h.get_hand_total_score())

Now I think I’ve got what I need to put together a simple scoring mechanism… and that will get me close to the point where I can actually write a game!

]]>
https://thepostulate.com/yahtzee-the-last-brick-in-the-foundation/feed/ 0
Yahtzee in Python: Counting Individual Numbers https://thepostulate.com/yahtzee-in-python-counting-individual-numbers/?utm_source=rss&utm_medium=rss&utm_campaign=yahtzee-in-python-counting-individual-numbers https://thepostulate.com/yahtzee-in-python-counting-individual-numbers/#respond Sat, 06 Feb 2021 02:28:40 +0000 https://thepostulate.com/?p=221 Yahtzee in Python: Counting Individual Numbers Read More »

]]>
Now that I’ve got the “special” hands done, the other part I will need to support is the ability to score the “top of the card.” This means that I will need to be able to count (and score) ones, twos, threes, fours, fives, and sixes. The get_counts() and count_of() methods that we build in the previous post to help us with finding full houses will come in handy here. As a reminder, the methods in question look like this:

    def get_counts(self) -> tuple:
        """
        Get the count of each number appearing in the hand.
        e.g. - a hand of [3, 3, 4, 4, 1] will return (1, 0, 2, 2, 0, 0)
        :return: A tuple of integers with the value at each index indicating the number of occurences of number n-1 in
        the hand.
        """
        retval = tuple(self.hand.count(i) for i in range(1, self.num_sides+1))
        return retval

    def count_of(self, num: int) -> int:
        return self.hand.count(num)

Using these two methods, I’ve got two ways to figure out the scores for the individual numbers. Simply, the score for an individual number is:

    def get_score_for_number(self, number):
        return self.count_of(number) * number

Hopefully it’s becoming apparent that taking the time to build some of the methods in the SimpleHand class is starting to pay off! The next step for me is to write one more helper method, update my unit tests and then put all of this together and use it to build a scorecard. Once I’m there, I’ll be able to simulate a full game!

]]>
https://thepostulate.com/yahtzee-in-python-counting-individual-numbers/feed/ 0
Yahtzee: Finding the Full Houses https://thepostulate.com/yahtzee-finding-the-full-houses/?utm_source=rss&utm_medium=rss&utm_campaign=yahtzee-finding-the-full-houses https://thepostulate.com/yahtzee-finding-the-full-houses/#respond Thu, 04 Feb 2021 01:28:18 +0000 https://thepostulate.com/?p=219 Yahtzee: Finding the Full Houses Read More »

]]>
I think I’m making good progress on my implementation of a YahtzeeHand to analyze the game. However, I don’t have any logic right now to detect a Full House — three of one number combined with two of another number. This is a bit difficult to implement with the current logic, so I’m going to expand the SimpleHand class to include a new method: get_counts(). This method will return a tuple of size num_sides, where each item will indicate the number of dice in the hand bearing the same number as the index. For example, with the following hand [1, 4, 1, 4, 2], get_counts() would return (2, 1, 0, 2, 0, 0) — which would indicate 2 x ones, 1 x twos, 0 x threes, 2 x fours, and 0 fives and sixes. Having this functionality will really help discover full houses, and will also help a lot down the road when I have to figure out the “upper” part of the Yahtzee scorecard (ones, twos, threes, etc).

It turns out that get_counts() is pretty easy to implement with a comprehension:

    def get_counts(self) -> tuple:
        """
        Get the count of each number appearing in the hand.
        e.g. - a hand of [3, 3, 4, 4, 1] will return (1, 0, 2, 2, 0, 0)
        :return: A tuple of integers with the value at each index indicating the number of occurrences of number n-1 in
        the hand.
        """
        retval = tuple(self.hand.count(i) for i in range(1, self.num_sides+1))
        return retval

I also added a docstring because frankly it’s not quite clear what the return value from this method is and someone using it might be confused.

Having this simple get_counts() method allows me to pretty easily add a method to detect full houses:

class YahtzeeHand(SimpleHand):
...
    def is_full_house(self) -> bool:
        counts = self.get_counts()
        return 3 in counts and 2 in counts

Adding that to our overall test for “first time” rolls, let’s us see that full houses are more common than I thought!

100000 rolls:
LargeStraight        :     3191 / 100000 --    3.19%
SmallStraight        :    15446 / 100000 --   15.45%
FullHouse            :     3867 / 100000 --    3.87%
3ofaKind             :    21334 / 100000 --   21.33%
4ofaKind             :     2020 / 100000 --    2.02%
Yahtzee!             :       76 / 100000 --    0.08%

Now that I’ve got this working, I’m going to move on to do the “top” part of the score card and also construct the overall game logic that will help us use our new class to play some Yahtzee!

]]>
https://thepostulate.com/yahtzee-finding-the-full-houses/feed/ 0
Expanding the Yahtzee Simulation: Adding more hands https://thepostulate.com/expanding-the-yahtzee-simulation-adding-more-hands/?utm_source=rss&utm_medium=rss&utm_campaign=expanding-the-yahtzee-simulation-adding-more-hands https://thepostulate.com/expanding-the-yahtzee-simulation-adding-more-hands/#respond Wed, 03 Feb 2021 03:12:57 +0000 https://thepostulate.com/?p=216 Expanding the Yahtzee Simulation: Adding more hands Read More »

]]>
Now that I’ve got a solid foundation in SimpleHand, it’s relatively easy to expand the implementation of YahtzeeHand. This is a valuable aspect of inheritance and object oriented programming in general. I’m able to add the logic for 3, 4 and 5 (Yahtzee!) of a kind very easily. Here’s what it looks like all together in the YahtzeeHand class:

class YahtzeeHand(SimpleHand):
    def __init__(self, seed: int = 0):
        super(YahtzeeHand, self).__init__(num_sides=6, num_dice=5, seed=seed)

    def is_large_straight(self) -> bool:
        return self.num_sequential() == 5

    def is_small_straight(self) -> bool:
        return self.num_sequential() >= 4

    def is_triple(self) -> bool:
        return self.max_duplicates() >= 3

    def is_four_of_a_kind(self) -> bool:
        return self.max_duplicates() >= 4

    def is_yahtzee(self) -> bool:
        return self.max_duplicates() >= 5

I’ve got enough here now that I can run a quick simulation to answer some basic questions about Yahtzee — specifically how difficult it is to make each type of hand on an initial roll. Because I’ve got the basic Yahtzee class built, the code to run 100,000 rolls and collect specifics flows pretty easily:

def verify_yahtzee_hand(num_rolls: int = 10000):
    h = YahtzeeHand()

    num_special_hands = {
        "LargeStraight": 0,
        "SmallStraight": 0,
        "3ofaKind": 0,
        "4ofaKind": 0,
        "Yahtzee!": 0
    }

    for i in range(num_rolls):
        h.roll_all()
        if h.is_small_straight():
            num_special_hands["SmallStraight"] += 1
        if h.is_large_straight():
            num_special_hands["LargeStraight"] += 1
        if h.is_triple():
            num_special_hands["3ofaKind"] += 1
        if h.is_four_of_a_kind():
            num_special_hands["4ofaKind"] += 1
        if h.is_yahtzee():
            num_special_hands["Yahtzee!"] += 1

    print(f"{num_rolls} rolls:")

    for (name, occurrences) in num_special_hands.items():
        print(f"{name:20} : {occurrences:8d} / {num_rolls} -- {(occurrences / num_rolls):>8.2%}")

When we run the sim over 100,000 rolls, we get this result:

100000 rolls:
LargeStraight        :     3034 / 100000 --    3.03%
SmallStraight        :    15357 / 100000 --   15.36%
3ofaKind             :    21388 / 100000 --   21.39%
4ofaKind             :     2071 / 100000 --    2.07%
Yahtzee!             :       70 / 100000 --    0.07%

This yields some interesting results… namely, a “natural” Yahtzee (Yahtzee in one roll) is pretty tough to get! But also, a small straight (4 consecutive numbers) is relatively easy (about one chance in six). We are missing one major type of hand still: the full house. I’m going to need to build a bit of special logic to handle that, so I’ll do that in the next post!

]]>
https://thepostulate.com/expanding-the-yahtzee-simulation-adding-more-hands/feed/ 0
A Simple Yahtzee Simulation in Python https://thepostulate.com/a-simple-yahtzee-simulation-in-python/?utm_source=rss&utm_medium=rss&utm_campaign=a-simple-yahtzee-simulation-in-python https://thepostulate.com/a-simple-yahtzee-simulation-in-python/#respond Sat, 30 Jan 2021 18:58:06 +0000 https://thepostulate.com/?p=210 A Simple Yahtzee Simulation in Python Read More »

]]>
This is the beginning of what will be a multi-part series on simulating the game Yahtzee in Python. It builds upon some of the previous work I’ve done with the simple dice hand simulator.

Using this simulator, let’s see how we can extend it to model some common dice games. An important thing to realize is that there are two basic ways to understand events that involve chance: theory or simulation. This is very similar to the split between theoretical and experimental physics. Each approach has advantages and disadvantages, however my goal is to practice my Python programming, so using simulation to understand complex problems seems much more appropriate than trying to mathematically model dice games using probability theory.

Yahtzee is a game that I’ve been playing a bit lately and was wondering about some of the probabilities / strategies, so I’ve decided to explore that first.

Using SimpleHand as a base class, we can extend it to build the concept of a YahtzeeHand that is able to count the differing types of hands that appear in the game Yahtzee. We will start with a few of the most basic Yahtzee hands — Small Straight (4 sequential number), Large Straight (5 sequential numbers), and a Full House (a triple and a double in the same hand). Notice how that we can use the foundation we’ve built to make the implementation of this quite easy:

class YahtzeeHand(SimpleHand):
    def __init__(self, seed: int = None):
        super(YahtzeeHand, self).__init__(num_sides=6, num_dice=5, seed=seed)

    def is_large_straight(self) -> bool:
        return self.num_sequential() == 5

    def is_small_straight(self) -> bool:
        return self.num_sequential() >= 4

    def is_full_house(self) -> bool:
        counts = self.get_counts()
        return 3 in counts and 2 in counts

That’s it! Now we’ve got the basics of a Yahtzee hand and can use it to calculate some probabilities. Next time, I think I’ll extend this to include the other types of hands like three of a kind, four of a kind, Yahtzee and more.

]]>
https://thepostulate.com/a-simple-yahtzee-simulation-in-python/feed/ 0
Verifying the Dice Simulation – Theory vs. Reality https://thepostulate.com/verifying-the-dice-simulation-theory-vs-reality/?utm_source=rss&utm_medium=rss&utm_campaign=verifying-the-dice-simulation-theory-vs-reality https://thepostulate.com/verifying-the-dice-simulation-theory-vs-reality/#respond Fri, 29 Jan 2021 01:06:31 +0000 https://thepostulate.com/?p=208 Verifying the Dice Simulation – Theory vs. Reality Read More »

]]>
Now that I’ve built a basic framework for simulating “hands” of dice and I’ve also run enough unit tests to convince me that the “counting” algorithms work, the next thing to do is to make sure that when we roll the dice a lot of times, that the results come close to what probability theory says we should expect.

So, first, let’s figure out what some key probabilities are for a 3 dice hand:

  • P(3 of the same number or “triple”) = 6 / 216 (2.78%)
    [(1,1,1), (2,2,2), (3,3,3), (4,4,4), (5,5,5), (6,6,6)] / 6 * 6 * 6
  • P(2 of the same number or “double) = 90 / 216 (41.67%)
    [(1, 1, not 1), (2, 2, not 2)... (1, not 1, 1), (2, not 2, 2) ... (not 1, 1, 1), (not 2, 2, 2)] / 6 * 6 * 6
  • P(Everything else or “singles”) = 120 / 216 (55.56%)
    (216 - 96) / 216

Now, let’s run the following code over 100,000 hands to see how close we come to these probabilities:

def verify_simple_hand(num_rolls: int = 10000):
    h = SimpleHand(num_sides=6, num_dice=3)
    num_dups = {
        1: 0,
        2: 0,
        3: 0
    }
    for r in range(num_rolls):
        h.roll_all()
        nd = h.max_duplicates()
        num_dups[nd] += 1

    for (duplicates, occurrences) in num_dups.items():
        print(f"{duplicates} duplicates: {occurrences:8d} / {num_rolls} -- {(occurrences / num_rolls):>8.2%}")
1 duplicates:    55600 / 100000 --   55.60%
2 duplicates:    41628 / 100000 --   41.63%
3 duplicates:     2772 / 100000 --    2.77%

Process finished with exit code 0

This looks pretty good to me! Our simulated value for triples is within 6 of our expected number (2778) over 100k rolls. This is well in line with the variability we would expect. If we were being really serious, we probably could compute a p-value here… but this is just for fun… so repeatable results like this lead me to believe that we are correctly simulating the dice rolls and should have a good foundation for our future exploration!

]]>
https://thepostulate.com/verifying-the-dice-simulation-theory-vs-reality/feed/ 0
Simple Python Dice Simulator – How to Test Something Random https://thepostulate.com/simple-python-dice-simulator-how-to-test-something-random/?utm_source=rss&utm_medium=rss&utm_campaign=simple-python-dice-simulator-how-to-test-something-random https://thepostulate.com/simple-python-dice-simulator-how-to-test-something-random/#respond Thu, 28 Jan 2021 01:07:38 +0000 https://thepostulate.com/?p=206 Simple Python Dice Simulator – How to Test Something Random Read More »

]]>
In the last note, I outlined the basic design of the dice simulator, and also mentioned that I had built some unit tests. Some of you may be asking, “How do you run repeatable tests on something that’s random?” It’s a good question, and one that uses a technique that it’s good to be familiar with — specifically, the concept of a random number generator seed.

Some background: Random number generators on computers aren’t truly random. Instead they use a pseudo random number generation (PRNG) algorithm that appears random, but is actually deterministic (or predictable). The thing to remember is that a PRNG will produce the exact same sequence of numbers if it is initialized with the same seed. In practice, most PRNGs are seeded with the low order bits of the current time, or some other rapidly changing device characteristics… and because of this, they appear to produce random results. However, you can also override this default and initialize them with a given seed instead. If you do this, they will produce the same numbers… every time. This is super helpful for testing, or even for reproducing a simulation run for debugging purposes.

You can see an example of this with an excerpt of initializing the seed in SimpleHand and then also in the unit tests I built for the SimpleHand class:

class SimpleHand:
    def __init__(self, num_sides: int = 6, num_dice: int = 2, seed: int = None):
        self.rng = random.Random(seed)
...
from unittest import TestCase
from core.hand import SimpleHand, YahtzeeHand
TEST_SEED = 1234


class TestSimpleHand(TestCase):
    def test_roll_selected(self):
        h = SimpleHand(num_sides=6, num_dice=3, seed=TEST_SEED)
        self.assertEqual((4, 1, 1), h.get())
        h.roll_selected([1, 2])
        self.assertEqual((4, 1, 5), h.get())
        h.roll_selected([0, 1])
        self.assertEqual((1, 6, 5), h.get())
        h.roll_selected([2])
        self.assertEqual((1, 6, 6), h.get())
...

This setup allows us to build unit tests that can run over and over again, and always give the same results. Hooray!

Next, we’ll look at running a few quick simulations to make sure that the results from our project actually match what probability theory says should be happening when we roll a bunch of dice.

]]>
https://thepostulate.com/simple-python-dice-simulator-how-to-test-something-random/feed/ 0
Why Do We Have Low Expectations of Others? https://thepostulate.com/why-do-we-have-low-expectations-of-others/?utm_source=rss&utm_medium=rss&utm_campaign=why-do-we-have-low-expectations-of-others https://thepostulate.com/why-do-we-have-low-expectations-of-others/#respond Tue, 26 Jan 2021 17:25:34 +0000 https://thepostulate.com/?p=199 Why Do We Have Low Expectations of Others? Read More »

]]>
Over the weekend, it struck me that a good deal of the current political discourse (in the United States, at least) is driven by the belief that if “other” people are left to their own devices, they will do “bad” things. Certainly there is precedent for this; history is full of examples of small groups that have used power to oppress, suppress, or otherwise subjugate others. The interesting element for me, is that inherently I think most of us believe that we are better than that… no matter which side of issues we fall on.

So, it brings us to a point where we have multiple people on multiple sides of an issue each believing that they are the ones that would be benevolent, while everyone else would be malevolent. This seems intellectually dishonest to me. It’s not even an argument about being better equipped to solve a particular problem… It’s about intentions. I think the argument reduces to many of us truly believing that WE have good intentions, while other people do not. Why is this the case? Certainly if we were able to see inside the hearts of others, it might make things easier… but we can’t.

In my experience, one of the only things that has consistently shown that it can overcome this type of mistrust is familiarity. The more familiar we are with a person, the easier it is for us to believe that although their actions may be disagreeable to us, their intentions are good… and I think THAT is what we are gradually losing in society today. We are losing familiarity with those who think about things in a different way.

I think that a way out of our current situation is to simply spend (a lot of) time with people we don’t agree with… not to convince them that we are right and they are wrong, but to create the space and time to convince ourselves that their intentions are good… and frankly to give them the time to see that our intentions are good too.

It may be true that “the road to hell is paved with good intentions,” but it’s also true that even before it’s paved, the roadbed is built on a foundation of misunderstanding. That is what I think we should address.

]]>
https://thepostulate.com/why-do-we-have-low-expectations-of-others/feed/ 0
Modeling Dice Games in Python: The Beginning https://thepostulate.com/modeling-dice-games-in-python-the-beginning/?utm_source=rss&utm_medium=rss&utm_campaign=modeling-dice-games-in-python-the-beginning https://thepostulate.com/modeling-dice-games-in-python-the-beginning/#comments Mon, 25 Jan 2021 01:34:38 +0000 https://thepostulate.com/?p=188 Modeling Dice Games in Python: The Beginning Read More »

]]>
After spending a bit of time with ISBN and UPC parsing, I’ve decided that for the next bit of programming practice, I’ll take a bit of time to work with randomness, simulation, and maybe a bit of decision tree stuff. So, for the next couple of weeks or so, I’m going to spend some time modeling some popular dice games and seeing what I can learn about those games by proceeding “bottom up” (simulating a bunch of rolls) instead of “top down” (figuring out the odds using probability theory).

To do most of this work, I will need a basic model of a dice “hand” — a set of dice that are rolled and have some values. Note: It’s also possible, and perhaps more efficient, to do this using NumPy and making really big arrays of rolls… then processing things that way. However, that wouldn’t be a good mechanism for me to practice basic Python programming, so I’m not doing that!

Instead, I took a bit of time to build a simple class that represented a “hand” of dice. I created an underlying list of ints that represents the individual dice in the hand. I also built a couple of methods in the class that will help later to figure out various types of rolls and allow us to calculate them more easily.

Here is what the code looks like:

import random

class SimpleHand:
    def __init__(self, num_sides: int = 6, num_dice: int = 2, seed: int = None):
        self.rng = random.Random(seed)
        self.num_sides = num_sides
        self.num_dice = num_dice
        self.hand = [self.rng.randint(1, num_sides) for i in range(num_dice)]
        self.sorted_hand = None

    def __repr__(self):
        return str(self.hand)

    def _force_new(self, new_hand_list: list):
        # Sanity check
        if len(new_hand_list) != self.num_dice:
            raise Exception(f"Size of list [{len(new_hand_list)}] != number of dice in hand [{self.num_dice}]")
        for value in new_hand_list:
            if value > self.num_sides or value < 1:
                raise Exception(f"Improper value [{value}] for a hand with [{self.num_sides}] sides")
        self.hand = new_hand_list.copy()
        self.sorted_hand = None

    def roll_selected(self, positions: list):
        for i in positions:
            self.hand[i] = self.rng.randint(1, self.num_sides)
        self.sorted_hand = None

    def roll_all(self):
        self.roll_selected(list(range(self.num_dice)))

    def set_seed(self, new_seed: int = None):
        self.rng.seed(new_seed)

    def get(self) -> tuple:
        return tuple(self.hand)

    def get_sorted(self) -> tuple:
        if not self.sorted_hand:
            self.sorted_hand = sorted(self.hand)
        return tuple(self.sorted_hand)

    def get_counts(self) -> tuple:
        # TODO: Easiest implementation, but not very good performance
        """

        :return: A tuple containing the number of occurrences of each value indexed by die value
        """
        retval = tuple(self.hand.count(i) for i in range(1, self.num_sides+1))
        return retval

    def max_duplicates(self) -> int:
        return max(self.get_counts())

    def count_of(self, num: int) -> int:
        return self.hand.count(num)

    def num_sequential(self) -> int:
        sorted_hand = self.get_sorted()
        last_num = -1
        cur_sequence_length = 1
        max_sequence_length = 0
        for d in sorted_hand:
            if d == last_num + 1:
                cur_sequence_length += 1
            elif d == last_num:
                # Duplicate number case
                pass
            else:
                max_sequence_length = max(cur_sequence_length, max_sequence_length)
                cur_sequence_length = 1
            last_num = d
        return max(max_sequence_length, cur_sequence_length)

I also built some unit tests for this code. One notable piece of this is that you might notice the _force_new() method above. I created that so that I could, from the unit tests, force certain values into the hand in order to test the “analytical” methods like max_duplicates() and num_sequential(). In Python, beginning a method with an underscore indicates that it should be treated as “private” to that class and not used as part of the public interface.

I still need to tweak the usability of this a bit, but I think it will provide a good foundation for exploring a couple of popular dice games. Next up, we’ll try running some basic statistics and see how they line up with our calculated probabilities to make sure our model is working right!

]]>
https://thepostulate.com/modeling-dice-games-in-python-the-beginning/feed/ 1