Introductory Activity:

A lot to do today, so get started by reviewing notes from Part 1 on what makes a good Moose class. Create a moose class and add the properties and their initial values to the class. Outline in comments the methods: canEat and liveForYear in code and discuss what should be in each method.

The function canEat() can be as complicated as you’d like. We wanted to model that a moose may not find itself in the part of the island that has good vegetation for some reason, so we settled on a randomNumber test to see if the moose found itself in the well-vegetated portion of the island, or not. This seems to work well.

Moose Class:

class Moose {
    var mooseAgeMaturity = 5 // age a moose needs to be to reproduce
    var mooseLongevityAge = 100 // number of years a moose will live naturally
    var age = 0
    var alive = true // moose is alive or dead
    var vegetationNeededToSurvive = 0.01 // each moose needs to eat 1% worth of the islands vegetation per year
    var mooseBirthRate = 0.75 // each moose will create 0.75 moose each year once it’s old enough to mate
    var starvation = 0
    var environment: Environment

    init(in environment: Environment, age: Int) {
        mooseAgeMaturity = 5
        let deltaLife = Int.random(in: -6 ... 6)  // randomize the longevity of a moose - simulation not very sensitive to this feature
        mooseLongevityAge = 20 + deltaLife
        self.age = age
        alive = true
        vegetationNeededToSurvive = 0.002  //  fraction of the region's vegetation a single moose eats per year
        mooseBirthRate = 0.75  // fraction of a moose another moose gives birth to, assuming it is mature enough
        starvation = 0    //  counter to keep track of how many years a moose has gone without sufficient food
        self.environment = environment
    }

    func canEat() -> Bool {
        //  current vegetation cover in the region is a fraction from 0.0 to 1.0
        //  randomize the probability that a moose finds itself in the vegetated fraction of the region and can eat sufficiently for a year
        let randomNumber = Double.random(in: 0.0 ... 1.0)
        if randomNumber > environment.vegCover {
            return false
        }
        return true
    }
    
    func liveForYear() {
        
        // If a moose doesn’t find food for two years in a row, it dies
        // If a moose survives until mooseLongevityAge, it dies
        
        if canEat() {
            age += 1
            environment.vegCover -= vegetationNeededToSurvive
        } else {
            starvation += 1
        }
        if starvation >= 2 { alive = false }
        if age >= mooseLongevityAge { alive = false }
        
    }
   
}

Environment Class:

class Environment {
    var vegCover = 1.0
    let vegatationRegrowthRate = 0.1  // rate at which vegetation regrows each year
    var mooseArray = [Moose]()  //  create an empty array of moose
    

    func updateVegetation() {
        //  grow vegetation cover for a year
        vegCover += vegatationRegrowthRate
        if vegCover > 1.0 { vegCover = 1.0 }
    }
    
    //  update simulation population
    func updateMoosePopulation() {
        
        var newMooseArray = [Moose]()
        var newMooseBabies = 0.0
        //  check and see if there are any dead moose, of so, remove them from the mooseArray
        for i in mooseArray.indices {
            let thisMoose = mooseArray[i]

            //  each moose experiences a year
            thisMoose.liveForYear()
           
            //  determine how many new moose will be born this year
            if thisMoose.alive {
                newMooseArray.append(thisMoose)
                if thisMoose.age >= thisMoose.mooseAgeMaturity {
                    newMooseBabies += thisMoose.mooseBirthRate
                }
            }
        }
        
        //  have moose reproduce
        let numberOfBabies = Int(newMooseBabies)
        //  add new moose babies to the herd
        for _ in 0 ..< numberOfBabies {
            newMooseArray.append(Moose(in: self, age: 0))
        }
        
        mooseArray = newMooseArray
    }
}

Summary Activity:

Ask the students:

In preparation for running our experiments with our model, can you think of a way we can make the Simulation class more generic and useful?

Ideas:

  • create an initializer that takes a numberOfYearsToSimulate as an input argument
  • create an initializer that takes an initial number of moose, some properties of those moose (longevity, fractionOfVegetationEatenPerYear, maturityAge, etc.)