Fairer Dice

Try It Out

Open this example in Repiano

Introduction

A fair die rolled well will always fall on one of the six sides with exactly 1/6 a chance. Us humans though are not great at really appreciating random chance: we feel like sides that haven't come up recently are "due" to come up or switch slot machines at casinos feeling like certain machines are running hot or cold. Even generating a random-looking series of coin flips we don't do very well.

What if you're not rolling a die for a long period of time, and can fully accept truly random rolls, where after a while, there's a decent chance that some sides are over or underrepresented? What if you really want dice to converge quickly to 1/6 per side, and if a side hasn't come up in a while, you want it to be slightly more likely for that side to come up? Would dice that converge quickly to 1/6, more than truly fair and random dice, be "fairer" dice?

Configuration and Reasoning

When a "fairer" die is rolled, you want the face rolled to be controlled by two factors: a truly fair random roll, and a slight nudge towards balancing the sides - making sides that haven't come up a little more likely, and sides that have come up a lot a little less likely. So those will be two reasons used to identify a given roll.

A purely random number can be implemented with a value reason. A value reason with a value 0 and maximumValue 1 will generate random numbers from 0 to 1. This number has nothing to do with which side is rolled, or anything else, so there's no property to reference or anything external to need to reference. Let's say we want that random roll to still be most of the outcome, so I'll set a weight of 10.

As far as the slight nudge, there are a few possible ways to approach the problem. The most obvious is to say "I want the total count of each side to be about the same." This would mean a balance reason, balancing the total counts over the history of the roll.

In order to get a total count of sides over time, we would need a benefit reason at its simplest configuration - just count the rolls. benefit reasons have lots of configuration options to count things differently depending on how far back in time, or how far along a certain target is reached. In this case, a roll is a roll - it doesn't matter if I rolled a '4' four times at the beginning or the end, if it means that '4' is overvalued, then it's overvalued.

Actually implementing a benefit reason to count the '4's means there needs to be a property that gives a '1' every single time a 4 shows up. So, it's not the cleanest, but in the collection, we'll need to have an entry for each possible roll, but then a individual property that says how much each roll counts as a given side. It's a little pedantic to need to spell out that rolling a '4' means you've rolled a '4' one time, a '1' zero times, a '2' zero times, etc., but it spells out that this choice gives you these raw number properties that then can be summed over time.

So that means there are six benefit reasons - one for each side, and then a single balance reason. The weight of each benefit reason under the balance is the ideal ratio of the reasons, so a weight of 1 for each means we want a 1:1:1:1:1:1 ratio. (You can easily implement an unfair die by changing those ratios around.)

As far as experimenting or other obvious implementation choices, the main one would be adjusting the weight of the roll and the balance reasons - if the balance reason is much heavier, then it will force making rolls the least common side and use the random as a tie breaker. Another idea is to increase the scaling of the balance reason; as of this writing there isn't a clean way to more strongly emphasize ratios that significantly out of balance, but that could come up soon.

There are two other implementation ideas - one out of left field, and one that's just not very good.

If there were no balance reason, it could be possible to implement something like this by using six benefit reaons with a utility power function with a factor/scaling 0 < 1. It means that the first time you roll any number (e.g. a 4), it would be worth the most, the second time you roll a number it's worth a bit less, etc. That means when you roll, you would be choosing between a random reason plus, say, rolling '4' for the 3rd time, or rolling '6' for the 1st time. Because 1st time rolls are worth the most, they would be most preferred, so this would be a way to implement these "fairer" dice. That said, because over time the power would diminish, you'd need to keep scaling up the reason over time for it to be consistent, and the math behind the scenes is harder to conceptualize.

One not very good option would be to use the recency reason and prioritize sides that haven't come up in the last several rolls. This does nudge somewhat towards a fairer die, or at least a die with fewer streaks, but given that recency only accounts for the most recent time a number was rolled, it doesn't account for if that side actually has come up the most or least.

Example Notes

I created this example on 2025-10-23, and as of right now days are always recorded in history, so you'll see that in the example history; it's just an artifact of when I created it and can be any arbitrary day.

Collection

