Last active
June 25, 2019 15:36
-
-
Save Dean151/a11444d0be6c8fde094661dbba38c3be to your computer and use it in GitHub Desktop.
Generate Lottery Numbers using CryptoKit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Written by Thomas Durand (c)2019 ; all right reserved | |
// See https://www.thomasdurand.fr/swift/cryptokit/2019/06/25/generating-lottery-numbers-with-cryptokit.html for more context | |
/// Represent one `k among n` operation | |
struct Draw { | |
/// The number of number to draw (ie k) | |
let draw: UInt | |
/// The range of numbers to draw within (ie n) | |
let among: ClosedRange<Int> | |
/// Generate `draw` numbers within `among` | |
func random() -> Set<Int> { | |
var drawable = Set(among), drawnNumbers = Set<Int>() | |
var generator = CryptoKitRandomNumberGenerator() | |
while drawnNumbers.count < draw { | |
guard let drawn = drawable.randomElement(using: &generator) else { | |
break | |
} | |
drawable.remove(drawn) | |
drawnNumbers.insert(drawn) | |
} | |
return drawnNumbers | |
} | |
} | |
struct Lottery { | |
/// The draws for that lottery | |
/// Example draw 5 numbers within 1 and 49 & 1 number within 1 and 10. | |
let draws: [Draw] | |
/// Generate a lottery draw randomly | |
func random() -> [Set<Int>] { | |
return draws.map { $0.random() } | |
} | |
} | |
extension Lottery { | |
static let frenchLoto = Lottery(draws: [ | |
Draw(draw: 5, among: 1...49), | |
Draw(draw: 1, among: 1...10) | |
]) | |
static let euromillion = Lottery(draws: [ | |
Draw(draw: 5, among: 1...50), | |
Draw(draw: 2, among: 1...12) | |
]) | |
} | |
import CryptoKit | |
/// RandomNumberGenerator using CryptoKit | |
struct CryptoKitRandomNumberGenerator: RandomNumberGenerator { | |
mutating func next<T>() -> T where T : FixedWidthInteger, T : UnsignedInteger { | |
// Let generate a symmetric key using CryptoKit. It'll be generated using best cryptographic practices to have strong randomness. | |
// T will be 8 bytes at most (For UInt64) so generating a bits128 key (ie 16 bytes) is more than overkill already | |
let key = SymmetricKey(size: .bits128) | |
// Then we use the generated key to extract a random Unsigned Fixed Width Integer. | |
return key.withUnsafeBytes { (pointer) -> T in | |
// We have a pointer on random bytes generated by our class | |
// Let convert those bytes to T ; in order to get our number. | |
return pointer.bindMemory(to: T.self).baseAddress!.pointee | |
} | |
} | |
} | |
print(Lottery.frenchLoto.random()) | |
print(Lottery.euromillion.random()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment