Zombie Blokade

Zombie Blokade preview image

1 collaborator

Hobbes286x286 Rik Blok (Author)

Tags

agent-based model 

Tagged by Rik Blok over 10 years ago

diffuse-in-bounds.nls 

Tagged by Rik Blok over 10 years ago

epidemic 

Tagged by Rik Blok over 10 years ago

game 

Tagged by Rik Blok over 10 years ago

high-scores.nls 

Tagged by Rik Blok over 10 years ago

infection 

Tagged by Rik Blok over 10 years ago

output-wrap.nls 

Tagged by Rik Blok over 10 years ago

per-capita-tau-leaping.nls 

Tagged by Rik Blok over 10 years ago

random-walk.nls 

Tagged by Rik Blok over 10 years ago

sigfigs.nls 

Tagged by Rik Blok over 10 years ago

updownhill-in-bounds.nls 

Tagged by Rik Blok over 10 years ago

zombies 

Tagged by Rik Blok over 10 years ago

Model group UBC ISCI 422 | Visible to everyone | Changeable by the author
Model was written in NetLogo 5.0.4 • Viewed 930 times • Downloaded 53 times • Run 0 times
Download the 'Zombie Blokade' modelDownload this modelEmbed this model

Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)


Zombie Blokade

A [NetLogo] game by Rik Blok.

Oh no! Patient zero has turned into a zombie! Can you slow down the inevitable plague? Place or remove bloks to isolate zombies from humans. Act fast: proximity to humans drives zombies into a frenzy and panics humans, making the outbreak harder to contain!

Bloks impede zombies and humans. Unfortunately, bloks degrade naturally over time. Also, frenzied zombies and panicked humans can break through bloks, destroying them. Be careful where you try to place bloks; they can't be dropped on people (living or undead).

Movement

Zombies are attracted by the scent of humans. When calm, humans tend to congregate but when panicked they simply flee zombies. Neither humans or zombies can pass through bloks. (But panicked/frenzied individuals can break them.) Use the mouse to build a blokade to keep the humans and zombies apart.

How it works

Zombie Blokade contains a stochastic, spatial, SI (susceptible-infected) epidemic model. There are four kinds of agents:

  • CH = Calm Human (relaxed)
  • PH = Panicked Human (excited)
  • DZ = Docile Zombie (relaxed)
  • FZ = Frenzied Zombie (excited)

Changes occur when nearby agents interact according to these reactions.

Calm humans encountering a zombie may panic (if they survive):

  • CH + DZ → PH + DZ @ rate 1
  • CH + FZ → PH + FZ @ rate 1

But eventually they'll calm back down:

  • PH → CH @ relaxation-rate

Likewise, docile zombies will frenzy when they cross paths with a human:

  • DZ + CH → FZ + CH @ rate 1
  • DZ + PH → FZ + PH @ rate 1

But they too will relax back to their docile state eventually:

  • FZ → DZ @ relaxation-rate

Unfortunately, the living don't always survive zombie attacks. Encounters send attacking zombies into a frenzy and victims turn into docile zombies. In these interactions, excited (panicked/frenzied) have an advantage over relaxed (calm/docile) individuals:

  • CH + DZ → DZ + FZ @ rate infection-rate
  • CH + FZ → DZ + FZ @ rate infection-rate x excited-advantage
  • PH + DZ → DZ + FZ @ rate infection-rate / excited-advantage
  • PH + FZ → DZ + FZ @ rate infection-rate

Bloks are not permanent. They degrade naturally with an expected lifespan of 100 seconds:

  • blok → nothing @ rate 0.01

And finally, excited (panicked/frenzied) individuals can break through and destroy nearby bloks in their fury:

  • blok + PH(nearby) &rarr --> PH(nearby) @ rate 0.1
  • blok + FZ(nearby) &rarr --> FZ(nearby) @ rate 0.1

Scoring

Together, the above reactions fully describe the transitions between states. A simple "tau-leaping" method is used to decide which reactions occur over time. For comparison, [NetLogo]'s System Dynamics Modeler represents the same reactions assuming a large, "well stirred" population.

