Basic Xcode - reference in random number generator not working - ios

I am working on a very basic (I think) program in Xcode. I am trying to write an app for "drawing straws" where the last person who is chosen is the loser. There are a few things I'd like to do. First, see the code below:
import UIKit
let players = 4
var playerNames: [String] = ["John", "Tyler", "Pete", "Dave"]
var draw = Int(arc4random_uniform(4))
playerNames.removeAtIndex(draw)
print(playerNames)
let round2 = playerNames.count
var draw2 = Int(arc4random_uniform(2))
playerNames.removeAtIndex(draw2)
print(playerNames)
let round3 = playerNames.count
var draw3 = Int(arc4random_uniform(1))
playerNames.removeAtIndex(draw3)
print(playerNames)
The first thing that's wrong is I'm currently hard-coding the random integer being drawn in var draw = Int(arc4random_uniform(4)). When I try to reference players instead of just typing in 4, I get an error. Can someone please help explain the problem there?
I'll stop there for now to see if I can fix that, and I'll wait until that is fixed before posting a new question. Thank you.

The function arc4random_uniform() takes a UInt32, so you need to declare the variable players as UInt32
let players: UInt32 = 4
var draw = (arc4random_uniform(players))
For handling the round variables, you would cast the count to UInt32.
let round2 = UInt32(playerNames.count)
You can also refactor your code
let players: UInt32 = 4
var playerNames: [String] = ["John", "Tyler", "Pete", "Dave"]
println(playerNames)
for loop in 1...players {
RemovePlayer(&playerNames)
println(playerNames)
}
And the code for the RemovePlayer function
func RemovePlayer(inout names: [String]) {
// get the number of names in the array
var maxNames = UInt32(names.count)
var draw = Int((arc4random_uniform(maxNames)))
names.removeAtIndex(draw)
}

As Blackfrog already stated your randomization function requires a UInt32
Since you stated that you just started programing I would like to give you some other advice. Your code can be written as:
var playerNames = ["John", "Tyler", "Pete", "Dave"]
for var index = playerNames.count ; index > 1 ; index -= 1{
var random:Int = Int(arc4random_uniform(UInt32(index)))
playerNames.removeAtIndex(random)
}
println(playerNames[0])
This has a few advantages. At the moment you are hardcoding the values for how many players join a game. You will need to re-write an unnecessary amount of code if you want to add a player.
Let’s see what the above code does:
We start by declaring the playerNames adding a type declaration of [String] isn’t necessary, swift already knows that it is this type by its initial value.
for var index = playerNames.count; index > 1 ; index -= 1{
We create a new variable called index this will be set to the amount of items in the playerNames array (4 in this case)
After this we will declare that we want the loop as long as our index is greater than 1 this will make it run 3 times and make sure we are left with 1 item in our array
The index -=1 will subtract 1 from the index after each iteration through the loop.
var random:Int = Int(arc4random_uniform(UInt32(index)))
Here we declare a new variable random this will be of type Integer. Let’s work from the inside to the outside. UIt32(index) will convert our index which is of type Int to a type of UInt32, this is needed cause our random function requires a UInt32
Next up we request a random value which will lay between the index and 0 (thus between the bounds of the array). We want to remove the player who belongs to this random value in our array. To do this we need to convert our random Uint32 back to an Int we can do this by using Int().
Next we remove the player at the index using
playerNames.removeAtIndex(random)
Lastly we print the first (and only) item left in the array using
println(playerNames[0])

Related

Swift: how to always get different value from shuffled array [duplicate]

This question already has answers here:
Get a random unique element from an Array until all elements have been picked in Swift
(5 answers)
Closed 3 years ago.
I created an array of words basically inside of a button, and each time I press my button I get a random item from the array. Now sometimes I get identical items. What if I don't want my items to repeat themselves and I always want to get new items? (Obviously I even want them to repeat their cycle after they all have ben showed once).
#IBOutlet weak var shoppingLabel : UILabel!
#IBAction func shoppingListButton(_ sender: Any) {
var shoppingList = ["Oranges","Apples","Broccoli"].shuffled()
print(shoppingList)
resultLabel.text = shoppingList.first ?? ""
}
this is not a duplicate as the similar question has an array outside of the button and is a var array, mine is a let. With my array I'm unable to remove items from it because it can't be changed, and no, I can't make it a var array...
To cycle through a random array:
Create the array
Shuffle it once
Pick values by cycling through the array from the beginning to the end
To achieve 1) and 2) simply define the array as a constant and shuffle it outside the method in which you want to use it.
To achieve 3) create an additional variable to keep track of which index of the array you are currently at, and increment it after picking a value.
To make sure you don't go beyond the bounds of the array and to achieve the "cycling" through of the array, reset the index to 0 when the index becomes greater than the last index of the array. A simple way to do this, is to use the remainder operator % in Swift.
E.g.
let shoppingList = ["Oranges", "Apples", "Broccoli"].shuffled()
var currentIndex = 0
#IBAction func shoppingListButton(_ sender: Any) {
// pick an item
let nextItem = shoppingList[currentIndex]
// update the label
resultLabel.text = nextItem
// increment the index to cycle through items
currentIndex = (currentIndex + 1) % shoppingList.count
}
To pick random non-repeating values from an array:
Create the array
Pick a random value from the array
If the picked value equals the last value, pick a new one
To achieve 2) use the randomElement() function to pick a random element. This is less computationally expensive than shuffling the entire array and picking the first element each time.
To achieve 3) use a while loop or similar to keep picking random elements until a new one is generated.
E.g.
let shoppingList = ["Oranges", "Apples", "Broccoli"]
#IBAction func shoppingListButton(_ sender: Any) {
// pick a random element that is not equal to the last one
var nextItem: String?
repeat {
nextItem = shoppingList.randomElement()
} while nextItem == resultLabel.text
// update the label
resultLabel.text = nextItem
}

