powered by NetLogo

view/download model file: multi-patch-ecms.nlogo


This model demonstrates how cooperation (sharing) can be achieved through essentially selfish groups. Over time in each patch, groups of symbiotic relationships develop, and eventually collapse again. However new patches are seeded from other patches so that, on the whole, cooperation is maintained.

This version of the model maintains cooperation even with significant and continual injection of “strong cheaters” as defined in (Shutters and Hales 2012). It does so using a combination of (a) specialisation of skill (b) tag-based group formation and (c) being part of a mult-patch meta-population. These use only low-level abilities and could be implemented in quite simple entities.


Each agent can harvest food of a single type, but must have stores of all food types in order to survive. Agents within the same patch and similar tag values will share excess resources, giving a means of accessing other resources. Agents who gather enough resources reproduce, propagting their strategies (with some mutation); agents who fail to gather sufficient resources die. There is a small chance that mutation can occur during reproduction in tags, tolerances and skills.

The world is divided into a number of patches in a 2D grid. There is a small probability that agents can migrate to other patches (either adjacent ones or random ones based on a parameter).

Strong cheaters can be injected into the population in a number of ways: (a) by setting the probability that any new agent (either born or from outside) becomes a strong cheater (his is a one-way process - they cannot then mutate back again and their offspring are strong cheaters) or (b) by pressing the “Intro Cheat” or “Turn to Cheat” buttons which create/change a number of strong cheater agents in a single patch (determined by num-cheat).


Choose settings using the sliders.

Press setup to initialise the model, then “step” for a single step, or “go” for continuous running.

If you want to manually inject cheaters use the the “Intro Cheat” or “Turn to Cheat” buttons.



Notice that setting maxNumNew to 0 is catastrophic if you have a single patch - some random mutation of the population is essential to re-seed the patch once the population has collapsed. If you have multiple patches is can be set to 0 as seeding occurs from neighbouring patches. Enough patches makes the chance of simultaneous collaps of populations on all patches neglidgible.

Once mutual donation has started occurring (when there are enough overlapping individuals of each food type) the population takes of and a “tag group” forms. Eventually this group fails, followed by a short period of non-viability before a new group forms.




The “Tag Profile” is a visualisation of all the individuals that are alive at this instance. Each individual is represented as a horizontal line, whose centre is at the individual’s tag value and whose width is that of its tollerance. Given enough spare food each individual might donate some of its unneeded food to any other current individuals whose centre (i.e. tag) lies within its width. The bollour of the individual indicates its skill (i.e. the type of nutrition it can directly harvest). Clearly to be viable there must be some indivuals of each kind in each viablew tag group. The horizontal position of the lines indicate the age of the individual. Individuals of the same age are spread out a little horizontally so they can be seen.

The “Agent Atributes” graph shows the population size, the avereage tollerance of individuals and the rate at which individuals donate to others.

The “Subpopulations” graph shows the number of individuals of each skill type as a seperate line of dots (one colour for each type). Once a cooperative (i.e. mutally donating) tag group forms you see the population of all types rise and then oscilate with respect of each other until the group eventually collapses again to a situation of non-viability.

The Turtle view is another visualisation of the current population. Each individual is represented by a seperate turtle their: x-position being their tag, y-position being their tolerance (up to the maximum value), their colour being their skill type, their size representing the average size of their stores, and the direction indicating their age (from veritcally upwards for age 0 and rotating to the right each time interval until almost vertical again at their maximum age).

Allong the bottom of the screen are a set of graphs etc. for a single patch (the one in slightly darker grey in the world).


This model is at page: http://cfpm.org/models/multi-patch-ecms.html

This model is described in the paper:

