Adaptive random generation of multiple outcomes

Games often want to use randomness to spice up the experience – it can be a lot more exciting to risk something when you are not entirely certain of the result. In my game abstractanks, the power-ups are generated randomly. However, when playing the game, it seemed like it was always generating the same power-ups in a row, which can be kind of frustrating. Tough luck, because this is just how uniform randomness behaves – as anyone who ever played the board game Sorry! or the german class Mensch ärgere dich nicht! can sure testify.

Let’s try that with C# code:

```var outcomes = new []{'A', 'B', 'C', 'D', 'E', 'F'};
for (int i = 0; i < 200; ++i)
{
var index = random.Next(outcomes.Length-1);
Console.Write(outcomes[index]);
}
```

This will produce something like this:

```ECDDBABCEEBBDDADEDECCAECECEADBBCCCDAEBEECDBCACAEAA
BDEACECBDDBAEDCEEAEAECDEEEECBCCEECEDCBAECCCBDCDDEA
CEAABDEEBDEAEBABABDEBDAACBECBBAACAEDEEBAECECECCBAB
```

See all those strings of Cs and Es? Horrible! That does not feel random!

Games Dota 2 work with this by tweaking distribution after each “roll”. Specifically, for things like Phantom Assassin’s Coup de Grace, the chance is increased slightly after each unsuccessful attempt, making the 4th or so attempt a guaranteed critical strike.
But this technique only work nicely for one event in a stream of attempts. It fails to make multiple outcomes look better.
For game I devised an algorithm with two properties:
1. The same roll never appears twice in succession
2. No long stretches without a specific roll
Here’s my algorithm to do that:

```var outcomes = new []{'A', 'B', 'C', 'D', 'E', 'F'};
var chances = new []{1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
for (int i = 0; i < 200; ++i)
{
// Normalize chances so they sum up to 1.0, then build their prefix sum
var total = chances.Sum();
var prefixSum = chances
.Select(x => x / total)
.Aggregate(new List<double>(), (list, x) =>
{
return list;
}).ToList();

// Roll an outcome
var roll = random.NextDouble();
var pick = prefixSum.FindIndex(x => x >= roll);
Console.Write(outcomes[pick]);

// Now adapt the distribution by removing all chance from
// the last pick and distributing it to the N-1 others
var increment = chances[pick] / (chances.Length - 1);
chances = chances.Select((x, index) =>
{
if (index == pick)
return 0.0;
else
return x + increment;
}).ToArray();
}
```

And here’s what that produces:

```AEDFBCAEFDBAEDFCBDECBFCFDBEACFDECBDAEFCEBACDEAFBDC