NEW: Subscribe to Hacking with Swift+ and accelerate your learning! >>

Shuffling an array with GameplayKit: arrayByShufflingObjects(in:)

Many Swift game projects use this Fisher-Yates array shuffle algorithm implemented in Swift by Nate Cook:

extension Array {
    mutating func shuffle() {
        for i in 0..<(count - 1) {
            let j = Int(arc4random_uniform(UInt32(count - i))) + i
            swapAt(i, j)
        }
    }
}

With GameplayKit there's a specific method you can call that does a similar thing: arrayByShufflingObjects(in:). I say "similar thing" rather than "identical thing" because the GameplayKit returns a new array rather than modifying the original, whereas Nate's version shuffles in place.

For example, if you wanted to blithely ignore the inevitable legalities and set up a lottery in your neighborhood, you could create an array containing the numbers 1 to 49, randomize its order, then pick the first six balls:

let lotteryBalls = [Int](1...49)
let shuffledBalls = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: lotteryBalls)
print(shuffledBalls[0])
print(shuffledBalls[1])
print(shuffledBalls[2])
print(shuffledBalls[3])
print(shuffledBalls[4])
print(shuffledBalls[5])

Note that I'm using the default system randomization because determinism is exactly what you don't want in a lottery. Actually, forget it: if you're going to ignore the law and set up your own lottery, you might as well fix it so you win, right?

One of the advantages of GameplayKit's randomization is that it is truly deterministic, even across devices. This means as long as you tell it where to start, it will produce the same series of random numbers in the future. This is perfect for our evil lottery plan, and it gives me the chance to show you one last thing: seeding GameplayKit's random sources.

When we created our random seeds earlier, we just used this:

let mersenne = GKMersenneTwisterRandomSource()

That creates a new Mersenne Twister random source with a random starting point. But if you want to force a starting point – either because you want to win your lottery or because you want players in a network game to be synchronized – you can create your random source with a specific seed, which is a fixed starting point.

When you use a seed value, your random number generator becomes predictable – you can always predict exactly what “random” numbers get generated. But that's OK, because you can generate the seeds using a separate random number generator, so you're guaranteed uniqueness.

Here's our lottery example rewritten using a fixed seed value of 1001:

let fixedLotteryBalls = [Int](1...49)
let fixedShuffledBalls = GKMersenneTwisterRandomSource(seed: 1001).arrayByShufflingObjects(in: fixedLotteryBalls)
print(fixedShuffledBalls[0])
print(fixedShuffledBalls[1])
print(fixedShuffledBalls[2])
print(fixedShuffledBalls[3])
print(fixedShuffledBalls[4])
print(fixedShuffledBalls[5])

If you run that code now you'll see that the balls are shuffled identically every time. It's a random order, but predictably random if you know what I mean!

Hacking with Swift is sponsored by NSSpain

SPONSORED Announcing NSSpain 2020: Remote Edition! An online, continuous conference for iOS developers. We’ll start on Thursday and finish on Friday, with talks, activities, and lots of fun for 36 hours, non-stop. Sound good? Join us!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Link copied to your pasteboard.