Project 35 --> Project 36

This commit is contained in:
CypherPoet 2019-03-02 20:03:49 -05:00
parent 53948e433a
commit cf6ddd8f5e
5 changed files with 193 additions and 2 deletions

View File

@ -0,0 +1,87 @@
//: [Previous](@previous)
import UIKit
import GameplayKit
// six-sided die
let d6 = GKRandomDistribution.d6()
d6.nextInt()
// twenty-sided die
let d20 = GKRandomDistribution.d20()
d20.nextInt()
let crazyDie = GKRandomDistribution(lowestValue: 20, highestValue: 23508)
crazyDie.nextInt()
// Going out of bounds will straight-up crash things, though
// crazyDie.nextInt(upperBound: 1)
/// Distributions can have a custom source of randomness
let twisterEngine = GKMersenneTwisterRandomSource()
print(GKRandomDistribution(randomSource: twisterEngine, lowestValue: 10, highestValue: 1000).nextInt())
/**
Sometimes, though, we don't want completely realistic randomness: We might
want to avoid repeats. We might also want our distribution to be non-uniform --
for example, a Gaussian distribution
*/
/**
`GKShuffledDistribution` is an anti-clustering distribution, which means it
shapes the distribution of random numbers so that you are less
likely to get repeats. This means it will go through every
possible number before you see a repeat, which makes for a
truly perfect distribution of numbers.
*/
let shuffledDistribution = GKShuffledDistribution.d6()
for roll in 1...6 {
print("Roll \(roll): \(shuffledDistribution.nextInt())")
}
let gaussianDistribution = GKGaussianDistribution(lowestValue: 0, highestValue: 20)
var counts: [Int : Int] = [:]
for _ in 1...100_000 {
let result = gaussianDistribution.nextInt()
if let currentCount = counts[result] {
counts[result] = currentCount + 1
} else {
counts[result] = 1
}
}
for number in 1...20 {
print("\(number): \(counts[number]!)")
}
/**
Random number generators consist of two components: A source (sometimes synonymous with "engine") and a distribution.
The entropic output of the source/engine is mapped onto the distribution to produce a final value.
This is important, because we can _seed_ engines -- give them specific starting point. When
an engine is deterministic, using a common seed allows us to reproduce the same random output
across systems and individual game runs.
*/
let twisterEngineA = GKMersenneTwisterRandomSource(seed: 42)
let twisterEngineB = GKMersenneTwisterRandomSource(seed: 42)
let options = [Int](0...200)
let selectionA = twisterEngineA.arrayByShufflingObjects(in: options)[0..<6]
let selectionB = twisterEngineB.arrayByShufflingObjects(in: options)[0..<6]
print(selectionA)
print(selectionB)
//: [Next](@next)

View File

@ -0,0 +1,57 @@
import UIKit
import GameplayKit
let sharedRandom = GKRandomSource.sharedRandom()
print(sharedRandom.nextInt())
print(sharedRandom.nextInt())
print(sharedRandom.nextInt())
print(sharedRandom.nextInt(upperBound: 20))
print(sharedRandom.nextInt(upperBound: 20))
print(sharedRandom.nextInt(upperBound: 20))
print(sharedRandom.nextBool())
print(sharedRandom.nextUniform())
/**
Using the system's built-in random number source is exactly what you want when you just need something simple.
But the system's random number generator is not deterministic,
which means you can't predict what numbers it will output because it
always starts in a different state and that makes it useless for synchronizing network games.
*/
/**
Fortunately, GameplayKit offers three custom sources of random numbers,
all of which are deterministic, and all of which can be
serialized  i.e., written out to disk using something like NSCoding:
1. `GKLinearCongruentialRandomSource`: Slightly higher performance than Arc4, but lower randomness
2. `GKMersenneTwisterRandomSource`: High randomness, but slightly lower performance than Arc4
3. `GKARC4RandomSource`: Goldilocks random source
While deterministic, none of these ar cryptographically secure
*/
let linearCongruential = GKLinearCongruentialRandomSource()
print(linearCongruential.nextInt(upperBound: 20))
let twister = GKMersenneTwisterRandomSource()
print(twister.nextInt(upperBound: 20))
let arc4 = GKARC4RandomSource()
/**
Apple recommends you force flush its ARC4 random number generator before using it
for anything important, otherwise it will generate sequences that can be
guessed to begin with. Apple suggests dropping at least the first 769.
*/
arc4.dropValues(1024)
print(arc4.nextInt(upperBound: 20))

View File

