Treehouse Intermediate Course - Part 14 (Delegation 1)

So a slight change of plans - one more theoretical course before the practical one. I'm doing that because I get the feeling that delegation is going to be useful before the practical courses. So I'm going to do this one - a bit shorter than the last two. Some today before going to a barbecue!

Start Time - 12:15

Introduction to Design Patterns

During days of early software development, there were general problems, which have general solutions. These are called DESIGN PATTERNS! General and reusable, regardless of the domain.



Decorator pattern - structural pattern. This is concerned with how we add responsibilities to an object dynamically at run time, rather than at build time etc.

So a general intro!

Racing Horses

Cool, a link to a game!

Right source code copied over. I like the sound of this. There are horses with max speed. But they won't run at that all race. So their speed will be varied during the race by querying a speed.

So the random function I am going to change as that is old code... No, no need as I don't know the range!

Unfortunately there are issues with Pasan's outdated code...have used the question section to resolve one but apparently there is no 'Tracker'. Anyway, going to continue with this.

There is a class for Horse -

class Horse {
    let name: String
    let maxSpeed: Double
    var distanceTraveled = 0.0
    var currentLap = 1
    
    init(name: String, maxSpeed: Double) {
        self.maxSpeed = maxSpeed
        self.name = name
    }
    
    var currentSpeed: Double {
        let random = Double(arc4random())
        return random.truncatingRemainder(dividingBy: maxSpeed - 13) + 13
    }

}

And another one for Race -

class Race {
    let laps: Int
    let lapLength: Double = 300
    let participants: [Horse]
    
    let tracker = Tracker()
    
    
    lazy var timer: Timer = Timer(timeInterval: 1, repeats: true) { timer in
        self.updateProgress()
    }
    
    init(laps: Int, participants: [Horse]) {
        self.laps = laps
        self.participants = participants
    }
    
    func start() {
        // RunLoop.main.add(timer, forMode: .RunLoop.Mode.default)
        RunLoop.main.add(timer , forMode: .default)
        tracker.updateRaceStart(with: Date())
        print("Race in progress...")
    } // END func start()
    
    func updateProgress() {
        print("....")
        for horse in participants {
            horse.distanceTraveled += horse.currentSpeed
            
            if horse.distanceTraveled >= lapLength {
                horse.distanceTraveled = 0
                
                let lapKey = "\(Tracker.Keys.lapLeader) \(horse.currentLap)"
                if !tracker.stats.keys.contains(lapKey) {
                    tracker.updateLapLeaderWith(lapNumber: horse.currentLap, horse: horse, time: Date())
                }
                
                horse.currentLap += 1
                
                if horse.currentLap >= laps + 1 {
                    tracker.updateRaceEndWith(winner: horse, time: Date())
                    stop()
                    break
                }
            }
        }
    }
    
    func stop() {
        print("Race complete!")
        timer.invalidate()
        tracker.printRaceSummary()
    }

}


Lots of code for race! Various functions involved.

And then there are the actual horses -

let jubilee = Horse(name: "Jubilee", maxSpeed: 16)
let sonora = Horse(name: "Sonora", maxSpeed: 17)
let jasper = Horse(name: "Jasper", maxSpeed: 17)


let participants = [jubilee, sonora, jasper]


This allows the processes to keep running apparently. Rather than just at build time...

PlaygroundPage.current.needsIndefiniteExecution = true

Random values are involved. The issue at the moment is we don't know the result! We don't know who finished first in lap 1 etc. So there is going to be a new class. 

Here it is - 

class Tracker {
    
    struct Keys {
        static let raceStartTime = "raceStartTime"
        static let lapLeader = "leaderForLap"
        static let raceEndTime = "raceEndTime"
        static let winner = "winner"
    }
    
    var stats = [String: Any]()
    
    func updateRaceStart(with time: Date) {
        stats.updateValue(time, forKey: Keys.raceStartTime)
    }
    
    func updateLapLeaderWith(lapNumber number: Int, horse: Horse, time: Date) {
        let lapLead = "Horse: \(horse.name), time: \(time)"
        let lapLeadKey = "\(Keys.lapLeader) \(number)"
        
        stats.updateValue(lapLead, forKey: lapLeadKey)
    }
    
    func updateRaceEndWith(winner: Horse, time: Date) {
        stats.updateValue(winner.name, forKey: Keys.winner)
        stats.updateValue(time, forKey: Keys.raceEndTime)
    }
    
    func printRaceSummary() {
        print("***********")
        
        let raceStartTime = stats[Keys.raceStartTime]!
        print("Race start time: \(raceStartTime)")
        
        for (key, value) in stats where key.contains(Keys.lapLeader) {
            print("\(key): \(value)")
        }
        
        let raceEndTime = stats[Keys.raceEndTime]!
        print("Race end time: \(raceEndTime)")
        
        let winner = stats[Keys.winner]!
        print("Winner: \(winner)")
        
        print("***********")
    }
}

This is cool! We have an instance of the tracker in the race class - that's why it couldn't access 'Tracker' before. 

I love this! The combination of the race with random element and the different race and horse classes.  Really cool! It's given me an idea. After this delegation course, I AM going to do something similar with F1! I will try out different drivers, races and have some fun! Cool!

This is what gets printed at the moment - 

Race in progress...
....
....
....
....
....
....
....
....
....
....
....
....
....
....
....
....
....
....
....
....
....
Race complete!
***********
Race start time: 2019-08-17 11:41:20 +0000
leaderForLap 1: Horse: Sonora, time: 2019-08-17 11:41:41 +0000
Race end time: 2019-08-17 11:41:41 +0000
Winner: Sonora
***********

So a new RaceBroadcaster class! This will be for more descriptive reporting; as you can see, the above is VERY limited!

So there are key events during the race - positions at the end of each lap, key other things happening!
*For the f1 tracker idea, other events can be put in! Overtakes, crashes, engine failures etc. I love the possibilities here!

So this is where delegation comes in....

Unfortunately I have to stop here! A quick entry today. But more tomorrow evening. Really excited about making just an F1 tracker programme. Here is my idea - a few drivers e.g. six to start with. Several different races so that they favour different features! Races to include facts such as average speed, number of slow corners, middle speed corners etc. Drivers to have skills to reflect this. 

Finish Time - 12:57

Well that was short but sweet! Great example Pasan to go with this - thank you!

Comments

Popular posts from this blog

*Xcode Project Entry 2* F1 Quiz - part 1

Angela Yu Course Part 10 (up to lesson 112)

Angela Yu Xcode 12 Course - Part 7 (lectures 74 to 79)