Edmonds, B. (2013) Multi-Patch Cooperative Specialists With Tags Can Resist Strong Cheaters. In Rekdalsbakken, W., Bye, R.T. and Zhang, H. (eds), Proceedings of the 27th European Conference on Modelling and Simulation (ECMS 2013), May 2013, Alesund, Norway. European Council for Modelling and Simulation, 900-906. (http://cfpm.org/cpmrep220.html)

It is a multi-patch version of the single-patch version of this model that is described in:

Edmonds, B. (2006) The Emergence of Symbiotic Groups Resulting From Skill-Differentiation and Tags. Journal of Artificial Societies and Social Simulation, 9(1):10. (http://jasss.soc.surrey.ac.uk/9/1/10.html).

It is tolerant to the continual injection of “strong cheaters” as defined in:

Shutters, S. T. & Hales, D. (2013) Tag-Mediated Altruism is Contingent on How Cheaters Are Defined. Journal of Artificial Societies and Social Simulation, 16(1):4 http://jasss.soc.surrey.ac.uk/16/1/4.html.

This model came out of many discussions with David Hales which started when we were trying to understand a model published as:

Riolo, R. L., Cohen, M. D. and Axelrod, R. (2001) Evolution of cooperation without reciprocity. Nature, 411:441-443.

Our analysis and critique of this model was:

Edmonds, B. and Hales, D. (2003) Replication, Replication and Replication - Some Hard Lessons from Model Alignment. Journal of Artificial Societies and Social Simulation, 6(4):11 (http://jasss.soc.surrey.ac.uk/6/4/11.html)

The single-patch model and this multi-patch version are an attempt to produce a model of tag-based cooperation which did not suffer from the defects of the Riolo et al model, but showed genuiine tag-based cooperation.


globals [ reproductionNoiseSD maxPop shares donations possibleDonations donationRate color-set turnInc 
          maxDonationRate spread-max established? watched-patch w-donations w-possibleDonations report-time smoothing
          av-rel-tol don-rate pop num-strong-cheat num-weak-cheat 
          sum-pop sum-num-weak-cheat sum-num-strong-cheat sum-av-rel-tol sum-don-rate
          av-pop av-num-weak-cheat av-num-strong-cheat av-av-rel-tol av-don-rate ]

turtles-own [tag tolerance skill age store strong-cheater?]
;; tag - is the value of an agent's socially recognisable characteristic
;; tolerance - is the range above and below its own tag which defines which other agents it will donate to 
;; skill - is the kind of nutrition that this agent can harvest
;; age - the age of the agent in simulation ticks, this is not necessary for the simulation but allows the setting of a maximum age if desired
;; store - the amount of energy an agent can store, otherwise some agents accumulate HUGE stores of energy
;; strong-cheater? - whether the agent is of the special kind "strong cheater", any of its offspring will also be strong cheaters

patches-own [foodAvailable ]
;; The energy available on the patch

;;; Setup procedure ;;;
to setup 
  set color-set [red blue lime cyan magenta gray orange violet  yellow]
  set maxPop numFood / foodUsageRate
  ifelse maxAge > 0 
    [set turnInc 360 / maxAge]
    [set turnInc 1]
  set maxDonationRate 1 / numPairings
  set spread-max 0.4
  ask patches [set pcolor grey]
  set watched-patch patch watched-pxcor watched-pycor
  ask watched-patch [set pcolor grey - 1]
  set established? false
  set report-time 0
  set smoothing 0.2
  set sum-pop 0 set sum-num-weak-cheat  0 set sum-num-strong-cheat  0 set sum-av-rel-tol  0 set sum-don-rate 0

;;; Main loop ;;;
to go
  set report-time smoothing * timer + (1 - smoothing) * report-time
  if maxTime > 0 and ticks >= maxTime [ stop ]
  ;; generate initial newcomers
  if count turtles > stopNewThreshold [set established? true]
  if not established? 
    [ask one-of patches [repeat initialNumNew [ generate-agent ]]]
  ;; random newcomers
  if newRate > 0 
    [ repeat (floor (newRate - random-float newRate)) [ ask one-of patches [generate-agent] ] ]
  ;; and any suitable existing agents reproduce
  ask turtles [
    if (min store >= reproduceVal) [
      set store map [ ? - initialFood ] store
  ;; scatter the food and share it out
  ask patches [
    ask turtles-here [
      let val (item skill store + item skill shares)
      if val > maxRes [ set val maxRes ]
      set store replace-item skill store val
  ;; then get agents to share where appropriate
  set donations 0
  set possibleDonations 0
  set w-possibleDonations 0
  set w-donations 0
  ask turtles [ do-sharing ]
;;  ifelse count turtles > 0 and possibleDonations > 0
;;    [ set donationRate donations / possibleDonations ]
;;    [ set donationRate 0 ]

  ;; age, consume food and kill off any unfit agents
  ask turtles [
    set age (age + 1)
    set store map [ ? - foodUsageRate ] store
    set size mean store / 20
    if min store <= 0 [die]
    if maxAge > 0 and age > maxAge [ die ]
    right turnInc
  ;; migrate
  ask turtles with [not strong-cheater?] [
    if random-float 1 < prob-migrate [
      ifelse 0 = random 2
        [ set xcor [pxcor] of patch-here + (2 * random 2) - 1  ]
        [ set ycor [pycor] of patch-here + (2 * random 2) - 1  ]
  ask turtles with [strong-cheater?] [
    if random-float 1 < prob-cheater-migrate [
      ifelse 0 = random 2
        [ set xcor [pxcor] of patch-here + (2 * random 2) - 1  ]
        [ set ycor [pycor] of patch-here + (2 * random 2) - 1  ]
  ;; do displays

to basic-agent-settings
    set store (n-values numFoodTypes [initialFood])
    set age 0
    set size 0.1

to turtle-display-settings
    set heading 0
    set size 0.1
    set color item skill color-set
    setxy pxcor + spread pycor + spread
    ifelse strong-cheater? [set color color - 2 set shape "face sad"] [set shape "default"]

;; generate a random agent at a patch
to generate-agent
  sprout 1 [
    set skill (random numFoodTypes)
    set tolerance (random-float 1) * maxTolerance
    set tag random-float 1
    ifelse random-float 1 < prob-cheater 
      [set strong-cheater? true set tolerance 0] 
      [set strong-cheater? false]

to introduce-cheaters
  ask watched-patch [
    sprout numb-cheat [
      set strong-cheater? true
      set skill (random numFoodTypes)
      set tolerance 0
      set tag random-float 1

to change-to-cheater
;;  let chosen-patch one-of patches with [count turtles-here >= numb-cheat]
  let numb-turn min list numb-cheat count [turtles-here] of watched-patch
  if numb-turn > 0 [
    ask n-of numb-turn [turtles-here] of watched-patch [
      set strong-cheater? true
      set tolerance 0

to-report spread
  report max list (-1 * spread-max) min list spread-max random-normal 0 (spread-max / 3)

;; create an offspring, given parent's tolerance, tag and skill
to hatch-agent
  hatch 1 [
    if random-float 1 < prob-cheater [set strong-cheater? true]
    if probValMutation > 0 [if random-float 1 < probValMutation [
        set tolerance tolerance + random-normal 0 sdValMutation
        set tag tag + random-normal 0 sdValMutation
    if sdReproductionNoise > 0 [
      set tolerance tolerance + random-normal 0 sdReproductionNoise
      set tag tag + random-normal 0 sdReproductionNoise
    if tolerance < 0 [set tolerance 0]
    if tolerance > maxTolerance [set tolerance maxTolerance]
    if tag < 0 [set tag 0]
    if tag > 1 [set tag 1]
    if probSkillMutation > 0 [if random-float 1 < probSkillMutation [
      set skill random numFoodTypes
      set color item skill color-set
    if strong-cheater? [set tolerance 0]

;; maybe turtle display could be... plotted on a val/tolerance axis, with color being skill, 
;; size being number at that val,tol and no direction?

;; scatter the food randomly amongst the food types
;; and calculate the share each agent should get
to generate-food
  set foodAvailable (n-values numFoodTypes [0])
  ;; why 100 here??
  set foodAvailable map [? + random 100] foodAvailable
  let total sum foodAvailable
  ;; just in case 0 is randomly generated numFoodTypes times
  ;; (it did actually happen in testing!)
  ifelse total = 0 [ generate-food ]
   set foodAvailable map [ ? / total * numFood ] foodAvailable
   let pos 0
   set shares []
   repeat numFoodTypes [
     let val count turtles-here with [ skill = pos ]
     ifelse val = 0
       [set shares lput 0 shares]
       [set shares lput ((item pos foodAvailable) / val) shares]
     set pos pos + 1

;; agent tries random pairings to share any excess resources
to do-sharing
  if count turtles-here > 1 [
    ;; first determine excess
    let unneeded []
    let pos 0
    repeat numFoodTypes [
      let val item pos store - excessVal
      ifelse val > 0
        [set unneeded lput val unneeded]
        [set unneeded lput 0 unneeded]
      set pos pos + 1
    ;; if agent has nothing to share, don't go further
    if sum unneeded = 0 [ stop ]
    ;; select numPairings partners, and keep the ones that
    ;; lie within tolerance
    let partners []
    repeat numPairings [
      let partner one-of other turtles-here
      let diff ([tag] of partner - tag)
      if diff < 0 [ set diff diff * -1 ]
      if diff < tolerance [set partners fput partner partners]
    if patch-here = watched-patch [set w-possibleDonations w-possibleDonations + numPairings]
    set possibleDonations possibleDonations + numPairings
    ;; if there are any suitable partners, dole out the excess
    ;; and delete from own stores
    let num length partners
    if num > 0 [
      set donations donations + length partners
      if patch-here = watched-patch [set w-donations w-donations + length partners]
      let j 0
      repeat numFoodTypes [
        let val item j store
        set store replace-item j store (val - item j unneeded)
        set j j + 1
      set unneeded map  [? / num] unneeded
      foreach  partners [ ask ? [take-excess unneeded] ]

;; incorporate donated resources into own stores
to take-excess [ amounts ]
  let pos 0
  repeat numFoodTypes [
    let tmp item pos store + (item pos amounts) * donationBenefit
    if maxRes > 0 and tmp > maxRes [ set tmp maxRes ]
    set store replace-item pos store tmp
    set pos pos + 1


to do-stats
  if ticks <= stats-after [stop]
  let stat-time ticks - stats-after
  set sum-pop sum-pop + pop
  set sum-num-weak-cheat  sum-num-weak-cheat + num-weak-cheat 
  set sum-num-strong-cheat  sum-num-strong-cheat + num-strong-cheat 
  set sum-av-rel-tol sum-av-rel-tol + av-rel-tol 
  set sum-don-rate sum-don-rate + don-rate
  set av-pop sum-pop / stat-time
  set av-num-weak-cheat sum-num-weak-cheat  / stat-time
  set av-num-strong-cheat sum-num-strong-cheat / stat-time
  set av-av-rel-tol sum-av-rel-tol / stat-time
  set av-don-rate sum-don-rate / stat-time

to do-attributes
  set-current-plot "Agent Attributes" 
  let num (count turtles)
  ifelse num > 0 [
    set-current-plot-pen "av. tolerance" 
    set av-rel-tol (mean [tolerance] of turtles) / maxTolerance
    plot av-rel-tol
  ] [
    set av-rel-tol 0
  ifelse count turtles > 0 and possibleDonations > 0 [
    set-current-plot-pen "donation rate"
    set don-rate donations / possibleDonations
    plot don-rate
  ] [
    set don-rate 0
  set pop count turtles
  set num-strong-cheat count turtles with [strong-cheater?]
  set num-weak-cheat count turtles with [tolerance = 0]

to do-subpopulations
;;  set-current-plot "Subpopulations"
;;  set-plot-pen-mode 2
;;  let pos 0
;;  repeat numFoodTypes [
;;    let current-set (([turtles-here] of watched-patch) with [skill = pos])
;;    set-plot-pen-color item pos color-set
;;    plot count current-set
;;    set pos pos + 1
;;  ]
;;  set-plot-pen-color black
;;  plot count ([turtles-here] of watched-patch) with [strong-cheater?]

;; plot each agent as a line representing its tag +/- tolerance
;; vertical axis is age, with agents evenly spaced between each 
;; age point so that they can be more clearly seen
to do-profile
  if not profile-on? [stop]
  set-current-plot "Tag Profile"
  set-plot-x-range 0 1
  ifelse maxAge > 0 
    [set-plot-y-range 0 maxAge]
    [set-plot-y-range 0 360]
  let pos 0
  repeat maxAge [
    let current-set ([turtles-here] of watched-patch) with [age = pos]
    let num count current-set
    if num > 0 [
      set current-set [self] of current-set
      let i 0
      foreach current-set [
        ask ? [
          set-plot-pen-color color
          plotxy (tag - tolerance) age + (i / num)
          plotxy (tag + tolerance) age + (i / num)
          if strong-cheater? [ plot-dot i num]
        set i i + 1
    set pos pos + 1

to plot-dot [i num]
  let dot-sizex 0.0025
  let dot-sizey 0.2
  set-plot-pen-color black
  plotxy tag + dot-sizex age + (i / num) 
  plotxy tag + dot-sizex age + (i / num) + dot-sizey
  plotxy tag - dot-sizex age + (i / num) + dot-sizey
  plotxy tag - dot-sizex age + (i / num) - dot-sizey
  plotxy tag + dot-sizex age + (i / num) - dot-sizey
  plotxy tag + dot-sizex age + (i / num) 

to-report safeDiv [nm dn]
  if dn = 0 [report 0]
  report nm / dn