How to change property value with static method?

732 Views Asked by At

In this simple game there is a class Fighter whose purpose is to make two fighters fight. The one who looses health below 0, it looses the game.

In order to fight there is a static method fight (..) which iterates till one fighter wins the game, supported by another non static method attack (..)

object Fighter health should change as two objects fight during the game using the methods fight(...) and attack (...). The problem is it always prints the same Fighter health, and the game never ends. I don´t see where the issue is

class ViewController: UIViewController {
     override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let david = Fighter(name: "David", health: 100, damage: 30, defense: 10, initiative: 80)
        let goliath = Fighter(name: "Goliath", health: 300, damage: 60, defense: 14, initiative: 90)


        let myFight1 = Fighter.fight(fighter1: david, fighter2: goliath) // always executing same Fighters
        print(myFight1)

    }
}

import Foundation


struct Fighter {
    var name: String
    var health: Double
    var damage: Int
    var defense: Int
    var initiative: Int

    init (name: String, health: Double, damage: Int, defense: Int, initiative: Int) {
        self.name = name
        self.health = health
        self.damage = damage
        self.defense = defense
        self.initiative = initiative
    }

     init (name: String, health: Double, damage: Int, defense: Int) {
        self.name = name
        self.health = health
        self.damage = damage
        self.defense = defense
        self.initiative = 0
    }

    static func fight(fighter1: Fighter, fighter2: Fighter) -> Fighter {
        let f1 = fighter1
        let f2 = fighter2

        if f1.health == f2.health {
            return f1
        }

        if f2.initiative > f1.initiative {
           f2.attack(f: f1)
        }

        var i = 0

        while f1.health > 0 {
            i += 1
            print("--> i: \(i)")
            f1.attack(f: f2 )

            if f2.health <= 0 {
                return f1
            }
        f2.attack(f: f1)
            }
        return f2
        }

    func attack(f: Fighter) -> Void {
        var g = f
        g.health = g.health - Double(g.damage * (1 - g.defense / 100))
        print(g)
    }        
}
4

There are 4 best solutions below

0
On BEST ANSWER

You are using a struct for Fighter which is a value type in Swift.

The most basic distinguishing feature of a value type is that copying — the effect of assignment, initialization, and argument passing — creates an independent instance with its own unique copy of its data

Solution: Change Fighter to a class and you are good to go.

Output of the print statements: (Second print statement changed to print(g.name, g.health))

David 70.0
--> i: 1
Goliath 240.0
David 40.0
--> i: 2
Goliath 180.0
David 10.0
--> i: 3
Goliath 120.0
David -20.0


For more reading: Value and Reference Types

0
On

When you're saying...

    var g = f

...you're actually creating a copy of that object, not a reference. So, when you're changing 'health' property, you changing it in the copy. There are 2 simple solutions:

1) Change struct to class, 'cause classes are being referenced, unlike structs, which is just copying.

2) Replace original object with its modified copy (g)

0
On

As Rakesha notes, structs are value types, so your attack code doesn't actually modify anything:

func attack(f: Fighter) -> Void {
    var g = f   // Make a mutable copy of `f` called `g`
    g.health = g.health - Double(g.damage * (1 - g.defense / 100)) // Modify g
    print(g) // Print g
    // Throw g away
}

(Side note: I think g.damage is incorrect here; I think you probably meant self.damage.)

Nothing there actually modifies f. There are several ways to address this. One is to use classes, which introduces subtle mutable state. I don't think I'd do that here. By "subtle mutable state" I mean that you expect attack to modify f, but nothing in the signature says that it does that, so the caller may be surprised.

Instead, you have several ways to implement this that make your mutations explicit on structs. You can make attack explicitly modify f:

func attack(f: inout Fighter) {
    f.health = f.health - Double(damage * (1 - f.defense / 100))
}

Or you could turn it around and modify yourself when attacked by someone else:

mutating func attackedBy(f: Fighter) {
    health = health - Double(f.damage * (1 - defense / 100)
}
0
On

After calling the method func attack(f: Fighter) -> Void every time, the properties of the Fighter who is being attacked are not getting updated. So while loop is not going to break at any point.

Please replace the code below.

static func fight(fighter1: Fighter, fighter2: Fighter) -> Fighter {
        var f1 = fighter1
        var f2 = fighter2

        if f1.health == f2.health {
            return f1
        }

        if f2.initiative > f1.initiative {
            f1 = f2.attack(f: f1)
        }

        var i = 0

        while f1.health > 0 {
            i += 1
            print("--> i: \(i)")
            f2 = f1.attack(f: f2 )

            if f2.health <= 0 {
                return f1
            }
            f1 = f2.attack(f: f1)
        }
        return f2
    }

    func attack( f: Fighter) -> Fighter {
        var g = f
        g.health = g.health - Double(g.damage * (1 - g.defense / 100))
        print(g)
        return g
    }