Going random in the age of GameplayKit

Screen Shot 2015-06-30 at 8.08.57 AM

One of the great things about GameplayKit is how it helps you simulate rolled die and random distributions. The example in the above screenshot uses a Gaussian distribution as it rolls a number between 0 and 100. The results of 100000 “rolls” are shown in the playground.

Here’s an example that demonstrates how to generate random values.

public func RollEm(samples : Int) -> [Int] {
    // Build a source
    let source = GKARC4RandomSource()

    // Drop the first n > 768 values
    source.dropValuesWithCount(1024)

    // Build a distribution
    let distribution = 
        GKGaussianDistribution(randomSource: source, 
            lowestValue: 0, highestValue: 100)

    // Access random values
    var counts = Array(count: 101, 
        repeatedValue: 0)
    for _ in 1...samples{
        counts[distribution.nextInt()]++}
    return counts
}

Creating Sources

You start by creating a source. GameplayKit offers three choices: ARC4, linear congruential, or Mersenne twister. These are GKRandomSource subclasses that provide concrete randomization implementations. Each source uses a distinct approach to generate random numbers.

The linear source is fastest but least “random” because low bits repeat more than high bits. Apple recommends you choose this when performance matters. The Mersenne Twister source is slowest but creates the best results, avoiding repeating patterns. The ARC4 source (similar to arc4random) offers the middle ground of performance vs results.

The class documentation notes that ARC4 may create sequences with initial repeating values that reveal the source’s seed. “To obfuscate gameplay mechanics based on this generator, call this method with a count parameter of 768 or greater.” Drop values by calling dropValuesWithCount().

Apple also recommends avoiding GameplayKit randomization for cryptography.

Distributions

You pipe sources, which generate random bits, through distributions, which transform their output into actual random numbers. A distribution is limited by a range with known frequency characteristics. The Gaussian distribution used in the above example creates a normal (“bell”) curve, with a known mean and deviation derived from that range.

The shuffled distribution avoids repeated short sequences by “shuffling” outputs with uniform distributions. Apple writes,

A GKShuffledDistribution object produces random numbers that across many samplings are uniformly distributed, but where short sequences of similar values are unlikely. This behavior is sometimes called “fair” randomization, because true randomness in games can result in extended “lucky streaks” or “unlucky streaks” for players.

Finally, there’s the basic random distribution, which just creates a uniform distribution without bias.

Custom Distributions

I couldn’t find any notes about subclassing GKRandomDistribution in the headers but there are tons of probability distribution types and you’d think for simulation, you might want to expand beyond linear, shuffled, and Gaussian. I messed around a bit with building cumulative distribution functions for probability distributions. I found myself building classes using the GKRandom protocol instead of subclassing. Subclassing just seems pointless at this time.

Generating Numbers

Gameplay Kit replaces your dice pouch. You can simulate a d6 or d20 die using preset constructors or you can build a distributionForDieWithSideCount customize to the number of sides needed for your game. Just pull the nextInt from your die as each time you roll.

Distributions also allow you to grab random floating point values (nextUniform), coin tosses (nextBool). The returned values are affected by how you set up your distribution, so if use a skewed distribution, expect skewed results.

Shuffling

One final thing that GKRandomSource does that’s really nice to work with is arrayByShufflingObjectsInArray:. I’ve posted about using this before, so I’ll skip the details here. Handy and doesn’t require a distribution. Just grab the shared random source, which uses the built-in arc4 random generator.

2 Comments

  • A joker in this case is that GKGaussianDistribution doesn’t give you a Gaussian distribution. According to the documentation, you don’t want a Gaussian distribution, so it gives you something different. You’re welcome.

    Specifically, about once every 300 times, when you would expect to get a 3+ sigma result, it clips it back to a 3.0 sigma result. So if you are trying to generate a college basketball team by simulating a few thousand students and taking the tallest 5, then you will likely get 5 players with identical heights, who will play against their adjacent-state rivals who will also have exactly the same heights.

  • […] at WWDC 2015. It includes some interesting functionality that isn’t just for games such as random number generation, state machines and […]