You can change the outcome by adding/removing bloks to keep humans and zombies separated. The number of humans you keep alive is plotted over time, compared against the system dynamics expectation. Your score is the total (blue) area between the curves.

Things to try

The blue curve shows the actual outcome and the grey curve shows the expected outcome if the population is "well stirred". Can you check that they're consistent? How would you change the model code to "stir" the population?

Maybe dropping bloks on individuals should be allowed. But bloks are heavy so they'd kill living humans they landed on. Zombies are already (un)dead so they'd just be trapped until the blok was removed. How would you add this to the code?

If only the humans could find some weapons to fight back! A shotgun or a chainsaw would sure help. I wonder how adding weapons would change the game?

References

[[Gibson2000]] Gibson, Michael A. and Bruck, Jehoshua. Efficient Exact Stochastic Simulation of Chemical Systems with Many Species and Many Channels. J. Phys. Chem. A, 104(9): 1876-1889. doi:10.1021/jp993732q. 2000.

[[NetLogo]] Wilensky, U. NetLogo. http://ccl.northwestern.edu/netlogo/. Center for Connected Learning and Computer-Based Modeling, Northwestern University. Evanston, IL. 1999.

Comments and Questions

High scores don't save on website.

Sorry, high scores can't be saved on the website. If you 'Download this model' and run it natively in NetLogo you will be able to save your high scores.

Posted over 10 years ago

Click to Run Model

;==================== begin zombie-blokade.nlogo ===============================
; zombie-blokade - a simple game to demonstrate agent-based modeling
;
; By Rik Blok, 2013 
;
; This is free and unencumbered software released into the public domain.
;
; Anyone is free to copy, modify, publish, use, compile, sell, or
; distribute this software, either in source code form or as a compiled
; binary, for any purpose, commercial or non-commercial, and by any
; means.
;
; Revisions:
;
;   2013-09-05 - initial release by Rik Blok
;-------------------------------------------------------------------------------


__includes
[ "diffuse-in-bounds.nls"      ; Same as diffuse but with boundaries.
  "high-scores.nls"            ; Load/save high scores in a file.
  "output-wrap.nls"            ; Same as output-print but wraps printed output.
  "per-capita-tau-leaping.nls" ; Approximate Gillespie's SSA.
  "random-walk.nls"            ; Random walk for individuals.
  "sigfigs.nls"                ; Like precision but specifies number of significant figures.
  "updownhill-in-bounds.nls"   ; Like uphill/downhill but with boundary constraints.
]


extensions [ sound ]


breed [ calm-humans      calm-human      ] ; relaxed humans
breed [ panicked-humans  panicked-human  ] ; excited humans
breed [ docile-zombies   docile-zombie   ] ; relaxed zombies
breed [ frenzied-zombies frenzied-zombie ] ; excited zombies
breed [ bloks            blok            ]


patches-own
[ human-scent  ; diffusing scent zombies use to track humans
  zombie-scent ; diffusing scent humans use to avoid zombies
]


globals
[ diffusion-const      ; how fast humans/zombies move (> 0)
  excited-advantage    ; how much better is it to be excited vs relaxed? (>= 1)
  excited-scent        ; how much stronger do excited smell? (>= 1)
  excited-speed        ; how much faster do excited move? (>= 1)
  inaccuracy           ; determines dt in tau-leaping algorithm (> 0, < 1)
  infection-rate       ; rate of zombie attacks (> 0)
  is-running?          ; false on first pass through go, otherwise true
;  last-mouse-patch     ; remember which patch mouse was just on
  mouse-adds-bloks?    ; if true, mouse presses will add bloks, otherwise remove them
  relaxation-rate      ; how fast do zombies/humans relax from excited state? (> 0)
  scent-decay-rate     ; how fast scents die (> 0, <= 1)
  scent-dispersal-rate ; how fast scents disperse (> 0, <= 1)
  score                ; keep score to make game funner
  starting-zombies     ; how many starting zombies? (>= 1)
  time-to-cross-patch  ; typical ticks needed to cross one patch given diffusion-const
  turn-excited         ; amount of random turning when excited (>0, <= turn-relaxed)
  turn-relaxed         ; amount of random turning when relaxed (>= turn-excited)
  zombie-color         ; color to distinguish zombies from humans
]

