How to make a "for" loop on the condition of a number interval Xcode Swift2 - ios

Hey I'm trying to figure out how to tally up soccer goals on the condition that the goal was scored in under 45 minutes, but the func has some slight errors with swift 2. Any help? Thanks!
Code:
var barcelonavsRealMadrid1goals : [String : Int] = ["barcelonaGoal1":21,"RealMadridGoal2":23,"barcelonaGoal3":24,"RealMadridGoal4":27]
func Run() {
var goalCount=0
for (goal,numbers) in barcelonavsRealMadrid1goals{
for(var number in numbers) {
if(number < 45)
goalCount++
}
}

You have an extra for..in loop in there that's not needed:
for(var number in numbers) {
It also has an extraneous ( and ) around it
for var number in numbers {
Here is a working version of your code:
var barcelonavsRealMadrid1goals = ["barcelonaGoal1":21,"RealMadridGoal2":23,"barcelonaGoal3":24,"RealMadridGoal4":27]
func run() -> Int { // functions should start with lower case
var goalCount=0
for (_,numbers) in barcelonavsRealMadrid1goals where numbers < 45 {
goalCount++
}
return goalCount
}
let goalCount = run()
And the functional way would be something like:
let goalCount = goals.reduce(0) {
if $0.1.1 < 45 {
return $0.0 + 1
}
return $0.0
}
With explanation:
var goals = [
"barcelonaGoal1" :21,
"RealMadridGoal2":23,
"barcelonaGoal3" :24,
"RealMadridGoal4":27,
"RealMadridGoal5":45]
// For our use reduce takes an initial value of Int
// and a combine function of type
// (Int, (String, Int)) -> Int
//
// Reduce will call the closure once with
// each value in the map and the previous return value
let goalCount = goals.reduce(0, combine: {
(initial:Int, current:(key:String, value:Int)) -> Int in
var currentCount = initial
// print to show input and output of closure
print( "parameters:(\(initial), (\"\(current.key)\", \(current.value)))", terminator:", ")
defer {
print("return:\(currentCount)")
}
// end printing
if current.value < 45 {
++currentCount // add 1 to the running total
return currentCount
}
return currentCount
})
// console output:
// parameters:(0, ("barcelonaGoal1", 21)), return:1
// parameters:(1, ("RealMadridGoal4", 27)), return:2
// parameters:(2, ("RealMadridGoal5", 45)), return:2
// parameters:(2, ("RealMadridGoal2", 23)), return:3
// parameters:(3, ("barcelonaGoal3", 24)), return:4

For solving of you're problem try to use functional programing that is introduced in swift :
var barcelonavsRealMadrid1goals : [String : Int] = ["barcelonaGoal1":95,"RealMadridGoal2":23,"barcelonaGoal3":24,"RealMadridGoal4":27]
var filtered = barcelonavsRealMadrid1goals.filter { (team:String, minute:Int) -> Bool in
var state = false
if (minute > 45)
{
return true
}
return state
}
let totalCount = filtered.count

Try this method.
func Run() {
var goalCount=0
for (_, score) in barcelonavsRealMadrid1goals {
if(score < 45) {
goalCount++
}
}
print(goalCount)
}

Related

Gradually and randomly visualize string

I am currently working on a simple program to gradually and randomly visualize a string in two iteration. Right now I have managed to get the first iteration but I'm not sure how to do the second one. If someone could give any example or advice I would be very grateful. My code looks like this:
let s = "Hello playground"
let factor = 0.25
let factor2 = 0.45
var n = s.filter({ $0 != " " }).count // # of non-space characters
var m = lrint(factor * Double(n)) // # of characters to display
let t = String(s.map { c -> Character in
if c == " " {
// Preserve space
return " "
} else if Int.random(in: 0..<n) < m {
// Replace
m -= 1
n -= 1
return c
} else {
// Keep
n -= 1
return "_"
}
})
print(t) // h_l__ _l_______d
To clarify, I want to use factor2 in the second iteration to print something that randomly add letters on top of t that looks something like this h_l_o pl_g_____d.
Replacing Characters
Starting from #MartinR's code, you should remember the indices that have been replaced. So, I am going to slightly change the code that replaces characters :
let s = "Hello playground"
let factor = 0.25
let factor2 = 0.45
var n = s.filter({ $0 != " " }).count // # of non-space characters
let nonSpaces = n
var m = lrint(factor * Double(n)) // # of characters to display
var indices = Array(s.indices)
var t = ""
for i in s.indices {
let c = s[i]
if c == " " {
// Preserve space
t.append(" ")
indices.removeAll(where: { $0 == i })
} else if Int.random(in: 0..<n) < m {
// Keep
m -= 1
n -= 1
t.append(c)
indices.removeAll(where: { $0 == i })
} else {
// Replace
n -= 1
t.append("_")
}
}
print(t) //For example: _e___ ______ou_d
Revealing Characters
In order to do that, we should calculate the number of characters that we want to reveal:
m = lrint((factor2 - factor) * Double(nonSpaces))
To pick three indices to reveal randomly, we shuffle indices and then replace the m first indices :
indices.shuffle()
var u = t
for i in 0..<m {
let index = indices[i]
u.replaceSubrange(index..<u.index(after: index), with: String(s[index]))
}
indices.removeSubrange(0..<m)
print(u) //For example: _e__o _l__g_ou_d
I wrote StringRevealer struct, that handle all revealing logic for you:
/// Hide all unicode letter characters as `_` symbol.
struct StringRevealer {
/// We need mapping between index of string character and his position in state array.
/// This struct represent one such record
private struct Symbol: Hashable {
let index: String.Index
let position: Int
}
private let originalString: String
private var currentState: [Character]
private let charactersCount: Int
private var revealed: Int
var revealedPercent: Double {
return Double(revealed) / Double(charactersCount)
}
private var unrevealedSymbols: Set<Symbol>
init(_ text: String) {
originalString = text
var state: [Character] = []
var symbols: [Symbol] = []
var count = 0
var index = originalString.startIndex
var i = 0
while index != originalString.endIndex {
let char = originalString[index]
if CharacterSet.letters.contains(char.unicodeScalars.first!) {
state.append("_")
symbols.append(Symbol(index: index, position: i))
count += 1
} else {
state.append(char)
}
index = originalString.index(after: index)
i += 1
}
currentState = state
charactersCount = count
revealed = 0
unrevealedSymbols = Set(symbols)
}
/// Current state of text. O(n) conplexity
func text() -> String {
return currentState.reduce(into: "") { $0.append($1) }
}
/// Reveal one random symbol in string
mutating func reveal() {
guard let symbol = unrevealedSymbols.randomElement() else { return }
unrevealedSymbols.remove(symbol)
currentState[symbol.position] = originalString[symbol.index]
revealed += 1
}
/// Reveal random symbols on string until `revealedPercent` > `percent`
mutating func reveal(until percent: Double) {
guard percent <= 1 else { return }
while revealedPercent < percent {
reveal()
}
}
}
var revealer = StringRevealer("Hello товарищ! 👋")
print(revealer.text())
print(revealer.revealedPercent)
for percent in [0.25, 0.45, 0.8] {
revealer.reveal(until: percent)
print(revealer.text())
print(revealer.revealedPercent)
}
It use CharacterSet.letters inside, so most of languages should be supported, emoji ignored and not-alphabetic characters as well.

Get a random unique element from an Array until all elements have been picked in Swift

I have the following array:
var notebookCovers = ["cover1", "cover2", "cover3", "cover4", "cover4", "cover6", "cover7", "cover8", "cover9", "cover10"]
and a UIButton that when it's pressed it generates a new UIImage with one of the elements of the array.
What I need to do is every time the button is tapped to generate random but unique element from the array (without repeating the elements) until they've all been selected and then restart the array again.
So far, I have it getting a random element but it's repeated and I cannot figure out how to it so it gets a unique image every time
func createNewNotebook() {
let newNotebook = Notebook()
let randomInt = randomNumber()
newNotebook.coverImageString = notebookCovers[randomInt]
notebooks.insert(newNotebook, at: 0)
collectionView.reloadData()
}
func randomNumber() -> Int {
var previousNumber = arc4random_uniform(UInt32(notebookCovers.count))
var randomNumber = arc4random_uniform(UInt32(notebookCovers.count - 1))
notebookCovers.shuffle()
if randomNumber == previousNumber {
randomNumber = UInt32(notebookCovers.count - 1)
}
previousNumber = randomNumber
return Int(randomNumber)
}
Set is a collection type that holds unique elements. Converting your notebooks array to Set also lets you take advantage of its randomElement function
var aSet = Set(notebooks)
let element = aSet.randomElement()
aSet.remove(element)
Copy the array. Shuffle the copy. Now just keep removing the first element until the copy is empty. When it is empty, start over.
Example:
let arr = [1,2,3,4,5]
var copy = [Int]()
for _ in 1...30 { // just to demonstrate what happens
if copy.isEmpty { copy = arr; copy.shuffle() }
let element = copy.removeFirst() ; print(element, terminator:" ")
}
// 4 2 3 5 1 1 5 3 2 4 4 1 2 3 5 1 4 5 2 3 3 5 4 2 1 3 2 4 5 1
If you want to create a looping solution:
let originalSet = Set(arrayLiteral: "a","b","c")
var selectableSet = originalSet
func repeatingRandomObject() -> String {
if selectableSet.isEmpty {
selectableSet = originalSet
}
return selectableSet.remove(selectableSet.randomElement()!)!
}
force unwrapping is kind of safe here, since we know that the result will never be nil. If you don't want to force unwrap:
let originalSet = Set(arrayLiteral: "a","b","c")
var selectableSet = originalSet
func repeatingRandomObject() -> String? {
if selectableSet.isEmpty {
selectableSet = originalSet
}
guard let randomElement = selectableSet.randomElement() else { return nil }
return selectableSet.remove(randomElement)
}
You can try something like this,
var notebookCovers = ["cover1", "cover2", "cover3", "cover4", "cover4", "cover6", "cover7", "cover8", "cover9", "cover10"]
var tappedNotebooks: [String] = []
func tapping() {
let notebook = notebookCovers[Int.random(in: 0...notebookCovers.count - 1)]
if tappedNotebooks.contains(notebook){
print("already exists trying again!")
tapping()
} else {
tappedNotebooks.append(notebook)
print("appended", notebook)
}
if tappedNotebooks == notebookCovers {
tappedNotebooks = []
print("cleared Tapped notebooks")
}
}
It is possible with shuffle:
struct AnantShuffler<Base: MutableCollection> {
private var collection: Base
private var index: Base.Index
public init?(collection: Base) {
guard !collection.isEmpty else {
return nil
}
self.collection = collection
self.index = collection.startIndex
}
public mutating func next() -> Base.Iterator.Element {
let result = collection[index]
collection.formIndex(after: &index)
if index == collection.endIndex {
collection.shuffle()
index = collection.startIndex
}
return result
}
}
fileprivate extension MutableCollection {
/// Shuffles the contents of this collection.
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
let i = index(firstUnshuffled, offsetBy: d)
swapAt(firstUnshuffled, i)
}
}
}
Use:
let shuffler = AnantShuffler(collection: ["c1","c2","c3"].shuffled())
shuffler?.next()

Swift Anagram checker

I am attempting to build an anagram checker for swift. This is my code. In case you don't know an anagram checker checks if two strings have the same characters in them but, order does not matter.
func checkForAnagram(#firstString: String, #secondString: String) -> Bool {
var firstStringArray: [Character] = []
var secondStringArray: [Character] = []
/* if case matters delete the next four lines
and make sure your variables are not constants */
var first = firstString
var second = secondString
first = first.lowercaseString
second = second.lowercaseString
for charactersOne in first {
firstStringArray += [charactersOne]
}
for charactersTwo in second {
secondStringArray += [charactersTwo]
}
if firstStringArray.count != secondStringArray.count {
return false
} else {
for elements in firstStringArray {
if secondStringArray.contains(elements){
return true
} else {
return false
}
}
}
}
var a = "Hello"
var b = "oellh"
var c = "World"
checkForAnagram(firstString: a, secondString: b)
I am getting an error message of.
'[Character]' does not have a member 'contains'
The accepted answer is compact and elegant, but very inefficient if compared to other solutions.
I'll now propose and discuss the implementation of a few variants of anagram checker. To measure performance, I'll use the different variants to find the anagrams of a given word out of an array of 50,000+ words.
// Variant 1: Sorting of Character
// Measured time: 30.46 s
func anagramCheck1(a: String, b: String) -> Bool {
return a.characters.sorted() == b.characters.sorted()
}
This is essentially the solution of the accepted answer, written in Swift 3 syntax. It's very slow because Swift's String, unlike NSString, is based on Character, which handles Unicode characters properly.
A more efficient solution exploits the NSCountedSet class, which allows us to represent a string as a set of characters, each with its own count. Two strings are anagrams if they map to the same NSCountedSet.
Note: checking string lengths as a precondition makes the implementation always more efficient.
// Variant 2: NSCountedSet of Character
// Measured time: 4.81 s
func anagramCheck2(a: String, b: String) -> Bool {
guard a.characters.count == b.characters.count else { return false }
let aSet = NSCountedSet()
let bSet = NSCountedSet()
for c in a.characters {
aSet.add(c)
}
for c in b.characters {
bSet.add(c)
}
return aSet == bSet
}
Better but not excellent. Here, one of the "culprits" is the use of the native Swift Character type (from Swift's String). Moving back to good old Objective-C types (NSString and unichar) makes things more efficient.
// Variant 3: NSCountedSet of unichar
// Measured time: 1.31 s
func anagramCheck3(a: String, b: String) -> Bool {
let aString = a as NSString
let bString = b as NSString
let length = aString.length
guard length == bString.length else { return false }
let aSet = NSCountedSet()
let bSet = NSCountedSet()
for i in 0..<length {
aSet.add(aString.character(at: i))
bSet.add(bString.character(at: i))
}
return aSet == bSet
}
Using NSCountedSet is fine, but before we compare two NSCountedSet objects, we fully populate them. A useful alternative is to fully populate the NSCountedSet for only one of the two strings, and then, while we populate the NSCountedSet for the other string, we fail early if the other string contains a character that is not found in the NSCountedSet of the first string.
// Variant 4: NSCountedSet of unichar and early exit
// Measured time: 1.07 s
func anagramCheck4(a: String, b: String) -> Bool {
let aString = a as NSString
let bString = b as NSString
let length = aString.length
guard length == bString.length else { return false }
let aSet = NSCountedSet()
let bSet = NSCountedSet()
for i in 0..<length {
aSet.add(aString.character(at: i))
}
for i in 0..<length {
let c = bString.character(at: i)
if bSet.count(for: c) >= aSet.count(for: c) {
return false
}
bSet.add(c)
}
return true
}
This is about the best timing we are going to get (with Swift). However, for completeness, let me discuss one more variant of this kind.
The next alternative exploits a Swift Dictionary of type [unichar: Int] to store the number of repetitions for each character instead of NSCountedSet. It's slightly slower than the previous two variants, but we can reuse it later to obtain a faster implementation.
// Variant 5: counting repetitions with [unichar:Int]
// Measured time: 1.36
func anagramCheck5(a: String, b: String) -> Bool {
let aString = a as NSString
let bString = b as NSString
let length = aString.length
guard length == bString.length else { return false }
var aDic = [unichar:Int]()
var bDic = [unichar:Int]()
for i in 0..<length {
let c = aString.character(at: i)
aDic[c] = (aDic[c] ?? 0) + 1
}
for i in 0..<length {
let c = bString.character(at: i)
let count = (bDic[c] ?? 0) + 1
if count > aDic[c] ?? 0 {
return false
}
bDic[c] = count
}
return true
}
Note that a vanilla Objective-C implementation using NSCountedSet, corresponding to Variant 3, is faster than all the previous versions by a rather large margin.
// Variant 6: Objective-C and NSCountedSet
// Measured time: 0.65 s
- (BOOL)anagramChecker:(NSString *)a with:(NSString *)b {
if (a.length != b.length) {
return NO;
}
NSCountedSet *aSet = [[NSCountedSet alloc] init];
NSCountedSet *bSet = [[NSCountedSet alloc] init];
for (int i = 0; i < a.length; i++) {
[aSet addObject:#([a characterAtIndex:i])];
[bSet addObject:#([b characterAtIndex:i])];
}
return [aSet isEqual:bSet];
}
Another way we can improve upon the previous attempts is to observe that, if we need to find the anagram of a given word, we might as well consider that word as fixed, and thus we could build the corresponding structure (NSCountedSet, Dictionary, ...) for that word only once.
// Finding all the anagrams of word in words
// Variant 7: counting repetitions with [unichar:Int]
// Measured time: 0.58 s
func anagrams(word: String, from words: [String]) -> [String] {
let anagrammedWord = word as NSString
let length = anagrammedWord.length
var aDic = [unichar:Int]()
for i in 0..<length {
let c = anagrammedWord.character(at: i)
aDic[c] = (aDic[c] ?? 0) + 1
}
let foundWords = words.filter {
let string = $0 as NSString
guard length == string.length else { return false }
var bDic = [unichar:Int]()
for i in 0..<length {
let c = string.character(at: i)
let count = (bDic[c] ?? 0) + 1
if count > aDic[c] ?? 0 {
return false
}
bDic[c] = count
}
return true
}
return foundWords
}
Now, in the previous variant we have counted with a [unichar:Int] Dictionary. This proves slightly more efficient than using an NSCountedSet of unichar, either with early exit (0.60 s) or without (0.87 s).
You should try
func checkForAnagram(firstString firstString: String, secondString: String) -> Bool {
return firstString.lowercaseString.characters.sort() == secondString.lowercaseString.characters.sort()
}
func checkAnagrams(str1: String, str2: String) -> Bool {
guard str1.count == str2.count else { return false }
var dictionary = Dictionary<Character, Int>()
for index in 0..<str1.count {
let value1 = str1[str1.index(str1.startIndex, offsetBy: index)]
let value2 = str2[str2.index(str2.startIndex, offsetBy: index)]
dictionary[value1] = (dictionary[value1] ?? 0) + 1
dictionary[value2] = (dictionary[value2] ?? 0) - 1
}
return !dictionary.contains(where: {(_, value) in
return value != 0
})
}
Time complexity - O(n)
// Make sure name your variables correctly so you won't confuse
// Mutate the constants parameter, lowercase to handle capital letters and the sorted them to compare both. Finally check is there are equal return true or false.
func anagram(str1: String, srt2: String)->Bool{
let string1 = str1.lowercased().sorted()
let string2 = srt2.lowercased().sorted()
if string1 == string2 {
return true
}
return false
}
// This answer also would work
// Convert your parameters on Array, then sorted them and compare them
func ana(str1: String, str2: String)->Bool{
let a = Array(str1)
let b = Array(str2)
if a.sorted() == b.sorted() {
return true
}
return false
}
Don't forget whitespaces
func isAnagram(_ stringOne: String, stringTwo: String) -> Bool {
return stringOne.lowercased().sorted().filter { $0 != " "} stringTwo.lowercased().sorted().filter { $0 != " "}
}
Swift 4.1 Function will give you 3 questions answer for Anagram :-
1. Input Strings (a,b) are Anagram ? //Bool
2. If not an Anagram then number of count require to change Characters in strings(a,b) to make them anagram ? // Int
3. If not an Anagram then list of Characters needs to be change in strings(a,b) to make them anagram ? // [Character]
STEP 1:- Copy and Paste below function in to your required class:-
//MARK:- Anagram checker
func anagramChecker(a:String,b:String) -> (Bool,Int,[Character]) {
var aCharacters = Array(a)
var bCharacters = Array(b)
var count = 0
var isAnagram = true
var replacementRequiredWords:[Character] = [Character]()
if aCharacters.count == bCharacters.count {
let listA = aCharacters.filter { !bCharacters.contains($0) }
for i in 0 ..< listA.count {
if !replacementRequiredWords.contains(listA[i]) {
count = count + 1
replacementRequiredWords.append(listA[i])
isAnagram = false
}
}
let listB = bCharacters.filter { !aCharacters.contains($0) }
for i in 0 ..< listB.count {
if !replacementRequiredWords.contains(listB[i]) {
count = count + 1
replacementRequiredWords.append(listB[i])
isAnagram = false
}
}
}else{
//cant be an anagram
count = -1
}
return (isAnagram,count,replacementRequiredWords)
}
STEP 2 :- Make two Input Strings for test
// Input Strings
var a = "aeb"
var b = "abs"
STEP 3:- Print results :-
print("isAnagram : \(isAnagram(a: a, b: b).0)")
print("number of count require to change strings in anagram : \(isAnagram(a: a, b: b).1)")//-1 will come in case of cant be a Anagram
print("list of Characters needs to be change : \(isAnagram(a: a, b: b).2)")
Results of above exercise:-
isAnagram : false
number of count require to change strings in anagram : 2
list of Characters needs to be change : ["e", "s"]
Hope this 10 minutes exercise will give some support to my Swift
family for solving Anagram related problems easily. :)
We can use dictionary to construct a new data structure container. Then compare the value by key/character of the string.
func anagram(str1: String, str2 : String) -> Bool {
var dict1 = [Character: Int]()
var dict2 = [Character:Int]()
for i in str1 {
if let count = dict1[i] {
dict1[i] = count + 1
} else {
dict1[i] = 1
}
}
for j in str2 {
if let count = dict2[j] {
dict2[j] = count + 1
} else {
dict2[j] = 1
}
}
return dict1 == dict2 ? true : false
}
// input -> "anna", "aann"
// The count will look like:
// ["a": 2, "n": 2] & ["a": 2, "n": 2]
// then return true
Another easy that I just realise doing an Anagram function in Swift 5.X
func checkForAnagram(firstString firstString: String, secondString: String) -> Bool {
return !firstString.isEmpty && firstString.sorted() == secondString.sorted()
}
class Solution {
func isAnagram(_ s: String, _ t: String) -> Bool {
guard s.count == t.count else { return false }
let dictS = s.reduce(into: [Character: Int]()) { $0[$1, default: 0] += 1 }
let dictT = t.reduce(into: [Character: Int]()) { $0[$1, default: 0] += 1 }
for letter in s {
if let count = dictS[letter] {
guard count == dictT[letter] else { return false }
}
}
return true
}
}
Check two strings are anagram using inout method in Swift
func checkAnagramString(str1: inout String, str2: inout String)-> Bool{
var result:Bool = false
str1 = str1.lowercased().trimmingCharacters(in: .whitespace)
str2 = str2.lowercased().trimmingCharacters(in: .whitespaces)
if (str1.count != str2.count) {
return result
}
for c in str1 {
if str2.contains(c){
result = true
}
else{
result = false
return result
}
}
return result
}
Call function to check strings are anagram or not
var str1 = "tommarvoloriddle"
var str2 = "iamlordvoldemort"
print(checkAnagramString(str1: &str1, str2: &str2)) //Output = true.
func isAnagram(word1: String, word2: String) -> Bool {
let set1 = Set(word1)
let set2 = Set(word2)
return set1 == set2
}
or
func isAnagram(word1: String,word2: String) -> Bool {
return word1.lowercased().sorted() == word2.lowercased().sorted()
}

How to make computer generate predefined set of numbers in Swift?

I want to make the computer generate predefined set of numbers one-by-one. How is it possible in Swift? Is it possible using arrays?
For example: I want to generate [1,2,3,4] in that same order one-by-one. By generate I mean to show up like how random numbers show up when you type in arcrandom_uniform().
Example:
var randomNumber = [1,2,3,4,5]
var guessInt = guess.text.toInt()
Then when I type ..
if Int(randomNumber) == guessInt {
//conditions
}
The error shows as : Cannot assgn to the result of ths expresson
Sometimes I get the error as Int' is not convertible to '[Int]' swift
The code won't work! Help ?
Here's the code for #David Skrundz :
` #IBAction func guessButton(sender: AnyObject) {
var randomNumber = [1,2,3,4,7,8,9,5]
var guessInt = guess.text.toInt()
func generator() -> () -> Int {
var currentIndex = 0
let array = Array(1...5)
func generate() -> Int {
let value = array[currentIndex++]
if currentIndex >= array.count {
currentIndex = 0
}
return value
}
return generate
}
let gen = generator()
gen() // Returns 1
gen() // Returns 2
gen() // Returns 3
gen() // Returns 4
gen() // Returns 1
let answer = gen()
if answer == guessInt {
resultLabel.text = "Bingoo..! "}
else {
resultLabel.text = "Oh such a bad guesser! It was a \(answer)"
}
println(answer)
}
`
Any idea how to fix this code the problem mentioned below ! ? Please !
You may want the range operator:
for index in 1...5 {
println("\(index)")
}
If you want to generate the next element in a repeating sequence, you could use
func generator() -> () -> Int {
var currentIndex = 0
let array = Array(1...4)
func generate() -> Int {
let value = array[currentIndex++]
if currentIndex >= array.count {
currentIndex = 0
}
return value
}
return generate
}
And to use it:
let gen = generator()
gen() // Returns 1
gen() // Returns 2
gen() // Returns 3
gen() // Returns 4
gen() // Returns 1

I want multiple non-random numbers in swift

I am using swift and want to have a number of duplicatable patterns throughout my game.
Ideally I would have some sort of shared class that worked sort of like this (this is sort of pseudo-Swift code):
class RandomNumberUtility {
static var sharedInstance = RandomNumberUtility()
var random1 : Random()
var random2 : Random()
func seedRandom1(seed : Int){
random1 = Random(seed)
}
func seedRandom2(seed : Int){
random2 = Random(seed)
}
func getRandom1() -> Int {
return random1.next(1,10)
}
func getRandom2() -> Int {
return random2.next(1,100)
}
}
Then, to begin the series, anywhere in my program I could go like this:
RandomNumberUtility.sharedInstance.seedNumber1(7)
RandomNumberUtility.sharedInstance.seedNumber2(12)
And then I would know that (for example) the first 4 times I called
RandomNumberUtility.sharedInstance.getRandom1()
I would always get the same values (for example: 6, 1, 2, 6)
This would continue until at some point I seeded the number again, and then I would either get the exact same series back (if I used the same seed), or a different series (if I used a different seed).
And I want to have multiple series of numbers (random1 & random2) at the same time.
I am not sure how to begin to turn this into an actual Swift class.
Here is a possible implementation. It uses the jrand48 pseudo random number generator,
which produces 32-bit numbers.
This PRNG is not as good as arc4random(), but has the advantage
that all its state is stored in a user-supplied array, so that multiple
instances can run independently.
struct RandomNumberGenerator {
// 48 bit internal state for jrand48()
private var state : [UInt16] = [0, 0, 0]
// Return pseudo-random number in the range 0 ... upper_bound-1:
mutating func next(upper_bound: UInt32) -> UInt32 {
// Implementation avoiding the "module bias" problem,
// taken from: http://stackoverflow.com/a/10989061/1187415,
// Swift translation here: http://stackoverflow.com/a/26550169/1187415
let range = UInt32.max - UInt32.max % upper_bound
var rnd : UInt32
do {
rnd = UInt32(truncatingBitPattern: jrand48(&state))
} while rnd >= range
return rnd % upper_bound
}
mutating func seed(newSeed : Int) {
state[0] = UInt16(truncatingBitPattern: newSeed)
state[1] = UInt16(truncatingBitPattern: (newSeed >> 16))
state[2] = UInt16(truncatingBitPattern: (newSeed >> 32))
}
}
Example:
var rnd1 = RandomNumberGenerator()
rnd1.seed(7)
var rnd2 = RandomNumberGenerator()
rnd2.seed(12)
println(rnd1.next(10)) // 2
println(rnd1.next(10)) // 8
println(rnd1.next(10)) // 1
println(rnd2.next(10)) // 6
println(rnd2.next(10)) // 0
println(rnd2.next(10)) // 5
If rnd1 is seeded with the same value as above then it
produces the same numbers again:
rnd1.seed(7)
println(rnd1.next(10)) // 2
println(rnd1.next(10)) // 8
println(rnd1.next(10)) // 1
What you need is a singleton that generates pseudo-random numbers and make sure all your code that need a random number call via this class. The trick is to reset the seed for each run of your code. Here is a simple RandomGenerator class that will do the trick for you (it's optimized for speed which is a good thing when writing games):
import Foundation
// This random number generator comes from: Klimov, A. and Shamir, A.,
// "A New Class of Invertible Mappings", Cryptographic Hardware and Embedded
// Systems 2002, http://dl.acm.org/citation.cfm?id=752741
//
// Very fast, very simple, and passes Diehard and other good statistical
// tests as strongly as cryptographically-secure random number generators (but
// is not itself cryptographically-secure).
class RandomNumberGenerator {
static let sharedInstance = RandomNumberGenerator()
private init(seed: UInt64 = 12347) {
self.seed = seed
}
func nextInt() -> Int {
return next(32)
}
private func isPowerOfTwo(x: Int) -> Bool { return x != 0 && ((x & (x - 1)) == 0) }
func nextInt(max: Int) -> Int {
assert(!(max < 0))
// Fast path if max is a power of 2.
if isPowerOfTwo(max) {
return Int((Int64(max) * Int64(next(31))) >> 31)
}
while (true) {
var rnd = next(31)
var val = rnd % max
if rnd - val + (max - 1) >= 0 {
return val
}
}
}
func nextBool() -> Bool {
return next(1) != 0
}
func nextDouble() -> Double {
return Double((Int64(next(26)) << 27) + Int64(next(27))) /
Double(Int64(1) << 53)
}
func nextInt64() -> Int64 {
let lo = UInt(next(32))
let hi = UInt(next(32))
return Int64(UInt64(lo) | UInt64(hi << 32))
}
func nextBytes(inout buffer: [UInt8]) {
for n in 0..<buffer.count {
buffer[n] = UInt8(next(8))
}
}
var seed: UInt64 {
get {
return _seed
}
set(seed) {
_initialSeed = seed
_seed = seed
}
}
var initialSeed: UInt64 {
return _initialSeed!
}
private func randomNumber() -> UInt32 {
_seed = _seed &+ ((_seed &* _seed) | 5)
return UInt32(_seed >> 32)
}
private func next(bits: Int) -> Int {
assert(bits > 0)
assert(!(bits > 32))
return Int(randomNumber() >> UInt32(32 - bits))
}
private var _initialSeed: UInt64?
private var _seed: UInt64 = 0
}

Resources