Chapter 16, Gold Challenge Solution

Below is the complete project. It does not run because I wanted to verify the mayor’s anxiety level is private. Comment out the mayorsAnxiety declaration and final print() statement in main.swift, or remove the private access level from the mayor’s stored instance property to get the code to run.

//
//  main.swift
//  MonsterTown
//

import Foundation

var myTown = Town()
let myTownSize = myTown.townSize
print(myTownSize)
myTown.changePopulation(by: 1_000_000)
print("Size: \(myTown.townSize); population: \(myTown.population)")

let fredTheZombie = Zombie()
fredTheZombie.town = myTown
fredTheZombie.terrorizeTown()
fredTheZombie.town?.printDescription()
print("Victim pool: \(fredTheZombie.victimPool)")
fredTheZombie.victimPool = 500
print("Victim pool: \(fredTheZombie.victimPool)")

print(Zombie.spookyNoise)
if Zombie.isTerrifying {
    print("Run away!")
}
// When there is no town, there is no mayor. Use a default value of -1.
// This code does not compile because of access restrictions on the 
// mayor's anxiety.
let mayorsAnxiety = fredTheZombie.town?.mayor.anxietyLevel ?? -1
print("The mayor's anxiety level is \(mayorsAnxiety).")
//
//  Town.swift
//  MonsterTown
//

import Foundation

struct Town {
    static let world = "Earth"
    let region = "Middle"
    var mayor = Mayor()
    var population = 5_422 {
        didSet(oldPopulation) {
            if population <= oldPopulation {
                print("The population has changed to \(population) "
                      + "from \(oldPopulation).")
                mayor.sendThoughtsAndPrayers()
            }
        }
    }
    var numberOfStoplights = 4
    
    enum Size {
        case small
        case medium
        case large
    }
    
    var townSize: Size {
        switch population {
        case 0...10_000:
            return Size.small
            
        case 10_001...100_000:
            return Size.medium
            
        default:
            return Size.large
            
        }
    }
    
    func printDescription() {
        print("Populations: \(population); number of stoplights: "
              + "\(numberOfStoplights)")
    }
    
    mutating func changePopulation(by amount: Int) {
        population += amount
    }
}
//
//  Mayor.swift
//  MonsterTown
//

import Foundation

struct Mayor {
    private var anxietyLevel = 0
    
    mutating func sendThoughtsAndPrayers() {
        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.")
        anxietyLevel += 1
    }
}
//
//  Monster.swift
//  MonsterTown
//

import Foundation

class Monster {
    static let isTerrifying = true
    class var spookyNoise: String {
        return "Grrr"
    }
    var town: Town?
    var name = "Monster"
    var victimPool: Int {
        get {
            return town?.population ?? 0
        }
        set(newVictimPool) {
            town?.population = newVictimPool
        }
    }
    
    func terrorizeTown() {
        if town != nil {
            print("\(name) is terrorizing a town!")
        } else {
            print("\(name) hasn't found a town to terrorize yet...")
        }
    }
}
//
//  Vampire.swift
//  MonsterTown
//

import Foundation

class Vampire: Monster {
    var thralls = [Vampire]()
    
    override func terrorizeTown() {
        let population = town?.population ?? 0
        // Try to capture one of the townspeople.
        if population > 0 {
            town?.changePopulation(by: -1)
            thralls.append(Vampire())
        }
        super.terrorizeTown()
    }
}
//
//  Zombie.swift
//  MonsterTown
//

import Foundation

class Zombie: Monster {
    class override var spookyNoise: String {
        return "Brains..."
    }
    var walksWithLimp = true
    private(set) var isFallingApart = false
    
    func regenerate() {
        walksWithLimp = false
    }
    
    override func terrorizeTown() {
        if !isFallingApart {
            town?.changePopulation(by: -10)
        }
        super.terrorizeTown()
        regenerate()
    }
}