to startup
  reset  ; Might as well setup immediately when model loaded.
end 

to reset
; On model load or when user presses reset button.
  clear-all
  reset-ticks
  output-wrap 39 ; Set output box to wrap at column.
  output-wrap "ZOMBIE BLOKADE\n"
  output-wrap 
  ( word "Use white bloks to protect the humans (brown) from zombies (red). "
    "Bloks decay over time so watch out!\n"
  )
  output-wrap 
  ( word "Your score will be the blue area in the plot below -- "
    "the difference between the humans you keep alive versus "
    "the expected amount.\n"
  )
  
  ; Set defaults.
  set-default-shape turtles "person"
  set-default-shape bloks   "box"
  set diffusion-const 0.01 ; how fast do individuals move?
  set excited-advantage 2 ; advantage of being excited (>=1) over relaxed
  set excited-scent 1 ; smell of excited vs relaxed (>= 1)
  set excited-speed 4 ; running speed vs walking speed (=1)
  set inaccuracy 0.1 ; will be automatically adjusted to achieve desired speed
  set infection-rate 1 ; rate of zombie attacks (> 0)
  set is-running? false
;  set last-mouse-patch nobody
  set mouse-adds-bloks? false ; but will toggle to true (and print instructions) below.
  set relaxation-rate 0.25
  set scent-decay-rate 1 ; how fast scents decay (>0, <= 1)
  set scent-dispersal-rate 1 ; how fast scents disperse in space (>0, <= 1)
  set starting-zombies 1 ; how many zombies initially? (>= 1)
  set time-to-cross-patch random-walk-time-patch diffusion-const
  set turn-excited 15  ; how much to randomly turn when excited (<= turn-relaxed)
  set turn-relaxed 180 ; how much to randomly turn when relaxed (>= turn-excited)
  set zombie-color red + 0.75
  read-high-scores "zombie-blokade-high-scores.txt" 10
  toggle-mouse ; set mouse-adds-bloks? to true and print instructions

  ; Create initial populations.
  ; Make a green background.
  ask patches [ set pcolor green - 1 ]
  ; add bloks all around edges.
  ask patches with [ pxcor = min-pxcor or pxcor = max-pxcor or pycor = min-pycor or pycor = max-pycor ]
  [ sprout-bloks 1 [ set color white ]
  ]
  ; Place a zombie in the center.
  ask patch 0 0
  [ sprout-frenzied-zombies starting-zombies
    [ set color zombie-color
      set label "Grr! Argh!" 
      set size 1.5
    ]
  ]
  ; Add humans everywhere else.
  ask patches with [ not any? turtles-here ]
  [ sprout-calm-humans 1
    [ set color brown - 2 + random 7
    ]
  ]
  
  ; Place reactions here.
  ; 4 reactions for state of excitation (relaxed<-->excited for zombies and humans)
  ; where relaxed = calm/docile and excited = panicked/frenzied.
  ; Reaction: calm-human + any-zombie -> panicked-h + any-z @ rate 1
  add-per-capita-event calm-humans  
    task [ count docile-zombies-here + count frenzied-zombies-here ] 
    task [ set breed panicked-humans set size 1.5 ]
  ; Reaction: panicked-human -> calm-h @ relaxation-rate
  add-per-capita-event panicked-humans  
    task [ relaxation-rate ] 
    task [ set breed calm-humans set size 1 ]
  ; Reaction: docile-zombie + any-human -> frenzied-z + any-h @ rate 1
  add-per-capita-event docile-zombies  
    task [ count calm-humans-here + count panicked-humans-here ] 
    task [ set breed frenzied-zombies set size 1.5 ]
  ; Reaction: frenzied-zombie -> docile-z @ relaxation-rate
  add-per-capita-event frenzied-zombies  
    task [ relaxation-rate ] 
    task [ set breed docile-zombies set size 1 ]
  ; 4 reactions for zombie bites.  
  ; Excited humans have advantage against relaxed zombies and vice versa.
  ; Reaction: calm-human + docile-zombie -> docile-z + frenzied-z @ rate infection-rate
  add-per-capita-event calm-humans  
    task [ infection-rate * count docile-zombies-here ] 
    task 
    [ sound:play-drum "CABASA" volume
      set breed frenzied-zombies set color zombie-color set size 1.5 
    ]
  ; Reaction: calm-human + frenzied-zombie -> docile-z + frenzied-z @ rate advantage * infection-rate
  add-per-capita-event calm-humans  
    task [ infection-rate * excited-advantage * count frenzied-zombies-here ] 
    task 
    [ sound:play-drum "CABASA" volume
      set breed docile-zombies set color zombie-color 
    ]
  ; Reaction: panicked-human + docile-zombie -> docile-z + frenzied-z @ rate infection-rate / advantage
  add-per-capita-event panicked-humans  
    task [ infection-rate / excited-advantage * count docile-zombies-here ]
    task 
    [ sound:play-drum "CABASA" volume
      set breed frenzied-zombies set color zombie-color 
    ]
  ; Reaction: panicked-human + frenzied-zombie -> docile-z + frenzied-z @ rate infection-rate
  add-per-capita-event panicked-humans  
    task [ infection-rate * count frenzied-zombies-here ] 
    task 
    [ sound:play-drum "CABASA" volume
      set breed docile-zombies set color zombie-color set size 1 
    ]
  ; Destroy each blok randomly once per 100 ticks.
  ; Reaction: blok --> nothing @ rate 0.01
  add-per-capita-event bloks 
    task [ 0.01 ] 
    task 
    [ sound:play-drum "BASS DRUM 1" volume
      die 
    ]
  ; Bloks also destroyed by excited neighbors @ rate once per 10 ticks.
  ; Reaction: blok + (panicked-human or frenzied-zombie nearby) --> panicked-h or frenzied-h @ rate 0.1
  add-per-capita-event bloks 
    ; use in-radius to only look for zombies "touching" this blok.
    ; radius=0.8 is just bigger than distance to patch corner
    task [ 0.01 * ( count frenzied-zombies in-radius 0.8 + count panicked-humans in-radius 0.8 ) ] 
    task 
    [ sound:play-drum "BASS DRUM 1" volume
      die 
    ]
    
  ; system-dynamics tool gives (non-spatial) expectation to compare results against.
  system-dynamics-setup
  set-plot-x-range  -50 10
  set-plot-y-range 0 count calm-humans + count panicked-humans
  update-plots
