Bronze, Silver, & Gold Challenges - Ch 16


#1

Properties are a little more straightforward for me to understand, so let’s see how I did with these challenges.

Bronze Challenge:

Simple change for this one, just need a if statement before printing out a message:

/// Town.swift
var population: UInt = 5422 {
    didSet(oldPopulation) {
        if oldPopulation > population {
            print("The population has changed to \(population) from \(oldPopulation)".uppercased())
        }
    }
}

Silver Challenge:

This one seems deceptively straightforward. All I did was create the Mayor struct, gave it a method to print out the statement, and then called that statement in the didSet in Town's population.

/// Mayor.swift
struct Mayor {
    func offerCondolences() {
        print("I'm deeply saddened to hear about this latest tragedy. I promise that my office is looking into the nature of this rash of violence.")
    }
}
/// Town.swift
var mayor = Mayor()
var population: UInt = 5422 {
    didSet(oldPopulation) {
        if oldPopulation > population {
            print("The population has changed to \(population) from \(oldPopulation)".uppercased())
            mayor.offerCondolences()
        }
    }
}

Gold Challenge

To specify which monster was doing the attacking, I specified an enum called AttackType to represent which monster was currently attacking. Each monster registers itself as the attacker when it terrorizes the town, overwriting the property defined in the Town struct. I modified the offerCondolences() method, which I’ll explain afterwards:

/// Town.swift
struct Town {
    ....
    var population: UInt = 5422 {
        didSet(oldPopulation) {
            if oldPopulation > population {
                print("The population has changed to \(population) from \(oldPopulation)".uppercased())
                mayor.offerCondolences(forMonsterAttackType: attackType)
            }
        }
    }
    ....
    var attackType: AttackType = .monster
    var mayor = Mayor()

    enum AttackType {
        case zombie
        case vampire
        case monster
    }
    ...
}

Go into the respective monster class files and register their attack type when terrorizing:

/// Zombie.swift
final override func terrorizeTown() {
    ...
    town?.attackType = .zombie
    ....
}
/// Vampire.swift (I'm continuing from last chapter)
final override func terrorizeTown() {
    ...
    town?.attackType = .vampire
    ....
}

Now for the mayor: instantiate his anxiety level, and add a parameter to pass in the attack type. Now you can monitor what type of attack is going on, and can increase the mayor’s anxiety level accordingly. You have to specify Town.AttackType because the enum is nested inside of the Town struct. I print the anxiety level simply because I wanted to make sure it was called and I can monitor its value in the console, because the private keyword won’t allow me to access it from the main.swift file. Also make sure the function is mutating, since you’re modifying a property of the struct!

/// Mayor.swift
struct Mayor {
    private var anxietyLevel: Int = 0

    mutating func offerCondolences(forMonsterAttackType type: Town.AttackType) {
        print("I'm deeply saddened to hear about this latest tragedy regarding the \(type)s. I promise that my office is looking into the nature of this rash of violence.")
        if type == Town.AttackType.zombie {
            anxietyLevel += 1
            print("Current anxiety: \(anxietyLevel)")
        }
    }
}

After all that, my output comes to something like this:

  • Zombie Fred is terrorizing a town!
  • THE POPULATION HAS CHANGED TO 1005412 FROM 1005422
  • I’m deeply saddened to hear about this latest tragedy regarding the zombies. I promise that my office is looking into the nature of this rash of violence.
  • Current anxiety: 1
  • Zombie Fred is terrorizing a town!
  • THE POPULATION HAS CHANGED TO 1005402 FROM 1005412
  • I’m deeply saddened to hear about this latest tragedy regarding the zombies. I promise that my office is looking into the nature of this rash of violence.
  • Current anxiety: 2
  • Zombie Fred is terrorizing a town!
  • THE POPULATION HAS CHANGED TO 1005392 FROM 1005402
  • I’m deeply saddened to hear about this latest tragedy regarding the zombies. I promise that my office is looking into the nature of this rash of violence.
  • Current anxiety: 3
  • Zombie Fred is terrorizing a town!
  • THE POPULATION HAS CHANGED TO 1005382 FROM 1005392
  • I’m deeply saddened to hear about this latest tragedy regarding the zombies. I promise that my office is looking into the nature of this rash of violence.
  • Current anxiety: 4
  • Population: 1005382, number of stoplights: 4

I’d love to heard thoughts/ideas on this one. I wanted to restrain myself from skipping ahead, because a lot of the problems I was having trying to solve this could’ve been easily solved with initializers (the next chapter topic). Wanted to keep myself honest :stuck_out_tongue_winking_eye:


#2

Silver challenge:
Hello .Why does xcode give me mistake if I type this:
//Town.swift var mayor: Mayor
instead of this:
var mayor = Mayor()


#3