@ -0,0 +1,39 @@
//: [Previous](@previous)
import Foundation
/// `arc4random` is the classic way to generate random numbers.
/// It generates a number between 0 and 4,294,967,295, giving a range of 2^(32 - 1)
print(arc4random())
/// We can constrain this with a modulus
print(arc4random() % 42)
/// But to prevent [modulo bias](https://zuttobenkyou.wordpress.com/2012/10/18/generating-random-numbers-without-modulo-bias/), we can just pass our constraint as an argument
print(arc4random_uniform(42))
func randomBetween(min: Int, max: Int) -> Int {
guard min < max else { return min }
let randomFactor = Int(arc4random_uniform(UInt32((max - min) + 1)))
return randomFactor + min
}
for i in 0..<10 {
print(randomBetween(min: 4, max: 10 + (i * 10)))
}
/// So yeah... Swift's newer randomness methods are much nicer 😎
print(Int.random(in: 100...1000))
print(Double.random(in: 100...1000))
print(Float.random(in: 100...1000))
print(Bool.random())
//: [Next](@next)

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='ios' executeOnSourceChanges='false'>
<pages>
<page name='Old School Randomness'/>
<page name='GameplayKit Randomness'/>
<page name='Distributions'/>
</pages>
</playground>

View File

@ -58,8 +58,8 @@
| 32 | 📱<br>[Swift Searcher](/32-swift-searcher) | Dynamic Type, NSAttributedString, SFSafariViewController, SFSafariViewControllerDelegate, Core Spotlight, UIContentSizeCategoryDidChange, responding to the `CSSearchableItemActionType` activity during app startup to handle CoreSpotlight search hits | ✅ |
| 33 | 📱<br>[Name That Tune](/33-cloudkit-guess-the-song) | All about CloudKit ☁️: loading and saving text data, loading and saving binary data, subscribing to CKRecord updates, delivering push notifications, CloudKit Dashboard and more; AVAudioRecorder; AVAudioSession; requestRecordPermission(); CKRecord; CKAsset; CKQueryOperation; NSPredicate; CKRecord.Reference; fetch(withRecordID:); save(); CKQuerySubscription; NSSortDescriptor | ✅ |
| 34 | 🎮<br>[Four in a Row](/34-gamekit-four-in-a-row) | GameplayKit AI, GKGameModel, GKGameModelPlayer, GKGameModelUpdate, AI Heuristics, NSCopying, GKMinmaxStrategist | ✅ |
| 35 | 🛠<br>[Random Numbers](/35-Random-Numbers) | Int.random(in:), Float.random(in:), Double.random(in:), CGFloat.random(in:), Bool.random(), arc4random(), GKRandomSource.sharedRandom(), GKLinearCongruentialRandomSource, GKMersenneTwisterRandomSource, GKARC4RandomSource, GKRandomDistribution, GKShuffledDistribution, GKGaussianDistribution, Fisher-Yates Algorithm, arrayByShufflingObjects(in:) | 🚧 |
| 36 | 🎮<br>[Crashy Plane](/36-crashy-plane) | Composed Methods, Scale Modes, Parallax Scrolling, SpriteKit Physics, SKPhysicsContactDelegate, SKPhysicsBody, SKAudioNode, Managing Game State | 🔴 |
| 35 | 🛠<br>[Random Numbers](/35-random-numbers) | Int.random(in:), Float.random(in:), Double.random(in:), CGFloat.random(in:), Bool.random(), arc4random(), GKRandomSource.sharedRandom(), GKLinearCongruentialRandomSource, GKMersenneTwisterRandomSource, GKARC4RandomSource, GKRandomDistribution, GKShuffledDistribution, GKGaussianDistribution, Fisher-Yates Algorithm, arrayByShufflingObjects(in:), the importance of being able to seed the source of randomness 🌱| ✅ |
| 36 | 🎮<br>[Crashy Plane](/36-crashy-plane) | Composed Methods, Scale Modes, Parallax Scrolling, SpriteKit Physics, SKPhysicsContactDelegate, SKPhysicsBody, SKAudioNode, Managing Game State | 🚧 |
| 37 | 🎮<br>[Psychic Tester](/37-psychic-tester) | WatchKit Extensions, 3D Touch, CAEmitterLayer, CAGradientLayer, @IBDesignable, @IBInspectable, transition(with:), WCSession, WKInterfaceLabel, WKInterfaceButton | 🔴 |
| 38 | 🛠<br>[Github Commits (Core Data)](/38-githubcommits) | NSFetchRequest, NSManagedObject, NSPredicate, NSSortDescriptor, NSFetchedResultsController, ISO8601DateFormatter | 🔴 |
| 39 | 🛠<br>[Unit testing with XCTest](/39-swift-unit-tests) | XCTest, `filter()`, Test-Driven Development, Functional Programming, XCTestCase, Setting a Baseline, NSCountedSet, XCUIApplication(), XCUIElementQuery, UI Test Recording | 🔴 |