I am trying to code A* Pathfinding in Swift.
At this point I am encountering a problem in retrieving the G-Costs of my closed list.
This problem is that when I try to search the dictionary for an entry, a nil is returned even though I believe I am entering the right key.
Here is the relevant code (note: Dictionary is in string:Int format as it does not want CGPoints).
print("\(closedList["\(currentPos)"])")
print("\(currentPosString)")
print("\(closedList)")
GCost = GCost + (Int)(closedList["\(currentPosString)"]!)
CurrentPosString is a String, and CurrentPos is a CGPoint (I have tried both).
Here is the read out from this code (I have omitted lots of code).
nil
4, 4
["4.0, 4.0": 0]
(The last line returns an error due to a nil).
My question is how do I make the 'closedList[currentPosString]' have the right format to successfully access the entry and return 0?
Code to generate closedPosString:
closedList["\(currentBest)"] = openList["\(currentBest)"]
openList["\(currentBest)"] = nil
openListOrder["\(currentBest)"] = nil
for i in 0 ..< mapTerrain.numberOfColumns{
for j in 0 ..< mapTerrain.numberOfRows{
if currentBest == "\(i), \(j)"{
currentPos = CGPoint(x: i, y: j)
currentPosString = "\(i), \(j)"
closedList["\(currentPos)"] = closedList[currentBest!]
print("\(closedList["\(currentPos)"])")
print("a") //not printing for some reason
foundNextNewPos = true
}
if foundNextNewPos == true{
break
}
}
if foundNextNewPos == true{
break
}
}
Tried a rebuilt with this code, it still broke:
currentPosString = currentBest!
currentBest = nil
Generation of currentBest:
for key in openList.keys{
let tester = openList["\(key)"] //find way to not get a nil later
//print("\(openList["\(currentBest)"]!)") //gives nil
if key == "\(endPoint)"{
currentBest = key
foundEnd = true
break
}else if openList["\(currentBest)"] == nil{
currentBest = key
}else if tester! < openList["\(currentBest)"]!{
currentBest = key
} else if tester == openList["\(currentBest)"]{
if openListOrder["\(key)"]! > openListOrder["\(currentBest)"]!{
currentBest = key
}
}
}
The problem is that there is no entry "4, 4", the entry "4.0, 4.0" comes from an earlier line of code that relied on CGFloats to make the key, editing this to use the same format ("Int, Int") made it work.
if hexCounter < 3 && openList["\(currentPos.x), \(currentPos.y)"] == nil{
closedList["\(currentPos.x), \(currentPos.y)"] = 0
}
to
if hexCounter < 3 && openList["\(currentPosString)"] == nil{
closedList["\(currentPosString)"] = 0
}
Related
Whenever I call this function, my app freezes and nothing in the debug console prints. I am trying to get strings and a double from firebase and then stick them into an identifiable struct array. Any Ideas???
If you have another idea for storing that object, I would love to read it.
var count = Int()
var name = Array<String>()
var imageUrl = Array<String>()
var id = Array<String>()
var rating = Array<Double>()
var url = Array<String>()
var keys = 0
db.collection("parties").document(Utilities.code).addSnapshotListener { document, error in
//check for error
if error == nil {
//check if document exists
print("No error")
if document != nil && document!.exists {
print("Document Exists")
if let array = document!.get("yesName") as? Array<String> {
count = array.count
name = array
print("yesName = \(array)")
keys += 1
}
if let array = document!.get("yesImg") as? Array<String> {
imageUrl = array
print("yesImg = \(array)")
keys += 1
}
if let array = document!.get("yesId") as? Array<String> {
id = array
print("yesId = \(array)")
keys += 1
}
if let array = document!.get("yesRating") as? Array<Double> {
rating = array
print("yesRating = \(array)")
keys += 1
}
if let array = document!.get("yesUrl") as? Array<String> {
url = array
print("yesUrl = \(array)")
keys += 1
}
}
}else {
print("error = \(error!)")
}
}
while keys < 6 {
if name.count > 0 && imageUrl.count > 0 && id.count > 0 && rating.count > 0 && url.count > 0 {
for _ in 0...count {
yes.list.append(RestaurantListViewModel(name: name.first!, imageUrl: URL(string: imageUrl.first!)!, id: id.first!, rating: rating.first!, url: url.first!))
print("combined = \(yes.list.append(RestaurantListViewModel(name: name.first!, imageUrl: URL(string: imageUrl.first!)!, id: id.first!, rating: rating.first!, url: url.first!)))")
name.removeFirst()
imageUrl.removeFirst()
id.removeFirst()
rating.removeFirst()
url.removeFirst()
}
keys = 6
}
}
The snapshot listener will execute asynchronously that means in some time after the current method is finished. So the code after it is called is executed only once and just after listener creation : there is nothing yet in the arrays and keys == 0. May be this code should be inside the listener call after document is read and array updated.
Have you tried to debug while keys < 6 { circle?
Next line condition
if name.count > 0 && imageUrl.count > 0 && id.count > 0 && rating.count > 0 && url.count > 0
can easily equals to false and you get infinity circle running.
Also I see few logic issues in your code: in the first part of code you set up required fields (keys equals in a range from 0 to 6). But in the second part inside circle while keys < 6 you require all 6 fields and want to exit only after all values will have count > 0. You need to get value.count only when it needed.
I found out, that I can write this code:
func optionalReturn() -> Int? {
// do sth. and return maybe an Int, otherwise:
return nil
}
if let x = optionalReturn(), x > 5 {
print("test exists and is greater then 5")
}
Now I'm standing in front of the following problem: I want to have an if, that handles two cases:
case 1: x is not existing, OR
case 2: x is existing, but is greater than value 5
This is not working:
if x == nil || (let x = optionalReturn(), x > 5) {
...
}
Is sth. like this possible in Swift?
UPDATE
The above code was simplified to show my problem, but my case is a little bit different, because my called function doesn't return an optional Int, it returns an optional struct:
struct TestStruct {
var x: Int
var y: Int
}
func optionalReturn() -> TestStruct? {
// do sth. and maybe return a TestStruct(....), otherwise:
return nil
}
let test = optionalReturn()
if test == nil || ...?
UPDATE 2
In my first update I had a mistake. Thanks to Christik, who mentioned it in the comments, my code had a mistake and would have worked without it. I accepted his answer as a solution, but my first idea was right as well: Swift skips the other if-conditions, if the first OR-condition is true. So this code works as well:
...
if test == nil || (test!.x > 5 && test!.y > 6) {
print("I am here")
}
You can use the nil coalescing operator here, and give x a value greater than 5, if its nil:
if (x ?? 6) > 5 {
// do your thing
}
In regards to the question update, you can use map on the optional to achieve your goal:
if x.map({ $0.x > 5 && $0.y > 6 }) ?? true {
// do your thing
}
map has the advantage that it avoids the forced unwrap.
I'm trying to make a call from my JSON file where the variable is a string however in order to compare it I would want it to be an integer, however, whenever I try and convert it using methods on here nothing seems to be working, assuming the wrong syntax. This line essentially (pData.info?.nutriScore ?? 0) prints a score however its a string.
if let nScore = Int(pData.info?.myScore ?? 0) < 0 {
//Other Code
}
if let nutriScore = pData.info?.nutriScore, let nScore = Int(nutriScore) {
// your code here
}
You need
if let nScore = Int(pData.info?.myScore ?? "0" ) , nScore > 0 {
}
if let nScore:Int = Int(pData.info?.nutriScore ?? "0") {
if nScore < 0 {
print(nScore)
}
}
Avoid using ?? default value ,
Yes you dont have the value in your object so you are passing the default that doesnt mean default value is your Real data .
if let b = pData.info?.myScore, let nScore = Int(b) , nScore >= 0{
print(nScore)
} else {// handle negative logic}
Following code was working well with swift 2 but it is not working with swift 3.
let constraints: NSArray = contentView.constraints as NSArray
let indexOfConstraint = constraints.indexOfObject (passingTest: { (constraint, idx, stop) in
return (constraint.firstItem ).tag == bubbleTag && (constraint.firstAttribute == NSLayoutAttribute.left || constraint.firstAttribute == NSLayoutAttribute.right)
})
I am getting following error:
Binary operator '==' cannot be applied to operands of type '_' and
'NSLayoutAttribute'
I am getting this error for following line:
constraint.firstAttribute == NSLayoutAttribute.left
How can i fix it?
That error message is showing only the first error that your code may cause, and you may need some more fixes to work on it.
Assuming you are using NSArray (or NSMutableArray) for constraints, why don't you declare constraints as [NSLayoutAttribute]?
With that change, you may need to fix some other parts, but the code getting the index can be written like this:
let indexOfConstraint = constraints.index {constraint in
return constraint.firstItem.tag == bubbleTag && (constraint.firstAttribute == NSLayoutAttribute.left || constraint.firstAttribute == NSLayoutAttribute.right)
}
//indexOfConstraint becomes `Int?`, so you may need to use Optional binding
if let index = indexOfConstraint {
//Do something for found case
} else {
//Do something for not-found case
}
If you cannot change the type of constraints globally, you can put this sort of code in your local code-block just before the code above.
guard let constraints = constraints as? [NSLayoutConstraint] else {
fatalError("something wrong with `constraints`")
}
Please, check below updated code:
let constraints: NSArray = contentView.constraints as NSArray
let indexOfConstraint = constraints.indexOfObject (passingTest:){ ( constraint, idx, stop) in
return ((constraint as AnyObject).firstItem as! UIView).tag == bubbleTag && ((constraint as AnyObject).firstAttribute == NSLayoutAttribute.left || (constraint as AnyObject).firstAttribute == NSLayoutAttribute.right)
}
This is my original function in Swift 2:
// check on winning combinations
func checkWinnerMove(){
for var i = 0; i<winningCombinations.count && !isWinner;i += 1 {
if gameState[winningCombinations[i][0]-1] == activePlayer &&
gameState[winningCombinations[i][1]-1] == activePlayer &&
gameState[winningCombinations[i][2]-1] == activePlayer{
isWinner = true;
}else{
isWinner = false;
}
}
}
I have changed it to this:
// check on winning combinations
func checkWinnerMove(){
for i in 0 ..< winningCombinations.count && !isWinner{
if gameState[winningCombinations[i][0]-1] == activePlayer &&
gameState[winningCombinations[i][1]-1] == activePlayer &&
gameState[winningCombinations[i][2]-1] == activePlayer{
isWinner = true;
}else{
isWinner = false;
}
}
}
But keep getting a error when I add the
&& !isWinner
statment in the for-in loop. The error I get is:
No '..<' candidates produce the expected contextual result type 'Bool'
Any suggestions? Thank You!
Instead of forcibly trying to rewrite your original C-style for loop, consider what you're trying to achieve and attempt to re-write it in "native" Swift from scratch. How about breaking out of your loop once your true condition is met, instead of keeping it in the loop signature? E.g.
for i in 1...5 {
print(i)
if i == 3 { break }
} // 1 2 3
Applied to your example
func checkWinnerMove()
isWinner = false
for i in 0 ..< winningCombinations.count {
if gameState[winningCombinations[i][0]-1] == activePlayer &&
gameState[winningCombinations[i][1]-1] == activePlayer &&
gameState[winningCombinations[i][2]-1] == activePlayer {
isWinner = true
break
}
}
}
The explicit by index access of the (unknown for us) gameState and winningCombinations sequences is quite "unswifty" w.r.t. in the dangers of runtime exceptions for indices out of range for the sequences. So bear in mind that there are safer ways to perform such access.
For future reference, consider reading How to create a Minimal, Complete, and Verifiable example: since we (the potential answerer's of your question) don't have access to/the full information regarding isWinner, winningCombinations, gameState or activePlayer, we can't verify your example. Also, since the question cover a concept, it could be boiled down to a more minimal form. (Welcome to StackOverflow!)