I think the syntax “var mayor: Mayor” is just a declaration and the mayor variable has no defined value. So the compiler raises an error to forbid its use.
With the syntax “var mayor = Mayor()”, an instance of Mayor type is created (with default values given in the Mayor declaration) and is assigned to the variable mayor.


#4

The reason it sounds deceptively easy is because it is deceptive :slight_smile:
The problem question states

Have your town inform the mayor every time the property for population changes

The key here is to inform the mayor every time the population changes.

Here’s my solution (I could have used better function names in Mayor.swift:

/// Town.swift
var mayor: Mayor?
var population = 5_422 {
    didSet(oldPopulation) {
      mayor?.populationChanged(from: oldPopulation, to: population)
      if population < oldPopulation {
        print("The population has changed to \(population) from \(oldPopulation)")
      }
    }
  }

/// Mayor.swift
struct Mayor {
  func populationChanged(from old: Int, to new: Int) {
    if new < old {
      print("I'm deeply saddened to hear about this latest tragedy. I promise that my office is looking into the nature of this rash violence.")
    }
  }
}

#5

Hi, can you explain please why the Mayor insance have to be optional like: var mayor: Mayor?


#7

Gold Challenge

I solved the challenge in this manner because of the wording used in the description: “An instance of the Mayor type will naturally get nervous…” and “Increment the anxietyLevel property every time a Mayor instance is notified of a Zombie attack.”

I could be wrong but this is how I took it…

I added the below code to my files:

//  Mayor.swift
struct Mayor {
    
  private var anxietyLevel = 0
    
   mutating func increaseAnxietyLevel(by amount: Int?) {
        if amount != nil {
           //"Increment the anxietyLevel property"
            anxietyLevel += amount!
            /// added print statement to check the anxietyLevel increased
            print("Mayor anxiety level: \(anxietyLevel)")
            
        }
    }
 }

//  Zombie.swift
class Zombie: Monster {

   //“An INSTANCE of the MAYOR TYPE will naturally get nervous..."
   var notifyMayor = Mayor() 

   //"ZOMBIE ATTACK"
   final override func terrorizeTown() {
      // "Mayor instance is notified..."
       notifyMayor.increaseAnxietyLevel(by: 1)
       
        super.terrorizeTown()

    }
   
}

In the main.swift file: .terrorizeTown() on an instance of Zombie will increment the Mayor anxietyLevel property by 1.


#8

My silver challenge idea:

Main code:

var myTown = Town()
var theMayor = Mayor()
theMayor.name = "Tom Brown"
myTown.mayor = theMayor
if let thisMayor = myTown.mayor?.name {
    print("The Mayor is \(thisMayor).")
}
let myTownSize = myTown.townSize

Mayor struct code:

struct  Mayor {
    var name: String?

    func sad() {
        if let thisMayor = name { //It needs a specific mayor assigned to the name to continue
            print("A special message from Mayor \(thisMayor): I am deeply saddened to hear about this latest tragedy.  I promise that my office is looking into the nature of this rash of violence.")
    }
}
}

Town code:

struct Town {
    var mayor: Mayor? 
    static let Region = "South"
    var population = 5_422 {
        didSet(oldPopulation) {
                if population < oldPopulation {
                    print("The population has decreased to \(population) from \(oldPopulation).")
                    self.mayor?.sad()
           }
      }
}

#9

On the Gold Challenged, I’m not getting it to work exactly like I want to.

Like someone else mentioned, I want to had a variable and method in Mayor to establish the anxiety level and to increment that value with with the method. Using a setter was not an option because you can’t have an initial value with a getter/setter, so a method was the only way I saw to do this:

struct  Mayor {
    var name: String?
    var anxietyLevel = 0

    func sad() {
        if let thisMayor = name { //It needs a specific mayor assigned to the name to continue
            print("A special message from Mayor \(thisMayor): I am deeply saddened to hear about this latest tragedy.  I promise that my office is looking into the nature of this rash of violence.")
        }
    }
    mutating func anxious() {
            anxietyLevel += 1
        if let aMayor = name {
             print("Mayor: \(aMayor), Anxiety Level: \(anxietyLevel).")
        }
    }
}

My idea was to trigger the anxiety level change from the Zombie class, so I just added an extra line of code to trigger the method in the Mayor structure. The one extra line is: " town?.mayor?.anxious()". I had a ton of difficulty trying to figure out how to reference the correct instance of the particular town’s mayor, but this appears to do it.

class Zombie: Monster { // "Zombie" inherits from "Monster" class
    var walksWithLimp = true
//    internal private(set) var isFallingApart = false // getter should be internal and setter is private
    private(set) var isFallingApart = false // access control defaults to internal
    override class var spookyNoise: String { //a computed type property for a zombie.
        return "Brains..."
    }
    
    final override func terrorizeTown() { // final indicates that subclasses can't override further.
        if let thisTown = town {
            if thisTown.population > 10 && isFallingApart != true {
        town?.changePopulation(by: -10) // optional chaining -- makes sure that it is safe to call a function on the "town" instance.
        super.terrorizeTown() // "super" accesses the superclasses' implementation of a method.
        town?.mayor?.anxious()
            } else {
                town?.population = 0 // optional chaining.
                print("They're all dead!") 
            }
        }
    }
}

If you have the Zombie terrorize the town, the anxiety level of the particular mayor in question does increment up. The problem is that if I try to check on the anxiety level from “main,” I cannot get the code to show me anything other than “0.” Here is the code I use in “main” to identify the town and mayor and then attempt to check on the the mayor’s anxiety level:

var myTown = Town()
var theMayor = Mayor()
theMayor.name = "Tom Brown"
myTown.mayor = theMayor
if let thisMayor = myTown.mayor?.name {
    print("The Mayor is \(thisMayor).")
}
let myTownSize = myTown.townSize

let fredTheZombie = Zombie()
fredTheZombie.town = myTown
var fredsName = "Fred the Zombie"
fredTheZombie.name = fredsName 
fredTheZombie.terrorizeTown()
fredTheZombie.terrorizeTown()
if let anxietyTest = myTown.mayor?.anxietyLevel, let boss = myTown.mayor?.name {
    print("The anxiety level of \(boss), is \(anxietyTest).")
}

No matter what I do, I cannot get the mayor’s anxiety level to show up by a call from “main.” It always shows “0”.


#10

Orangecicle, I think you need to get the town from the zombie rather than using the myTown variable.

This is what I have

fredTheZombie.town?.mayor.howDoYouDo()


#11

I’d appreciate any hints/suggestions for the Silver Challenge. I’m not sure how use the town and population properties in Mayor struct since they’re value types and can’t be inherited.

Here’s the mayor.swift code so far (I know it’s incorrect):


import Foundation

struct Mayor {
    var population: Int
    var decreased: Int
    
    mutating func informTheMayor(if popDec: Int) {
        if let mayorOfTown = town?.population?.mayor {
            if population -= 1 {
                return "I'm deeply saddened to hear about this latest tradgedy. I promise that my office is looking into the nature of this rash of violence."
                else if population += 1 {
                  return
                }
            }
        }
    }
}

//Since Mayor is a struct and therefore a value type, how do I reference town and population?

Here’s town.swift so far:


var mayor = Mayor()

Any hints/suggestions for this challenge?


#12

You’re over thinking your Mayor implementation. The only thing your Mayor struct should be doing is apologizing if your population decreases. Per the description, your town type should have a variable mayor that holds an Instance of the Mayor type. So the Silver challenge is really a continuation of the bronze challenge. Let me know if those are enough hints to get you pointed in the right direction or if you need more help.


#13

I believe the error is because you provide no default value for mayor, and that your struct currently doesn’t have a initializer setup. If you had one, you’d have no error. Alternatively you could just declare it as an optional and then

var mayor: Mayor?
or
var mayor: Mayor!

would be fine.


#14

@Tiberius

Okay I’ve changed it to apologize if the population decreases and it gives me the error:
Missing return in a function expected to return ‘String’

Here’s what I have now:

//Mayor.swift

import Foundation

struct Mayor {
    
    mutating func informTheMayor(from population: Int, to newPopulation: Int) -> String {
        if newPopulation < population {
        return "I'm deeply saddened to hear about this latest tradgedy. I promise that my office is looking into the nature of this rash of violence."
        }
    }
}

I must be still missing something?


#15

The function’s signature says that the function returns a string value, but there is no return statement returning a string value after the if-statement.

The function needs to return a string after the if-statement.


#16

Okay I was confused because I thought the string was doing that but I obviously forgot to account for false. This is sufficient right?

//Mayor.swift

import Foundation

struct Mayor {
    
    mutating func informTheMayor(from population: Int, to newPopulation: Int) -> String {
        if newPopulation < population {
        return "I'm deeply saddened to hear about this latest tradgedy. I promise that my office is looking into the nature of this rash of violence."
        }
        else {
            return " "
        }
    }
}


I was trying to just exit the function if it’s false but since I specified the return as String, it won’t let me

It doesn’t give me an error anymore


#17

Completely overlooked accounting for false. Big mistake.


#18

Learning from mistakes is one of the key requirements for becoming a good programmer. :slight_smile:


#19

As my old software engineering professor taught us:

Good programming comes from experience.

Experience comes from bad programming.


#20

You’re overthinking this function again. The town struct is already doing this check for you, so you don’t need to duplicate the logic. This is as simple as:

func mayorApologizes(){
        print("I'm deeply saddened to hear about this latest tradgedy. I promise that my office is looking into the nature of this rash of violence.")
}

And then you add this function in your town.swift file in the appropriate place of the didSet section of the population variable.


#21


Hey guys can you help me with my silver challenge, it’s not being printed in the console.