Here’s what I got for the Silver Challenge. It looks to give me the correct solution. However, any suggestions for optimizing it would be great.
import Foundation
class Vampire: Monster {
//initialize empty vampire array
var vampireThrall = [Int]()
//initialize vampireTerrorizing property as bool
var vampireTerrorizing = false
override func terrorizeTown() {
//check that a town exist to terrorize
if town != nil {
//check if vampire is terrorizing
if vampireTerrorizing == true {
//add 1 to the vampireThrall array
vampireThrall.append(1)
//and decrement the population by 1
town?.changePopulation(by: -1)
}
//call Monster class's implementation of the terrorizeTown function
super.terrorizeTown()
}
}
}
Here’s what I input into main.swift…
import Foundation
var myTown = Town()
myTown.changePopulation(by: 500)
let fredTheZombie = Zombie()
fredTheZombie.town = myTown
fredTheZombie.terrorizeTown()
fredTheZombie.town?.printDescription()
var aTown = Town()
let vickyTheVampire = Vampire()
vickyTheVampire.town = aTown
vickyTheVampire.terrorizeTown()
vickyTheVampire.town?.printDescription()
and here’s the final result in main.swift…
Monster is terrorizing a town!
Population: 5912, number of stoplights: 4
Monster is terrorizing a town!
Population: 5422, number of stoplights: 4
Program ended with exit code: 0
Again any suggestions to improve or something I did wrong would be greatly appreciated.
Before doing that, ask yourself: when terrorizeTown is executed, could the vampire not be terrorizing the town? Would it ever make sense for that variable to be false?
First of all I think you should look at what JonAult wrote because that is much more important here.
But in general, of course you need to change your attributes and variables somehow. Either from inside the class or like in this instance where it is a public attribute from other class or main.swift
var myTown = Town()
var aTown = Town()
myTown.changePopulation(by: 500)
let vickyTheVampire = Vampire()
vickyTheVampire.town = aTown
vickyTheVampire.terrorizeTown()
vickyTheVampire.town?.printDescription()
vickyTheVampire.terrorizeTown()
vickyTheVampire.town?.printDescription()
vickyTheVampire.terrorizeTown()
vickyTheVampire.town?.printDescription()
I get the following output (haven’t changed name to “Vampire” yet)…
Monster is terrorizing a town!
Population: 5422, number of stoplights: 4
Monster is terrorizing a town!
Population: 5422, number of stoplights: 4
Monster is terrorizing a town!
Population: 5422, number of stoplights: 4
Program ended with exit code: 0
import Foundation
class Vampire: Monster {
//initialize empty vampire array
var vampireThrall = [Int]()
//initialize vampireTerrorizing property as bool
var vampireTerrorizing: Bool = true
override func terrorizeTown() {
//check that a town exist to terrorize
if town != nil {
//check if vampire is terrorizing
if vampireTerrorizing {
//add 1 to the vampireThrall array
vampireThrall.append(1)
//and decrement the population by 1
town?.changePopulation(by: -1)
}
//call Monster class's implementation of the terrorizeTown function
super.terrorizeTown()
}
}
}
Output:
Monster is terrorizing a town!
Population: 5421, number of stoplights: 4
Monster is terrorizing a town!
Population: 5420, number of stoplights: 4
Monster is terrorizing a town!
Population: 5419, number of stoplights: 4
Program ended with exit code: 0
What I was trying to suggest is that you don’t need the vampireTerrorizing property at all.
But if you’re going to keep it, then you should move the super.terrorizeTown() call inside the if statement, so that it is only called if vampireTerrorizing is true. With what you have now, if vampireTerrorizing is false you won’t change the town population or add to the vampire’s list of thralls but you’ll still print the “Monster is terrorizing a town!” message, which doesn’t make sense. Either the vampire is terrorizing the town or it isn’t.
It’s been a while since I looked at this, but if I remember correctly the monster class has a name variable, so when you create your vampire you should set the name of your instance to vampire.
You should also include a check to make sure the population isn’t 0, otherwise you could be generating negative numbers.
In the spirit of vampires, when I did this challenge I took it one step further. Every time a vampire creates a thrall, the next attack consumes more population, however since baby vampires are notorious from destroying their prey, I left it as only +1 one thrall being created.
That all said, I’m not a big of checking for nil (that feels so Obj-C or C++), you’re better off doing something like:
if let population = town?.population {
// insert logic here
}
import Foundation
class Vampire: Monster {
//initialize empty vampire array
var vampireThrall = [Int]()
//initialize vampireTerrorizing property as bool
var vampireTerrorizing: Bool = true
override func terrorizeTown() {
//check that a town exist to terrorize
if let population = town?.population {
//check if vampire is terrorizing
if vampireTerrorizing && population > 0 {
//add 1 to the vampireThrall array
vampireThrall.append(1)
//and decrement the population by 1
town?.changePopulation(by: -1)
//call Monster class's implementation of the terrorizeTown function
super.terrorizeTown()
}
}
}
}
“It’s been a while since I looked at this, but if I remember correctly the monster class has a name variable, so when you create your vampire you should set the name of your instance to vampire.”
You mean like this?
var vampireThrall = [Int]()
var name = "Vampire"
It gives me the error:
Cannot override with a stored property 'name'
So do I have to override the name variable?
But here’s the rest the code so far with the optional town population and checking for a population > 0 you suggested:
import Foundation
class Vampire: Monster {
//initialize empty vampire array
var vampireThrall = [Int]()
//initialize vampireTerrorizing property as bool
var vampireTerrorizing: Bool = true
override func terrorizeTown() {
//check that a town exist to terrorize
if let population = town?.population {
//check if vampire is terrorizing
if vampireTerrorizing && population > 0 {
//add 1 to the vampireThrall array
vampireThrall.append(1)
//and decrement the population by 1
town?.changePopulation(by: -1)
//call Monster class's implementation of the terrorizeTown function
super.terrorizeTown()
}
}
}
}
You don’t want to declare a new name variable, you just need to change the value of the one you already have (which was inherited from Monster).
But in order to do that, you’ll have to declare an initializer function for Vampire (which the book doesn’t talk about until Chapter 17). If you haven’t gotten to that yet, the init() function is getting called automatically when a new Vampire object is created. The compiler creates a default init() for you, but you can explicitly define one yourself if needed.
You don’t need to override. In your main.swift file when you created your vampire, the monster class already has a name property. So you just need to set it. So something like this:
struct Town {
var population = 10
var numberOfStoplights = 4
func printTownDescription() {
print("Population: \(population), number of stoplights: \(numberOfStoplights).")
}
mutating func changePopulation(amount: Int) {
population += amount
population = population < 0 ? 0 : population
}
}
This is my silver challenge:
class Vampire: Monster {
var vampire: [Victim]? = []
override func terrorizeTown() {
vampire?.append(.thrall)
town?.changePopulation(amount: -1)
super.terrorizeTown()
}
}
enum Victim {
case thrall
}
I’m curious about why you created var vampireTerrorizing: Bool = true? If you’re already calling Vampire.terrorizeTown(), then it doesn’t seem like you would need the additional check in at if vampireTerrorizing && population > 0. You’ve already called the method that that is part the Vampire class, which means you know that when this is called, it’s a vampire that is terrorizing the town.
Good job so far. It looks like you’re almost there.