I'm trying to get this part of the app to work were the user clicks a button and a label prints a randomly generated number between 1-12. I've been able to successfully do that, but I also want it to not repeat any random numbers that have already been printed.
What I've tried doing is putting any printed number into an array, and then checking the array each time it generates a new number.
I've gotten it to work in the Playground, but cannot get it working with a real project.
Here is the code for my project.
var usedNumbers = [Int]()
var randomConv = 0
func randomize() {
lblRandom.text = "\(arc4random_uniform(12) + 1)"
randomConv = Int(lblRandom.text!)!
}
@IBAction func btnRandomPressed(sender: AnyObject) {
randomize()
if usedNumbers.contains(randomConv) {
randomize()
} else {
usedNumbers.append(randomConv)
}
if usedNumbers.count == 12 {
btnRandom.hidden = true
}
}
And here is the code from my playground.
var lblRandom = "\(arc4random_uniform(12) + 1)"
var randomConv = 0
var usedNumbers = [Int]()
func randomize() {
lblRandom = "\(arc4random_uniform(12) + 1)"
randomConv = Int(lblRandom)!
}
repeat {
randomize()
if usedNumbers.contains(randomConv) {
randomize()
} else {
usedNumbers.append(randomConv)
print(lblRandom)
}
} while usedNumbers.count < 12
I´m not quite sure why your code is not working, but try this code instead this works.
var usedNumbers = [Int]()
var randomConv = 0
@IBAction func btnRandomPressed(sender: AnyObject) {
randomConv = Int(arc4random_uniform(12) + 1)
if usedNumbers.contains(randomConv) {
// If you find a duplicate you fire the event again and a new number will be randomized
print("Exists \(randomConv)")
btn_Pressed.sendActionsForControlEvents(.TouchUpInside)
} else {
print(randomConv)
lblTest.text = String(randomConv)
usedNumbers.append(randomConv)
}
}
Update
When the usedNumbers.contains(randomConv)
condition is true
, you can use this row to fire the button event again: btn_Pressed.sendActionsForControlEvents(.TouchUpInside)
- btn_Pressed is your buttons outlet from your Storyboard.
I have updated the code block so that you can see a fully working example.
Update 2, An alternative solution
func randomize(){
repeat {
if (usedNumbers.count == 12){
return
}
randomConv = Int(arc4random_uniform(12) + 1)
} while usedNumbers.contains(randomConv)
usedNumbers.append(randomConv)
lblTest.text = String(randomConv)
}
@IBAction func btnRandomPressed(sender: AnyObject) {
randomize()
}
The system already has a tool for this: GKShuffledDistribution
is one of the many randomization utilities in GameplayKit. The one thing it does differently from other GKRandom
classes is exactly what you ask — it makes sure not to repeat values that have already been "drawn" from its random pool.
Here's an example:
import GameplayKit
let shuffle = GKShuffledDistribution(forDieWithSideCount: 12)
for _ in 1...100 { print(shuffle.nextInt()) }
Use a playground to graph the results and you'll notice that no number ever repeats immediately (no horizontal lines in the graph) and also that no number repeats more than once every 12 "rolls".
If you wanted to implement similar behavior yourself, you could improve over your other attempts (and the other answers thus far) by using something like Set
or Dictionary<Int, Bool>
to store already-used or yet-unused values, as those can respond to contains
without searching the entire data structure.
Another solution:
var availableNumbers = Array(1...12)
var randomConv = 0
@IBAction func btnRandomPressed(sender: AnyObject) {
if availableNumbers.count > 0 {
let randIndex = Int(arc4random_uniform(UInt32(availableNumbers.count)))
randomConv = availableNumbers.removeAtIndex(randIndex)
someLabel.text = "\(randomConv)"
}
}
You begin your program with array of available numbers to draw. Then you choose element of the array randomly by drawing random index and removing the element at said index from the array. It automatically resizes your array and you are sure that you won't draw the same number again.
来源:https://stackoverflow.com/questions/34622567/randomizing-through-number-range