back to list


2025-08-15

A Commitment Scheme for Draft Order Randomization

Many fantasy football platforms have clear and transparent features for commissioners to randomize the draft order. Usually the commissioner can schedule a time to have the order randomly chosen, or it can be done ad hoc at a time of their choosing. Since some leagues however might have custom rules, platforms also allow commissioners to manually set a non-random arbitrarily chosen draft order. In certain circumstances the draft order can be very important to members of the league. If your league picks a random draft order, then naturally raises the question: how you know the draft order was chosen truly randomly? We will assume any order chosen by the platform itself is sufficiently random, but how do you know the commissioner didn't roll it a couple extra times until they got the spot they wanted? A good fantasy platform should be very transparent about the decision and should broadcast to the other members that the draft order has been updated. But this isn't universally the case. Or alternatively, if for whatever reason you set a random order manually (generated outside of the fantasy platform), how do you know the order is random?

This blog is describing a simple commitment scheme that allows for a league of players to ensure the order was set fairly. The idea is really a simple variation on "Coin Flipping", but please email me if you notice any holes or have any suggestions.

The Challenge

The goal is to design a simple system that allows a random draft order to be chosen for a league of players, while each can be confident it was chosen at random, and not manipulated by any other member of the league, including the commissioner.

To simplify things, we will say we want to decide on some random number, not necessarily an order. There are many practical ways we can derive a random order from a simple random integer. One for instance is using the number to seed a previously discussed generated and using it to shuffle the teams in the league, see numpy.random.Generator.shuffle. This isn't very accessible though, so other more practical methods will be discussed towards the end.

The Method