Swift: Getting EXC_BAD_INSTRUCTION when trying to append random array elements

I am getting the error, "Thread 1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0x0)" when trying to append random array elements into a new array.
The debug log says "fatal error: Index out of range"
//If there are more than 6 players prioritizing the event, make a random choice. garudaLocations is an array containing the players who prioritized the event "Garuda".
if garudaLocations.count > 6 {
var finalGarudaPlayers : [Int] = []
let getRandom = randomSequenceGenerator(1, max: garudaLocations.count) //Tell RNG how many numbers it has to pick from.
var randomGarudaPrioritiesIndex = Int()
for _ in 1...6 {
randomGarudaPrioritiesIndex = getRandom() //Generate a random number.
finalGarudaPlayers.append(garudaLocations[randomGarudaPrioritiesIndex]) //ERROR: Thread 1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0x0)
}
debugPrint(finalGarudaPlayers) //Print array with the final priority Garuda members.
randomSequenceGenerator is a function I got from here, which does work to generate the random numbers.
func randomSequenceGenerator(min: Int, max: Int) -> () -> Int {
var numbers: [Int] = []
return {
if numbers.count == 0 {
numbers = Array(min ... max)
}
let index = Int(arc4random_uniform(UInt32(numbers.count)))
return numbers.removeAtIndex(index)
}
}
To get a better understanding, I am trying to write a piece of a "team making" program where players are automatically sorted into events, but they can choose which events they would like to prioritize.
I can only have 6 people per event, however, so the goal is to take the existing garudaLocations array, choose a random 6 index locations, and get rid of the rest of the players.
I get the error only after I submit more than 6 players to the same event.
Any help is very much appreciated!
You can never speak of a non-existent index. If you do, you will crash just as you are crashing now.
So, you are saying:
garudaLocations[randomGarudaPrioritiesIndex]
Now, I do not know what garudaLocations is. But I can tell you for certain that if randomGarudaPrioritiesIndex is not an existing index within garudaLocations, you will absolutely crash.
Thus, you can easily debug this by logging (print) randomGarudaPrioritiesIndex.
Bear in mind that the largest existing index is not garudaLocations[garudaLocations.count]. It is garudaLocations[garudaLocations.count-1]. So compare randomGarudaPrioritiesIndex to garudaLocations.count-1. If it is greater, you crash when you use it as an index on garudaLocations.

Putting array into label in swift

#IBAction func generateBtn(sender: UIButton) {
let strt = UInt32(strtNum.text!)
let end = UInt32(endNum.text!)
let ttlNums = Int(amtNums.text!)
let x = RandomNum()
var z = 0
while z<ttlNums{
let y = x.rndNumGen(strt!, end: end!)
z += 1
var h = [String]()
h.append(String(y))
let display:String = h.joinWithSeparator(", ")
winningNums.text = display
print (display)
}
}
I don't know what is wrong with this code. I am trying to put the string display into the label and it prints out the last number from the random number generator. When i print it to the console it shows all of the random numbers.
The primary issue here is that your array is created fresh in every loop iteration, and your label is being set in every loop iteration. That means that the array will only ever contain the element made in that iteration, after which it's reset to a new array, and a new element is added. The array needs to be initialized once at the start, and have elements added to it repeatedly in the loop, then put into the label once at the end.
#IBAction func generateBtn(sender: UIButton) {
guard let startText = strtNum.text, let start = UInt32(startText),
let endText = endNum.text, let end = UInt32(endText),
let ttlText = amtNums.text, let ttlNums = UInt32(ttlText) else {
//one of these is nil, handle it gracefully here
return
}
let randomGenerator = RandomNum()
var h = [String]()
h.reserveCapacity(ttlNums)
for _ in 0..<ttlNums {
let randomNum = randomGenerator.rndNumGen(start, end: end)
h.append(String(RandomNum))
}
let display = h.joinWithSeparator(", ")
winningNums.text = display
print(display)
}
I've made a few other changes to bring this code in line with Swift best practices and conventions:
Don't force unwrap. Use an if let or guard let binding to safely handle nil values.
Give your variables meaningful names. Avoid single-letter names except in specific instances.
Don't put spaces beside a function/method name and the proceeding brackets.
Don't use a while loop to iterate over a known range. Instead, use a for in loop.
Dn't type in t3xtspk, it mks ur code look lik an angsty teenagr wrote it. Autocomplete will finish off words for you, so you barely end up typing anyway. Make it easy and readable.
I would suggest you make a few changes yourself:
Rename generateBtn. Functions/methods DO things, they're actions. They should be named with verbs, or verb phrases. Perhaps try something like displayRandomArray.
Refactor the random array generation into its own method.
Rename RandomNum. By the looks of it, it's not a number at all, it's a random number generator. Perhaps try RandomNumberGenerator
Rename h.
Add code to deal with what happens when the .text is nil, or what happens when it contains a string that isn't a UInt32 (thus causing the UInt32 initializer to fail and return nil)