end 

to play
; Called repeatedly by 'play' button.
  ; Check if game over.
  if not (any? calm-humans or any? panicked-humans)
  [ if is-running? 
    [ set is-running? false
end -game
    ]
    output-wrap "Press 'reset' to play again."
    stop
  ]

  ; Just for fun, mark last human.
  if (count calm-humans + count panicked-humans) = 1
  [ ask calm-humans     [ set label "I am Legend!" follow-me ]
    ask panicked-humans [ set label "I am Legend!" follow-me ]
  ]

  ; Check if game starting.
  if not is-running? 
  [ set is-running? true
    output-wrap "Try to form a blokade between the humans and zombies!\n"
    output-wrap 
    ( word "Click on a blok to move it away or "
      "click on an empty space to move a random blok there.  "
      "Be careful: every placed blok comes from somewhere else!\n"
    )
  ]
  
  ; Do actions.
  every dt  ; Try to match model ticks and real time.
  [ advance-frame ; Advance model time by dt and choose new dt.
  ]
  
  ; Update plot, but no more than 10 times per second.
  every 0.1
  [ let xmax precision (ticks + 1) 0 
    set-plot-x-range ( xmax - 60 ) xmax ; make sure plot auto scale is off
    update-plots
  ]
  
  handle-mouse-activity