First you need to decide on and assemble a few things:

  1. A stable channel for communication (league chat, text group chat, etc)
  2. A hash function and settings everyone is to use (a simple website that will give you a sha256 hash)
  3. An initial order of the teams (this won't influence the final random, but needs to stay the same)

Some of these steps might sound overly specific but they are all important and are there for good reason.

Step 1: Start

The commissioner should announce that the order generation is starting. They should the initial order that will be used. It doesn't matter how they come up with this order, but it it needs to be agreed upon. Ask each member of the league to make up some secret phrase that they would be willing to share later. Have them write it down somewhere so they don't forget.

Step 2: Sharing

Next have them find the sha256 hash of their secret. They can do these by using the previously agreed upon website. Once they have the hash of their secret, they should share it in the league chat. It is very important that they only share the hash (the random string of letters and numbers) not the entire secret.

Step 3: Compare

Once everyone has shared their hash, the commissioner should then ask everyone to share their secret. Each member of the league should then go and ensure that the hash of the secret they shared matches the hash that they reported during the "Sharing" phase. If any of the hashes do not match, this process should be restarted. A mismatch hash indicates either a simple mistake or an intention to rig the draft order.

Step 4: Evaluate

Assuming each of the hashes match what each member initially reported, you can then move on to calculating the final random number. Compile all of the secrets that the members gave, not the hashes. Next, put the secrets in lexicographical (alphabetical) order. Finally, append all of the secrets together in this order. Do not put any spaces or other symbols between them. You can then place this one long string into the same sha256 calculator. The final sha256 hash is your piece of randomness. This is effectively a 256 bit random number.

Step 5: Derive

You then need to take this large random number and produce an order. One straight forward way to do this would be by using some Python code to shuffle your initial order randomly and seeding the random number generator using the number you calculated in the last step.

This could look something like this:

import numpy as np

teams = np.array(["t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"])
rng = np.random.default_rng(seed=0xae0e657769d5dcc53ef588405ad76db11b36da5995b05c5d844efbf2a952df0e)
rng.shuffle(teams)
print(teams)

The most important thing is that you decide on the procedure for deriving the order from the random number before hand.

Reminder

After Step 2, everything should be verifiable by each member in the league. Meaning everyone should be able to easily replicate and verify the resulting draft order. If there are even small differences in the way the details, like combining everyone's secrets differently, or using different sha256 calculator websites, then the resulting orders may be different. The key thing that should impact the final draft order is everyone's secrets.

Example

Let's work a simple example of what this might look like in tiny 4 team Sleeper league.

The 4 teams are:

  • Sara's Team (Commissioner)
  • Todd's Squad
  • Aditi's Team
  • Larry's Team

Sara picks the following initial starting order:

  1. Todd's Squad
  2. Aditi's Team
  3. Sara's Team
  4. Larry's Team

Sara then says they'll use the following sha256 calculator website.

The process proceeds as follows:

  • Sara requests everyone pick a secret and send the sha256 hash in the league chat
  • Aditi sends the hash 8dcbd7a90e4970405efdfe5b4f598461c3b29a8dade6eb70893f3edf90092365
  • Todd sends the hash 7c95d7bf76008830030d2383d60f88ba97e69e50ea8e0655cd69ad54c7d7cafa
  • Sara sends the hash a2d0116ab97dd735c6108567a8c008d83829705e471cf57fab1f414eb0f1cfb0
  • Larry sends the hash 125a6774223cea3b09bbba0b6254e746926553ce07f6de256e8be378e85e5fff
  • Now that everyone has sent their hash, Sara requests everyone reveal their secret
  • Aditi reveals her secret to be Aditi rules 5674
  • Todd reveals his secret to be super-secret-phrase-4321
  • Sara reveals her secret to be 54345317888653
  • Larry reveals his secret to be secret001121
  • Everyone in the league confirms that all of the hash's correctly correspond to their secret
  • Sara alphabetically sorts all of the secrets:
    • 54345317888653
    • Aditi rules 5674
    • secret001121
    • super-secret-phrase-4321
  • Sara then combines these secrets in this order to get: 54345317888653Aditi rules 5674secret001121super-secret-phrase-4321
  • Sara then takes the sha256 hash of this value to get: 54e66f9507de74eeed0bd05921a887687fef8b89c82d84991d20223a248bce04
  • Sara then runs the following script:
    • import numpy as np
      
      # This is the initial order decided before starting the process
      teams = np.array(["Todd's Squad", "Aditi's Team", "Sara's Team", "Larry's Team"])
      # This is the hash we found in the previous step
      our_random = 0x54e66f9507de74eeed0bd05921a887687fef8b89c82d84991d20223a248bce04
      rng = np.random.default_rng(seed=our_random)
      rng.shuffle(teams)
      print(teams)
      # ["Aditi's Team", "Sara's Team", "Todd's Squad", "Larry's Team"]
      
  • The script shows the order is:
    1. Aditi's Team
    2. Sara's Team
    3. Todd's Squad
    4. Larry's Team
  • The rest of the league members verify this result by rerunning the process and ensuring they get the same result

Why this works and Why you (probably) shouldn't use it

What we'd really like to be able to do, is just have everyone pitch in their secrets, hash them into one number, and then run with that as our randomness used to generate the order. However, the issue is that if anyone in the league as the opportunity to go last, which is obviously inevitable, they have the opportunity to "choose" the draft order by picking a secret that generates a hash that shuffles the order in a desirable way.

The key that makes this approach work is the hash function. These functions take some input data (our secrets) and produce some random looking output. In addition, these functions are not reversible. So if you have the hash, there is absolutely no way to know the original input. However, if the original input is later revealed it's trivially easy to get the same hash. The hash that each player provides are commitments. Meaning that each player is "committing" to their secret without having to reveal to anyone what that secret is. If a player later tries to lie by saying their secret is something other than what they originally committed to, it'll be easy to confirm they lied by hashing their secret and seeing it doesn't match their commitment.

So by making every player commit to their secret before revealing them, we can be sure that no one, including the last player to provide their secret, can manipulate the draft order.

This is all great, but realistically your league of 10 to 12 aren't going to go through all of this effort. If you use a more modern platform like Sleeper that announces anytime the draft order changes or is re-rolled, that is really just as fair as this. If you have those options available just use those.