Multidimensional Array Looping in cellForRowAtIndexPath Swift

I have a multidimensional array that I want to display the values of onto one UILabel in each respective cell.
My arrays look like this:
var arrayExample = [["beverages", "food", "suppliers"]["other stuff, "medicine"]]
I'm looping through these values in the cellForRowAtIndexPath in order for it to display on different cells (on a UILabel) the appropriate values:
if let onTheLabel: AnyObject = arrayOfContactsFound as? AnyObject {
for var i = 0; i < objects!.count; i++ {
cell?.contactsUserHas.text = "\(onTheLabel[indexPath.row][i])" as! String
print("arrayOfContactsFound Printing! \(onTheLabel)")
}
}
When printing to the console I get:
arrayOfContactsFound Printing! (
(
beverages,
"supply chain",
pharmacuticals
)
)
But on my label I get "beverages". That's it. How can I get the other 2 values (or X amount if there are more or less than 3 values)?
My for in loop is obviously not doing the trick. Assuming I can optimize/fix that to display all the values?
Thanks in advance.
In your loop you're setting the text of your label multiple times. Each time you set it it doesn't accumulate, it completely replaces the current text with the new text. You'll want something like this:
// Remove the cast and the loop in your code example, and replace with this
let items = arrayOfContactsFound[indexPath.row]
let itemsString = items.joinWithSeparator(" ")
cell?.contactsUserHas.text = itemsString
Another thing to note is your cast doesn't quite make a lot of sense.
var arrayExample = [["beverages", "food", "suppliers"]["other stuff, "medicine"]]
So arrayExample is of type [[String]]. I'm assuming each cell in your table view represents one of the arrays of strings in your array. So each cell represents one [String]. So your items should be arrayExample[indexPath.row]. The cast to AnyObject doesn't make too much sense. If anything you'd be casting it to [[AnyObject]], but there's no reason to because the compiler should already know it's [[String]].

Swift convert object that is NSNumber to Double

I have this code in Swift and it works, but I would think there is a better way to get my object from NSNumber and convert it to a Double:
var rating: NSNumber
var ratingDouble: Double
rating = self.prodResult?.prodsInfo.prodList[indexPath.row].avgRating as NSNumber!!
ratingDouble = Double(rating.doubleValue)
Update
Swift's behavior here has changed quite a bit since 1.0. Not that it was that easy before, but Swift has made it harder to convert between number types because it wants you to be explicit about what to do with precision loss. Your new choices now look like this:
var rating: NSNumber
var ratingDouble: Double
ratingDouble = rating as! Double // 1
ratingDouble = Double(exactly: rating)! // 2
ratingDouble = Double(truncating: rating) // 3
ratingDouble = rating.doubleValue // 4
if let x = rating as? Double { // 5
ratingDouble = x
}
if let x = Double(exactly: rating) { // 6
ratingDouble = x
}
This calls Double._forceBridgeFromObjectiveC which calls Double(exactly:) with Double, Int64, or UInt64 based on the stored type in rating. It will fail and crash the app if the number isn't exactly representable as a Double. E.g. UInt64.max has more digits than Double can store, so it'll crash.
This is exactly the same as 1 except that it may also crash on NaN since that check isn't included.
This function always returns a Double but will lose precision in cases where 1 and 2 would crash. This literally just calls doubleValue when passing in an NSNumber.
Same as 3.
This is like 1 except that instead of crashing the app, it'll return nil and the inside of the statement won't be evaluated.
Same as 5, but like 2 will return nil if the value is NaN.
If you know your data source is dealing in doubles, 1-4 will probably all serve you about the same. 3 and 4 would be my first choices though.
Old Answer for Swift 1 and 2
There are several things you can do:
var rating: NSNumber
var ratingDouble: Double
ratingDouble = rating as Double // 1
ratingDouble = Double(rating) // 2
ratingDouble = rating.doubleValue // 3
The first item takes advantage of Objective-Cbridging which allows AnyObject and NSNumber to be cast as Double|Float|Int|UInt|Bool.
The second item presumably goes through a constructor with the signature init(_ number: NSNumber). I couldn't find it in the module or docs but passing AnyObject in generated an error that it cannot be implicitly downcast to NSNumber so it must be there and not just bridging.
The third item doesn't employ language features in the same way. It just takes advantage of the fact that doubleValue returns a Double.
One benefit of 1 is that it also works for AnyObject so your code could be:
let ratingDouble = self.prodResult!.prodsInfo.prodList[indexPath.row].avgRating! as Double
Note that I removed the ? from your function and moved the ! in. Whenever you use ! you are eschewing the safety of ? so there's no reason to do both together.

Resources