end 

to toggle-mouse
; Swap behaviour of mouse clicks: add <--> remove bloks.  
  set mouse-adds-bloks? not mouse-adds-bloks?
  output-wrap 
  ( word "Mouse clicks move bloks "
    ifelse-value mouse-adds-bloks? [ "into" ][ "away from" ]
    " the chosen location. Click 'toggle' in the top right to switch.\n"
  )
end 

to advance-frame
; Carry out reactions, spread scents, move individuals, and keep score.
  ; time this "frame".
  reset-timer 
  ; do local reactions (relaxation, excitation, attacks, etc.)
  let new-dt per-capita-tau-leap inaccuracy dt
  ; Don't permit dt=0 because it screws up random-walk.
  if new-dt = 0
  [ ;set new-dt ( 1 - inaccuracy ) * time-to-cross-patch
    set new-dt dt * 1.1
  ]
  set dt new-dt
  if dt > 0.1 [ set dt 0.1 ]

  let is-in-bounds? task [ not any? bloks-here ]
  spread-scents is-in-bounds?
  follow-scents is-in-bounds?
  
  tick-advance dt
  set score score + score-increment
  if score < 0 [ set score 0 ]
  
  ; Try to tune simulation speed to 1 tick per second (ie. timer = dt)
  ; regardless of speed slider and computer speed.
  if timer > 1.1 * dt  ; too slow, make bigger jumps
  [ set inaccuracy inaccuracy * 1.1
  ]
  if timer < 0.9 * dt  ; too fast, run slower with smaller jumps
  [ set inaccuracy inaccuracy * 0.9
    wait dt - timer
  ]
  ; don't allow simulation to run too slow (no cheaters! :) )
  if inaccuracy > 0.25 [ set inaccuracy 0.25 ]
  
  ; Advance non-spatial system-dynamics model by dt.
  system-dynamics-go
end 

to spread-scents
; Spread scents for tracking.
; Parameters:
[ is-in-bounds? ; task that reports true if scent can spread into patch
]
  ask calm-humans      [ set human-scent human-scent + dt ]
  ask panicked-humans  [ set human-scent human-scent + dt * excited-scent ]
  ask docile-zombies   [ set zombie-scent zombie-scent + dt ]
  ask frenzied-zombies [ set zombie-scent zombie-scent + dt * excited-scent ]
  diffuse-in-bounds  "human-scent"  (scent-dispersal-rate * dt)  is-in-bounds?
  diffuse-in-bounds  "zombie-scent" (scent-dispersal-rate * dt)  is-in-bounds?
  ask patches 
  [ set human-scent  human-scent  * (1 - scent-decay-rate * dt)
    set zombie-scent zombie-scent * (1 - scent-decay-rate * dt)
  ]
end 

to follow-scents 
; Face towards/away from scents as desired and move (with some randomness).
; Parameters:
[ is-in-bounds? ; task that reports true if agent can move into patch
]
  ; Relaxed agents avoid overcrowding.
  let relaxed-is-in-bounds? task [ not any? bloks-here and count turtles-here < 2]
  ; Excited agents are too busy chasing/fleeing to care much about crowding.
  let excited-is-in-bounds? task [ not any? bloks-here and count turtles-here < 8]
  ; Humans want to be together...
  ask calm-humans
  [ set heading towards-uphill-in-bounds task [ human-scent ]  relaxed-is-in-bounds?
  ]
  ; ...except when they're fleeing zombies.
  ask panicked-humans
  [ set heading towards-downhill-in-bounds task [ zombie-scent ]  excited-is-in-bounds?
  ]
  ; Zombies sniff out and chase humans.
  ask docile-zombies
  [ set heading towards-uphill-in-bounds task [ human-scent ]  relaxed-is-in-bounds?
  ]
  ask frenzied-zombies
  [ set heading towards-uphill-in-bounds task [ human-scent ]  excited-is-in-bounds?
  ]
  
  ; Movement.  Add some randomness to direction and move forward.
  let relaxed-jump random-walk-jump-size diffusion-const dt
  let excited-jump relaxed-jump * excited-speed
  ; Excited move less randomly than relaxed.
  let tight-angle random-walk-cone-from-per-tick turn-excited dt
  let wide-angle  random-walk-cone-from-per-tick turn-relaxed dt
  random-walk-in-cone-bounds calm-humans      relaxed-jump wide-angle  is-in-bounds?
  random-walk-in-cone-bounds panicked-humans  excited-jump tight-angle is-in-bounds?
  random-walk-in-cone-bounds docile-zombies   relaxed-jump wide-angle  is-in-bounds?
  random-walk-in-cone-bounds frenzied-zombies excited-jump tight-angle is-in-bounds?