{
  "guid": "02998479-57ba-4f49-8212-53518c8aa28f",
      "name": "Fairer Dice",
      "isModified": false,
      "schema": {
        "idProperty": "Face",
        "properties": [
          {
            "name": "Face",
            "type": "string",
            "label": ""
          },
          {
            "name": "1",
            "type": "number",
            "label": ""
          },
          {
            "name": "2",
            "type": "number",
            "label": ""
          },
          {
            "name": "3",
            "type": "number",
            "label": ""
          },
          {
            "name": "4",
            "type": "number",
            "label": ""
          },
          {
            "name": "5",
            "type": "number",
            "label": ""
          },
          {
            "name": "6",
            "type": "number",
            "label": ""
          }
        ]
      },
      "items": [
        {
          "1": 1,
          "Face": "1"
        },
        {
          "2": 1,
          "Face": "2"
        },
        {
          "3": 1,
          "Face": "3"
        },
        {
          "4": 1,
          "Face": "4"
        },
        {
          "5": 1,
          "Face": "5"
        },
        {
          "6": 1,
          "Face": "6"
        }
      ]
    }

History

{
  "guid": "f0e33765-3951-4404-b846-779b1b0ce11d",
      "name": "Fairer Dice",
      "schema": {
        "properties": [
          {
            "name": "Face",
            "type": "collection",
            "label": "",
            "collectionGuid": "02998479-57ba-4f49-8212-53518c8aa28f"
          }
        ]
      },
      "items": [
        {
          "id": "b95e9f3b-ddcb-4858-81ab-99c9cfddfd3a",
          "timestamp": "2025-10-23",
          "Face": "6"
        },
        {
          "id": "5bbd6667-1d42-4a96-918c-3795474aa4c9",
          "timestamp": "2025-10-23",
          "Face": "4"
        },
        {
          "id": "d8451cc2-7b03-4ed1-a649-d6c4f9fa7435",
          "timestamp": "2025-10-23",
          "Face": "2"
        },
        {
          "id": "190c7203-4334-4cd5-950d-e24b22de3514",
          "timestamp": "2025-10-23",
          "Face": "1"
        },
        {
          "id": "2114483e-36d7-4c5d-9abf-c071ba9e7471",
          "timestamp": "2025-10-23",
          "Face": "6"
        },
        {
          "id": "8b94db12-5d2c-4184-8349-89d9bc995673",
          "timestamp": "2025-10-23",
          "Face": "6"
        },
        {
          "id": "dee74204-ea5f-4f75-a5bf-6a8467e22220",
          "timestamp": "2025-10-23",
          "Face": "6"
        },
        {
          "id": "e51c4cf1-683d-4132-bf33-9e3858054ab9",
          "timestamp": "2025-10-23",
          "Face": "3"
        },
        {
          "id": "55188800-6ac9-4c15-b7da-a0f7c8322ebb",
          "timestamp": "2025-10-23",
          "Face": "4"
        }
      ]
    }

Choice

{
  "guid": "de51f891-1709-4dd0-a474-23ceac6b5675",
      "name": "Fairer Dice",
      "collectionGuid": "02998479-57ba-4f49-8212-53518c8aa28f",
      "historyGuid": "f0e33765-3951-4404-b846-779b1b0ce11d",
      "reasons": [
        {
          "name": "Random Roll",
          "weight": 10,
          "type": "value",
          "properties": {
            "maximumValue": 1,
            "value": 0
          }
        },
        {
          "name": "Balance",
          "weight": 1,
          "type": "balance",
          "properties": {
            "reasons": [
              {
                "name": "1",
                "weight": 1,
                "type": "benefit",
                "properties": {
                  "name": "1"
                }
              },
              {
                "name": "2",
                "weight": 1,
                "type": "benefit",
                "properties": {
                  "name": "2"
                }
              },
              {
                "name": "3",
                "weight": 1,
                "type": "benefit",
                "properties": {
                  "name": "3"
                }
              },
              {
                "name": "4",
                "weight": 1,
                "type": "benefit",
                "properties": {
                  "name": "4"
                }
              },
              {
                "name": "5",
                "weight": 1,
                "type": "benefit",
                "properties": {
                  "name": "5"
                }
              },
              {
                "name": "6",
                "weight": 1,
                "type": "benefit",
                "properties": {
                  "name": "6"
                }
              }
            ]
          }
        }
      ]
    }