Wait until property is true before continuing loop - swift

Trying to do something I feel like should be simple but nothing I've tried so far works. I'm trying to prevent this loop from continuing until a property on my enemy is set to true.
My enemy node figures out a path to the player during the walk state. I don't want to iterate to the next enemy until the path has been calculated. My enemy node has a pathComplete node I set to true during the walk state.
This is executed on touch.
for node:AnyObject in self.children {
if node is EnemyNode {
let enemy = node as! EnemyNode
enemy.destination = coordinate
enemy.stateMachine.enterState(WalkingState)
}
}

If I understand what you want to do, then you should use the recursion instead the loop.
First you need to create some enemyNodeArray that will be contains objects you need.
Then you can make two functions like this:
func actionForObjectWithIndex(index: Int, completion block: (nextIndex: Int) -> Void) {
guard index >= 0 && index < enemyNodeArray.count else {
return
}
// do what you need with object in array like enemyNodeArray[index]...
...
// Then call completion
block(nextIndex: index + 1)
}
func makeActionWithIndex(index: Int) {
actionForObjectWithIndex(index, completion: {(nextIndex: Int) -> Void in
self.makeActionWithIndex(nextIndex)
})
}
and start use it like this:
if !enemyNodeArray.isEmpty {
makeActionWithIndex(0)
}
This algorithm will be take every object in an array, and perform certain actions with them, and it will move to the next item only after it has finished with the previous.

Related

Constant Used Before Being Initialized

I'm converting a project to Swift 3 and I'm getting this error below:
fileprivate class func moveAi() -> DataModel {
let player = pathForPlayer(true)
let i: Int
let scope = GameModel.shared.scopeForPlayer(GameModel.shared.topPlayer.id, rival: GameModel.shared.downPlayer.id)
//for (i = player.count-1; i > 0; i -= 1) { // Original for loop prior to Swift 3
for i in stride(from: player.count-1, to: 0, by: -1) {
if scope.contains(player[i]) {
break
}
}
// Error is on this line
return DataModel.idConvertToPlayer(player[i], player: true)
}
// Path for Player
class func pathForPlayer(_ play: Bool) -> [Int] {
let player = play ? GameModel.shared.topPlayer.id : GameModel.shared.downPlayer.id
let end = play ? topEnd : downEnd
return pathForPlayer(Node(data: player, parent: -1), end: end)
}
If I move the return within the for loop the error goes away, but I still need to return a value here.
Any suggestions?
Updated Code:
fileprivate class func moveAi() -> DataModel {
let player = pathForPlayer(true)
let _: Int // i
var final: Int?
let scope = GameModel.shared.scopeForPlayer(GameModel.shared.topPlayer.id, rival: GameModel.shared.downPlayer.id)
**//for (i = player.count-1; i > 0; i -= 1)** {
for i in stride(from:player.count-1, to:0, by:-1) {
final = i
if scope.contains(player[i]) {
break
}
}
return DataModel.idConvertToPlayer(player[final!], player: true)
}
The above code does not provide the crash I was previously getting, but I am getting a new crash.
The best way to describe is my AI will prevent itself from finishing a game.
I'm building off the board game Quoridor. The AI will build walls around it's pawn so it can't move any further. This does not happen every game though. It's very random when it happens.
Could it be based on how I've written the for loop? Is the Swift way correct or could I get insight to how to right the commented for loop in Swift syntax?
Your loop gives i a value inside the loop, but not outside. After the loop ends, i has no value. If you want some value of i to be available after the loop finishes, you need to assign it to something that exists outside the loop.
One way would be to add this before the loop:
var final : Int?
Then in the loop, set final = i somewhere. After the loop finishes, the value will be in final.
Your function moveAi has 2 different things called i: An Int constant defined at the outer scope of the function and an inner constant that exists inside the scope of your for i in... loop. They are separate.
Inside the for loop, i hides the outer constant, and refers to the value extracted from your stride call.
Outside the for...in loop, that inner definition of i goes out of scope and no longer exists. Now you've got a constant that has never been initialized with a value.
You could rewrite your function like this:
fileprivate class func moveAi() -> DataModel {
let player = pathForPlayer(true)
var i: Int
let scope = GameModel.shared.scopeForPlayer(GameModel.shared.topPlayer.id, rival: GameModel.shared.downPlayer.id)
for index in stride(from: player.count-1, to: 0, by: -1) {
i = index
if scope.contains(player[i]) {
break
}
}
// Error is on this line
return DataModel.idConvertToPlayer(player[i], player: true)
}

Why is my minimax algorithm not undoing every move?