end 

to-report score-increment
; Calculate and report score change. 
  ; Difference between actual vs. expected curves.
  let score-inc ( count calm-humans + count panicked-humans ) - (ch + ph) * ( count patches - count bloks )
  ; If running too slow, penalize player (should discourage slow speed slider cheat).
  if timer > dt [ set score-inc score-inc * ( dt / timer ) ]
  ; Score is time integral (blue area in plot).
  report score-inc * dt
end 

to handle-mouse-activity
; Respond to mouse activity.
  if mouse-inside? and mouse-down? ; click
  [ let mouse-patch patch mouse-xcor mouse-ycor
;    if mouse-patch != last-mouse-patch
;    [ 
    ask mouse-patch
      [ if-else mouse-adds-bloks?
        [ if not any? turtles-here
          [ ask one-of bloks [ move-to mouse-patch ]
            sound:play-drum "HI AGOGO" volume
          ]
        ]
        [ if any? bloks-here 
          [ ask bloks-here [ move-to one-of patches with [ not any? turtles-here ]]
            sound:play-drum "LOW AGOGO" volume
          ]
        ]
      ]
;      set last-mouse-patch mouse-patch ; don't repeat actions on this patch
;    ]
  ]
end 

to end-game
; Game is over.  Wrap up.
  set score precision score 0
  output-wrap ( word "Congratulations!  Your score is " score "!\n" )
  if is-high-score? score [ add-high-score score "You got a high score!  What is your name?" ]
  output-print "High scores:"
  output-print high-scores-list 25
end 

to-report volume
; Convert sound level (dB) to power.  
; Relationship: level = level_0 + 10 log_10 ( power / power_0 )
; where level = 0 is off and
; level = 1...16 dB corresponds to power = 4...127.
  if sound-level = 0 [ report 0 ] ; sound off
  ; else
  report round ( 127 * 10 ^ ( ( sound-level - 16 ) / 10 ) )
end 
;==================== end zombie-blokade.nlogo =================================

There are 2 versions of this model.

Uploaded by When Description Download
Rik Blok over 10 years ago Fixed sound when moving bloks away. Download this version
Rik Blok over 10 years ago Initial upload Download this version

Attached files

File Type Description Last updated
diffuse-in-bounds.nls extension Same as diffuse but with boundaries. over 10 years ago, by Rik Blok Download
high-scores.nls extension Load/save high scores in a file. Revision 2013-09-05. over 10 years ago, by Rik Blok Download
output-wrap.nls extension Same as output-print but wraps printed output. over 10 years ago, by Rik Blok Download
per-capita-tau-leaping.nls extension Approximate Gillespie's SSA. over 10 years ago, by Rik Blok Download
random-walk.nls extension Random walk for individuals. over 10 years ago, by Rik Blok Download
sigfigs.nls extension Like precision but specifies number of significant figures. over 10 years ago, by Rik Blok Download
updownhill-in-bounds.nls extension Like uphill/downhill but with boundary constraints. over 10 years ago, by Rik Blok Download
Zombie Blokade.png preview Preview for 'Zombie Blokade' over 10 years ago, by Rik Blok Download
zombie-blokade-high-scores.txt data High scores file. over 10 years ago, by Rik Blok Download

This model does not have any ancestors.

This model does not have any descendants.