Constant Used Before Being Initialized - swift

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)
}

Related

Swift for loop is creating new objects

I'm working on a game where I store my player objects in an array. I want to give these players a hand of cards and I do so in the following way:
var players = [Player]()
mutating func deal(count: Int) {
for var player in players {
for _ in 1...count {
if let card = deck.draw(){
player.addCard(card: card)
}
}
if (player.id==1){print(player)}
}
print(players[0])
}
struct Player{
var id : Int
private(set) var randomValue: Int = 0
private(set) var hand = [PlayingCard]()
init(id: Int) {
self.id = id
randomValue = 100.arc4random
}
mutating func addCard(card: PlayingCard){
hand.append(card)
}
}
The problem is the hand is not actually being updated. Printing the player at index 0 (player.id == 1) inside the for loop gives me exactly what I expect. However, the print outside the loop has an empty hand and the random number is different. So I know the loop is creating a new object since init is called. Why is this happening and how can I edit the actual object in players array?
player is struct, when you iterate players - you get a copy of the players in the array.
You update it, print it and see the correct result, but it's scope is for the for loop only.
Either make player a class or update your array with the new copy of the player.

Recursive class initialisation in Swift

I'm building a simple tree data structure that takes some input and creates nodes in the tree until the input is small enough. That's all well and good (and very simple). For traversal reasons, I want to include a reference to the parent on each node (so I can walk it back "up"), or nil in the case of root. I tried to do this more or less like this (this is, of course, a simpler version—the actual tree is an octree):
class Recursive {
let value: Int
let child: Recursive?
weak var parent: Recursive?
init(value: Int, parent: Recursive? = nil) {
self.value = value
self.parent = parent
if value > 1 {
self.child = Recursive(value: value-1, parent: self) // fails
} else {
self.child = nil
}
}
}
var r: Recursive? = Recursive(5)
while r != nil {
print(r!.value)
r = r!.child
} // should print 5, 4, 3, 2, 1
Of course, this doesn't actually work. It seems you cannot pass "self" as an argument within the init method. I guess this is because of the whole two-step initialisation thing. What's the best (most idiomatic and/or most convenient and/or most performant) approach to this sort of thing in Swift? I've mostly written Python and a bit of C before now, so this Swift stuff is all pretty new to me.
I suppose I could write a separate function that initialises the tree, but it seems like it ought to be possible to do it all from the initialisation method. I just can't figure out how.
The problem is that you can pass self to the recursive call only
after it has been fully initialized (otherwise the recursively called
function could access an uninitialized property).
A possible solution is to assign the child's parent after the
child has been constructed. That allows you to keep child as a constant
property:
class Recursive {
let value: Int
let child: Recursive?
weak var parent: Recursive?
init(value: Int) {
self.value = value
if value > 1 {
self.child = Recursive(value: value - 1)
// Now `self` is fully initialized!
self.child?.parent = self
} else {
self.child = nil
}
}
}
And just for fun: The "linked-list traversal" can be done without
forced unwrapping:
var r: Recursive? = Recursive(value: 5)
while let node = r {
print(node.value)
r = node.child
}
and even shorter, with a constant list head:
let r = Recursive(value: 5)
for node in sequence(first: r, next: { $0.child }) {
print(node.value)
}
Your child parameter should be var instead of let and it should work. You can also remove the else since child is nil by default (var optionals are nil by default)

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 to act atomically on a struct property?

Assume I have the following objects:
class Dog {
let name: String
var stats: Stats?
func doSomething() { /*...*/ }
}
struct Stats {
var weight: Double
var age: Int
func ageEquivalence() -> Int { /*...*/ }
}
... and that I want to perform some calculation on a Dog instance. I want to do this atomically, however. In other words, I want to guarantee that either all code is executed, or no code is executed.
To clarify the problem, first, let's assume that Stats were a class. In that case, this problem is easy to solve:
func updateAge(_ dog: Dog) {
guard let stats = dog.stats else {
return
}
stats.age += 1 // A
dog.doSomething() // B
sendNotification(stats.ageEquivalence()) // C
}
It's either going to going to execute lines A-C if stats exists, or do nothing.
Now let's go back to the original problem, assume Stats is a struct again. If I try to run the code above, first I must change let stats to var stats, otherwise the compiler will complain that I'm trying to update a constant.
func updateAge(_ dog: Dog) {
guard var stats = dog.stats else {
return
}
stats.age += 1 // A
dog.doSomething() // B
sendNotification(stats.ageEquivalence()) // C
}
But now we run in to the problem that stats is a copy of dog.stats so when I update the age on line A, I'm not actually modifying the original dog's age.
Our next approach might look like this:
func updateAge(_ dog: Dog) {
dog.stats?.age += 1 // A
dog.doSomething() // B
if let stats = dog.stats {
sendNotification(stats.ageEquivalence()) // C
}
}
The problem with this approach is that if doSomething has a side effect which sets stats = nil, we would have run lines A & B, but not the C.
What's the best way to solve this problem?
It seems that the crux of the problem is this line:
dog.stats?.age += 1
In order to modify a struct, we must act on the original object, meaning we must use optional chaining. This cannot be used in conjunction with a guard var since that would create a copy of the original struct, and the original struct might be changed due to a side effect at any point of the execution.