Project 35 --> Project 36
This commit is contained in:
parent
53948e433a
commit
cf6ddd8f5e
|
@ -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)
|
|
@ -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))
|
|
@ -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)
|
|
@ -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>
|
|
@ -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 | 🔴 |
|
||||
|
|
Loading…
Reference in New Issue