I am creating an AI for an original 4-player board game that I made.
Details about the board game:
4 players take turns to move their coloured pieces simultaneously in one of the four cardinal directions. Pieces can be moved off the board. Players each have 5 lives at the start. For each piece moved off the board, the player lose 1 life. New pieces will spawn deterministically throughout the game.
I looked up how to do a minimax algorithm and found this. I read through that and thought I understood everything, so I tried to translate the Java code in Section 1.5 to Swift.
Here is my thought process:
Since my game has 4 players, I would treat everyone else as minimising players.
In the Java code, there is a line where the move is undone. Since my game's game state can change drastically in each move, I would just store all the game states in an array, and when something needs to be undone, I can just call dropLast on the array.
Since a move in my game is represented as a Direction enum, I will return a (Int, Direction) tuple instead if an int array like the Java code.
game is a computed property that just returns gameStates.last!
game.currentPlayer will change every time I call one of the moveUp/Down/Left/Right methods on game, so I don't need to write extra code to decide who's the next player.
In the last line, I need to return (bestScore, bestDirection), but I realised sometimes bestDirection is not assigned. Therefore, I made bestDirection an optional. If it is not assigned at the return statement, I'll just return an arbitrary direction.
And here is my attempt:
private func minimax(depth: Int, color: Color) -> (score: Int, direction: Direction) {
var bestScore = color == myColor ? Int.min : Int.max
var currentScore: Int
var bestDirection: Direction?
if game.players.filter({$0.lives > 0}).count < 2 || depth == 0 {
// This is a call to my heuristic evaluation function
bestScore = evaluateHeuristics()
} else {
// if the player has no pieces on the board, just move up since moving in any direction won't change anything
for move in (game.board.indicesOf(color: color).count == 0 ? [Direction.up] : [Direction.up, .down, .left, .right]) {
let gameCopy = game.createCopy()
switch move {
case .up: gameCopy.moveUp()
case .down: gameCopy.moveDown()
case .left: gameCopy.moveLeft()
case .right: gameCopy.moveRight()
}
gameStates.append(gameCopy)
// myColor is like mySeed in the original Java code
if color == myColor {
currentScore = minimax(depth: depth - 1, color: game.currentPlayer.color).score
if currentScore > bestScore {
bestScore = currentScore
bestDirection = move
}
} else {
currentScore = minimax(depth: depth - 1, color: game.currentPlayer.color).score
if currentScore < bestScore {
bestScore = currentScore
bestDirection = move
}
}
_ = gameStates.dropLast()
}
}
return (bestScore, bestDirection ?? .left)
}
When I test out this AI with a depth of 4, it seems to either do stupid moves, like moving his pieces off the board, or move his pieces in one direction only.
I also noticed that gameStates has a length of about 90 when the recursive call returns. Normally it should be 1 right? Because all the moves the AI tried should have been undone by the time the recursive call returns, and gameStates will only contain the initial state.
What did I do wrong?
dropLast() returns an array slice containing all but the last element of the array. It does not modify the original array. Use removeLast()
Edit
What you really want is a stack data structure. Here's one.
public struct Stack<Element>
{
fileprivate var elements: [Element] = []
public init() {}
/// Push an element onto the top of the stack
///
/// - parameter newElement: The element to push
public mutating func push(_ newElement: Element)
{
elements.append(newElement)
}
/// Pops the top element off the stack
///
/// - returns: The top element or nil if the stack is empty.
public mutating func pop() -> Element?
{
let ret = elements.last
if ret != nil
{
elements.removeLast()
}
return ret
}
/// The top element of the stack. Will be nil if the stack is empty
public var top: Element?
{
return elements.last
}
/// Number of items in the stack
public var count: Int
{
return elements.count
}
/// True if the stack is empty
public var isEmpty: Bool
{
return elements.isEmpty
}
}

Can't modify struct property since "Function call returns immutable value"

I have a struct, called "enemy" with two properties:
struct enemy {
var hp = 0
var dam = 0
}
I have a function that rolls a dice (I know not the neatest way to do it but I'm emulating the real-life process), and I call that function in another function to decide enemy values during an enemy encounter.
My diceRoll function (I know this works since I use it elsewhere)
func diceRoll(roll: Int) {
outcome = roll
}
And finally the bit in question that's not working:
func decideEnemyStats() {
diceRoll(roll: Int(arc4random_uniform(6) + 1));
enemy().hp = outcome
}
I get this error:
cannot assign to property: function call returns immutable value
why?

How do I, in swift, use ccPhysicsCollisionBegin to find the collision between a specific type of object and a singular instance of another object?

For example, let's say I have a block being hit with several balls. How would I make it so the function gets run when any ball hits the block? (as opposed to a singular ball hitting the block)
Essentially, I want this:
func ccPhysicsCollisionBegin(pair: CCPhysicsCollisionPair!, AllBalls[] nodeA: CCNode!, block nodeB: CCNode!) -> Bool {
return true
}
If all your balls are objects of a Ball class, you can set the collisionType property of the ball's physics body to ball.
For example:
func didLoadFromCCB() {
physicsBody.collisionType = "ball"
}
Then, each ball object that you create will have that same collision type and the following function will be called whenever any of the balls collide with the block:
func ccPhysicsCollisionBegin(pair: CCPhysicsCollisionPair!,
ball: CCNode!, block: CCNode!) -> ObjCBool {
return true
}

Looping through all objects/sprites in a scene

Is it possible to loop through all the objects contained in a scene when the override function "didMoveToView" is called? In context, I'm trying to check all the objects to see if one of them has the name "planet1" then make it orbit around a given point.
if you need to loop through all the nodes just use
enumerateChildNodesWithName("//*", usingBlock:
{ (node, stop) -> Void in
if node.name=="planet1" {
//make it orbit
}
})
in the name string // means search the entire node tree, not just this node's children.
* is wildcard and matches any name, you can use "xxx*" to match any name starting with xxx and the same for "*xxx" to match any name ending in xxx
EDIT: you can just do:
planet1=self.childNodeWithName("//planet1")!
Just loop through all of the parent view's subviews.
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
for sibling in view.subviews {
// check sibling view
}
}
Note that at least one of the objects in the loop will be equal to self. Also note that they'll all have a type of AnyObject. So you'll have to downcast. If your view named "planet1" is a particular class though:
for sibling in view.subviews {
if let planet = sibling as? PlanetSprite {
if planet.name == "planet1" {
// do stuff
}
}
}
This might be about what you're looking for.