posted by
on October 14, 2025Unfair Flips is a game about flipping a coin to get 10 Heads flips in a row.
Unfair Flips is a game about how humans interact with variable probability.
Unfair Flips is a game about how people interpret the information presented to them, relative to the seemingly random outcomes they experience.
Unfair Flips is a game about why people play games at all, when there’s only a finite amount of time to experience them.
Unfair Flips is a game with a speedrunning community. The rules of the speedrun say that time starts when the first coin is flipped and ends when one of the various endings occurs and then number of total flips is displayed. The current world-record strategy routeing seems like an optimal series of upgrades to minimize time played, along with a lot of luck. Statistically speaking, the variance in flips will eventally produce a relatively fast run. The minimum required number of flips per run is 10 Heads and no Tails. With enough time across enough runners, it’s possible that someone will even get this lucky. Other speedruns tend to rely on memorization, dexterity, and endurance. While the orders of upgrades could land in those former categories, the repeated flipping of the coin lies squarely in the lattermost category.
Unfair Flips is a game made by Heather Flowers in Unity. Using JetBrains’s dotPeek decompiler, I read through the CoinFlip.cs
file, veryfing that the code for whether a coin lands Heads or Tails is solely1 based on Heads flip percentage and Unity’s pseudorandom number generator:
// CoinFlip.cs
bool heads = (double) UnityEngine.Random.Range(0.0f, 1f) < (double) coinFlip.flipHeadsChance;
Unfair Flips is a game made in Unity 2022.3.62.f2, which relies on the default pseudorandom number generator, which is further reliant on an XORshift128-style algorithm. The seed of this pRNG algorithm is determined on app start:
It is statically initialized with a high-entropy seed from the operating system, and stored in native memory where it will survive domain reloads. This means that the generator is seeded exactly once on process start, and after that is left entirely under script control.
An approximate implementation of Unity’s Random.Range
function can be found here. Because XORshift128 is not cryptographically secure, it is possible to determine the seed of an XORshift128-style pRNG by observation of outputs alone. This could be done in as few as three sequential random numbers, if all of the bits of the number were revealed. By determining the seed, it would be possible to know whether the next number accessed from this generator would correspond to a Heads or a Tails. In fact, it would be possible to look arbitrarily far ahead and figure out when a sequence of numbers would produce 10 Heads and a game win. In addition to the Random.Range
call for determining Heads or Tails, the code for flipping the coin includes a random call for number of turns of the coin in the flip animation, a random call for the sound to play during the flip, a random call on the 9th Heads flip to determine the ending, and - if the ending is Tails - another random call to determine the sound of the coin landing. This means that each flip could reveal about a half-dozen bits if all these parts were well-observed. However, according to the rules of the speedrun, time starts at first flip, which means time would be ticking while the flips are being performed to reverse engineer the seed.
Unfair Flips is a game with one non-player character: Gar, the mongoose-mask-wearing milk-drinking bar patron. Gar drinks this milk throuhout the course of play. Gar’s drink function is as follows:
// Gar.cs
private IEnumerator Drink()
{
while (true)
{
if (this.ableToDrink)
{
yield return (object) new WaitForSeconds(Random.Range(30f, 60f));
if (this.ableToDrink)
this.img.sprite = this.drink1;
yield return (object) new WaitForSeconds(1f);
if (this.ableToDrink)
this.img.sprite = this.drink2;
yield return (object) new WaitForSeconds(Random.Range(2f, 5f));
if (this.ableToDrink)
this.img.sprite = this.drink1;
yield return (object) new WaitForSeconds(1f);
if (this.ableToDrink)
this.img.sprite = this.baseSprite;
}
yield return (object) null;
}
}
Gar’s drinking is completely independent of the player’s actions and continues even in the main menu, on start-up, just after the random seed is initialized. A “diligent observer” could note how many seconds it takes Gar to begin drinking, as well as how long it takes Gar to put down the milk glass, and those seconds could be used to reveal bits about the random seed, which could then be reverse engineered through the same mechanisms above. After enough careful observation, it could be possible to determine that a given seed would produce the right random numbers for exactly 10 Heads flips in a row at some future point.
Unfair Flips is a game that runs at 60fps. By observing frames of Gar’s drinking habits, an “extraordinarily diligent observer” could note how many frames it takes Gar to begin drinking, thereby how many seconds, and also thereby gaining additional decimal places - meaning extra bits - of precision on what the Random.Range
number is. The same is true for how many frames it takes Gar to put down the milk after drinking, though the total number of bits leaking through would be fewer, due to the smaller range bounds.
Unfair Flips is a game with a theoretical optimal strategy: use tools to determine the random seed over the course of days or weeks of the game running and Gar drinking milk, begin a timer and the first flip at exactly the moment when the tool says that there’s a 100% chance, and then complete 10 Heads flips in a row. As of the timer starting, no tools would be necessary2. The runner would flip Heads 8 times, gain access to the upgrades, mash the -0.2 seconds flip time
button, flip a 9th Heads and have the upgrade menu disappear, and then flip the 10th Heads and stop the timer.
Unfair Flips is a game that will someday have a glitchless, tool-unassisted World Record of around ~19.6 seconds from start to finish of the recording. It will take days or weeks of observations3, followed by some random amount of time having to wait for the right opening, and then about 20 seconds of flipping. To me, this amount of work to get the World Record run is just barely too much to be worthwhile. I have loved the process of digging into how a run like this could be done,4 but I don’t have plans on trying it myself.
Unfair Flips is a game about why people play games at all, when there’s only a finite amount of time to experience them.
Footnotes
-
My apologies to The Red Duke and The Blue Baron for this existential threat to their existence. ↩
-
In fact, the recording instructions for the speedrun specifically state that the runner needs only submit a recording of the first flip through the 10th Heads, along with the total number of flips. This recording, if done according to the rules, could be impossible to differentiate from a recording of someone just happening to get extraordinarily lucky. ↩
-
Unless it’s possible to speed up the framerate to compress the time that an automated tool takes to reverse-engineer the random seed. ↩
-
Shoutouts to both Colin and Cassie for helping me bounce ideas off of them while going through this process. ↩