;; The first three lines of this file were inserted by DrRacket. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "2017-fall-reader.rkt" "csc104")((modname sniffle) (compthink-settings #hash((prefix-types? . #f)))) ; CSC104 2017 Fall, Project II. ; ; A Program to Model and Visualize the Spread of an Infection. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; AFTER YOU HAVE IMPLEMENTED THE ENTIRE PROGRAM, play with it, adjusting the settings. ; ; Try to find some settings that produce a delicate balance of the spread of infection: ; when running it a few times with those settings can produce visibly different outcomes. ; Also try large and small values, and view the effects. ; Write a couple of paragraphs here summarizing some of your findings: ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; IMPLEMENTATION REQUIREMENTS. ; For each function: uncomment any commented-out check-expects, study them and the description ; of the function, and if you are asked to implement or fix the function then do so. ; Uncomment any definitions that use those functions. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Some list functions. (require (only-in racket take drop)) ; Imports two fast list functions. ; take : list number → list ; drop : list number → list ; These function take a list, and a number for how many elements to keep or remove: (check-expect (take (list "gnu" "fox" "bee") 2) (list "gnu" "fox")) (check-expect (drop (list "gnu" "fox" "bee") 2) (list "bee")) ; A function to make a list with a repeated element. ; constant-list : number any → list (check-expect (constant-list 3 "cat") (list "cat" "cat" "cat")) (check-expect (constant-list 3 "cat") (repeated identity "cat" 3)) ; Fix constant-list. (define (constant-list a-length an-element) (list)) ; Functions to move elements from the front to the back of a list, or vice versa. ; left-cycle : list number → list ; right-cycle : list number → list #;(check-expect (left-cycle (list "gnu" "fox" "bee") 0) (list "gnu" "fox" "bee")) #;(check-expect (left-cycle (list "gnu" "fox" "bee") 1) (list "fox" "bee" "gnu")) #;(check-expect (left-cycle (list "gnu" "fox" "bee") 2) (list "bee" "gnu" "fox")) #;(check-expect (left-cycle (list "gnu" "fox" "bee") 2) (append (list "bee") (list "gnu" "fox"))) #;(check-expect (right-cycle (list "gnu" "fox" "bee") 0) (list "gnu" "fox" "bee")) #;(check-expect (right-cycle (list "gnu" "fox" "bee") 1) (list "bee" "gnu" "fox")) #;(check-expect (right-cycle (list "gnu" "fox" "bee") 2) (list "fox" "bee" "gnu")) #;(check-expect (right-cycle (list "gnu" "fox" "bee") 2) (append (drop (list "gnu" "fox" "bee") (- 3 2)) (take (list "gnu" "fox" "bee") (- 3 2)))) ; Implement left-cycle. ; Implement right-cycle. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Managing the Statuses of the Subjects in the Simulation. ; The behaviour of the simulation depends on three numeric settings: ; ; Virulence - how likely an infectable person becomes infected ; Duration - how long an infected person stays infected ; Immunity - how long a person stays immune after an infection ends ; ; Those settings will be stored and passed around in a list. ; ; Using setting names for functions that access them helps us keep track of the order. ; ; virulence : list → number ; duration : list → number ; immunity : list → number (check-expect (virulence (list 20 10 15)) 20) (define virulence first) ; That could also have been defined by: #;(define (virulence some-settings) (first some-settings)) #;(check-expect (duration (list 20 10 15)) 10) ; Implement duration. #;(check-expect (immunity (list 20 10 15)) 15) ; Implement immunity. #;(check-expect (virulence-duration-immunity 20 10 15) (list 20 10 15)) ; Implement virulence-duration-immunity. ; Some initial settings for the simulation. #;(define initial-settings (virulence-duration-immunity 20 10 15)) ; Infection Status. ; ; A person is either: ; ; Infectable. ; This is represented by zero, and shown as a blue dot. ; ; Infected, with a certain number of days of infection left. ; This is represented by a number which is the *negative* of the number ; of days of infection left, and shown as a red dot. ; ; Immune, with a certain number of days of immunity left. ; This is represented by a *positive* number which is the number of days ; of immunity left, and shown as a green dot. ; Predicates to make reading the code more meaningful: ; infectable? : number → boolean ; infected? : number → boolean ; immune? : number → boolean #;(check-expect (infectable? -2) #false) #;(check-expect (infectable? 0) #true) #;(check-expect (infectable? 3) #false) #;(check-expect (infected? -2) #true) #;(check-expect (infected? 0) #false) #;(check-expect (infected? 3) #false) #;(check-expect (immune? -2) #false) #;(check-expect (immune? 0) #false) #;(check-expect (immune? 3) #true) ; Implement those three functions, and use them where appropriate when checking statuses later. ; A function to produce the initial list of unifected statuses, except for one person on the edge ; of the wrap-around world who starts infected for Duration days. ; initial-statuses : number number list → list #;(define (initial-statuses width height some-settings) (local [(define dots (* width height)) (define dots/2 (quotient dots 2))] (append (constant-list dots/2 0) (list (- (duration some-settings))) (constant-list (- (- dots dots/2) 1) 0)))) #;(check-expect (initial-statuses 2 3 (virulence-duration-immunity 20 10 15)) (list 0 0 0 -10 0 0)) ; A function to produce the colour representing a person's status. ; status-colour : number → list #;(check-expect (status-colour -2) (list 100 0 0)) #;(check-expect (status-colour 0) (list 0 0 100)) #;(check-expect (status-colour 3) (list 0 100 0)) ; Fix status-colour. #;(define (status-colour a-status) (cond [(infected? a-status) (list 0 0 0)] [else (list 0 0 0)])) ; A function to update a person's status. ; ; update-status : number → number ; ; If infectable, stay infectable. ; If infected, become infected for one less day, except if that makes the person uninfected ; then they become immune for the number of days of immunity specified in the settings. ; If immune, become immune for one less day. #;(check-expect (update-status 0 (virulence-duration-immunity 20 10 15)) 0) #;(check-expect (update-status -2 (virulence-duration-immunity 20 10 15)) -1) #;(check-expect (update-status -1 (virulence-duration-immunity 20 10 15)) 15) #;(check-expect (update-status 3 (virulence-duration-immunity 20 10 15)) 2) ; Fix update-status. #;(define (update-status a-status some-settings) a-status) ; A function to update everyone's duration of infection and immunity in a list of statuses. ; update-statuses : list-of-numbers → list-of-numbers #;(check-expect (update-statuses (list 3 -1 0 -2) (virulence-duration-immunity 20 10 15)) (list 2 15 0 -1)) #;(check-expect (update-statuses (list 3 -1 0 -2) (virulence-duration-immunity 20 10 15)) (local [(define (update a-status) (update-status a-status (virulence-duration-immunity 20 10 15)))] (list (update 3) (update -1) (update 0) (update -2)))) ; Fix update-statuses. #;(define (update-statuses some-statuses some-settings) some-statuses) ; A function to possibly infect a subject. ; ; Produce the status representing an infection of Duration days if: ; the status represents someone infectable, ; neighbour-0 or neighbour-1 or neighbour-2 or neighbour-3 is a status for someone infected, and ; the Virulence is more than this randomly chosen number: (random 100). ; Otherwise: leave the subject alone. ; ; infect : number number number number number list → number #;(check-expect (or (infectable? (infect 0 -1 2 -3 4 (virulence-duration-immunity 20 10 15))) (infected? (infect 0 -1 2 -3 4 (virulence-duration-immunity 20 10 15)))) #true) #;(check-expect (infectable? (infect 0 -1 2 -3 4 (virulence-duration-immunity 0 10 15))) #true) #;(check-expect (infected? (infect 0 -1 2 -3 4 (virulence-duration-immunity 100 10 15))) #true) #;(check-expect (infectable? (infect 0 1 -2 3 -4 (virulence-duration-immunity 0 10 15))) #true) #;(check-expect (infected? (infect 0 1 -2 3 -4 (virulence-duration-immunity 100 10 15))) #true) #;(check-expect (infect 0 1 -2 3 -4 (virulence-duration-immunity 100 10 15)) (- (duration (virulence-duration-immunity 100 10 15)))) #;(check-expect (infect 0 -1 2 -3 4 (virulence-duration-immunity 100 10 15)) (- (duration (virulence-duration-immunity 100 10 15)))) #;(check-expect (infect -1 0 -2 3 -4 (virulence-duration-immunity 20 10 15)) -1) #;(check-expect (infect 2 0 3 -4 5 (virulence-duration-immunity 20 10 15)) 2) #;(check-expect (infect 0 1 0 2 0 (virulence-duration-immunity 20 10 15)) 0) ; Due to the randomness, a check-expect that tests that an infectable subject can be infected ; is a bit tricky to do properly. Here is an approach that should rarely fail. #;(define (try-to-infect unused-parameter) (infect 0 -1 2 -3 4 (virulence-duration-immunity 20 10 15))) #;(check-expect (member? (- (duration (virulence-duration-immunity 20 10 15))) (map try-to-infect (range 0 10000 1))) #true) #;(check-expect (member? 0 (map try-to-infect (range 0 10000 1))) #true) ; Fix infect. #;(define (infect subject neighbour-0 neighbour-1 neighbour-2 neighbour-3 some-settings) subject) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Settings Management and big-bang Functions. ; ; These are all implemented: you can uncomment any commented-out definitions, and the big-bang ; expression that is at the end of the file. ; The width and height of the world. (define WIDTH 100) (define HEIGHT 100) ; The current settings and list of statuses are bundled together in a list. (define settings-and-statuses list) (define settings first) (define statuses second) (require (only-in racket map)) ; Imports a version of map that works with more than two lists. ; Update each person's status, possibly infecting some of them, based on the settings. ; day-tick : list → list #;(define (day-tick the-settings-and-statuses) (local [(define the-statuses (statuses the-settings-and-statuses)) (define the-settings (settings the-settings-and-statuses)) (define (infector status n-0 n-1 n-2 n-3) (infect status n-0 n-1 n-2 n-3 the-settings))] (settings-and-statuses the-settings (update-statuses (map infector the-statuses (left-cycle the-statuses 1) (right-cycle the-statuses 1) (left-cycle the-statuses WIDTH) (right-cycle the-statuses WIDTH)) the-settings)))) ; An image showing name and number, separated by a colon and space. ; setting->image : string number → image (define (setting->image name number) (string->image (string-append name ": " (number->string number)) 16 "black")) ; An image of the grid of the statuses, with settings drawn underneath. ; draw-statuses : list → image #;(define (draw-statuses the-settings-and-statuses) (above (colors->image (map status-colour (statuses the-settings-and-statuses)) WIDTH HEIGHT) (setting->image "virulence" (virulence (settings the-settings-and-statuses))) (setting->image "duration" (duration (settings the-settings-and-statuses))) (setting->image "immunity" (immunity (settings the-settings-and-statuses))))) ; Update the settings and reset the statuses, when a key is pressed. ; change-settings : list string → list #;(define (change-settings the-settings-and-statuses a-key) (local [(define the-settings (settings the-settings-and-statuses)) (define (with-new-settings virulence-change duration-change immunity-change) (local [(define new-settings (virulence-duration-immunity (+ (virulence the-settings) virulence-change) (+ (duration the-settings) duration-change) (+ (immunity the-settings) immunity-change)))] (settings-and-statuses new-settings (initial-statuses WIDTH HEIGHT new-settings))))] (cond [(equal? a-key "1") (with-new-settings -1 0 0)] [(equal? a-key "2") (with-new-settings +1 0 0)] [(equal? a-key "3") (with-new-settings 0 -1 0)] [(equal? a-key "4") (with-new-settings 0 +1 0)] [(equal? a-key "5") (with-new-settings 0 0 -1)] [(equal? a-key "6") (with-new-settings 0 0 +1)] [else (with-new-settings 0 0 0)]))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Uncomment the big-bang expression and run the file to play with the simulation! ; ; You can adjust the settings by pressing one of the keys: 1, 2, 3, 4, 5, or 6. ; Pressing any other key starts the simulation again, without changing the current settings. #;(big-bang (settings-and-statuses initial-settings (initial-statuses WIDTH HEIGHT initial-settings)) [on-tick day-tick] [to-draw draw-statuses] [on-key change-settings])