cacatoo
Version:
Building, exploring, and sharing spatially structured models
198 lines (169 loc) • 10 kB
HTML
<!--
Cooperation example for students
DO NOT CHANGE THE FIRST 5 LINES OF CODE BELOW, THEY ARE NECESSARY FOR THE PAGE TO WORK
-->
<html>
<script src="https://bramvandijk88.github.io/cacatoo/scripts/cacatoo.js"></script> <!-- Include cacatoo library (compiled with rollup) -->
<script src="https://bramvandijk88.github.io/cacatoo/scripts/all.js"></script> <!-- Load other packages -->
<link rel="stylesheet" href="https://bramvandijk88.github.io/cacatoo/styles/cacatoo.css"> <!-- Set style sheet -->
<!--
CODE FOR SIMULATION BEGINS HERE
-->
<script>
// First, we define a few 'variables', which we can use to ajust the model later
var sim; // This 'global' variable will hold the entire simulation
var helper_rate_cooperator = 0.5 // Determines how much help a 'cooperative' individual gives to its neighbours
var helper_rate_cheater = 0.05 // Determines how much help a 'cheating' individual gives to its neighbours
var death = 0.05 // Determines how often an individual spontaneously dies, making a new spot available
var movement = 1 // Determines how much individuals 'move around'
/**
* function cacatoo() contains all the user-defined parts of a cacatoo-model. Configuration, update rules, what is displayed or plotted, etc. It's all here.
*/
function cacatoo() {
/*
1. SETUP. First, set up a configuration-object. Here we define how large the grid is, how long will it run, what colours will the critters be, etc.
*/
let config = { // Configuration of your model. How large is the grid, how long will it run, what colours will the critters be, etc.
title: "Cooperating with your neighbours",
description: "<center><div style=\"width:60%\"><img src=\"../images/coop.png\" width=900><b><br><br>Why do individuals cooperate? Let’s take the example where there are two kinds of individuals "+
"in a population: one cooperative (green) and one less cooperative (violet). Over time, the "+
"population will grow to colonise all of the available space. In order to claim a free spot on "+
"the grid, individuals require help. Of course, getting help from a cooperative (green) individual "+
"is much better than being helped by a less cooperative individual (violet). The non-cooperatives can "+
"get help from the cooperative individuals, and never waste valuable reproduction opportunities by "+
"replicating someone else. However, when left by their own devices, they are very bad at replicating. "+
"Who will win, and why? Let's find out!</b><br><br></div></center>",
maxtime: 1000000,
ncol: 300,
nrow: 100, // dimensions of the grid to build
seed: 56,
wrap: [false, false], // Wrap boundary [COLS, ROWS]
scale: 3, // scale of the grid (nxn pixels per grid point)
graph_interval: 5,
graph_update: 20,
statecolours: { 'alive': { 1: 'white' }}
}
/*
1. SETUP. (continued) Now, let's use that configuration-object to generate a new Cacatoo simulation
*/
sim = new Simulation(config) // Initialise a new Simulation instance with configuration given above
sim.makeGridmodel("coop") // Make a new gridmodel named cheater
sim.initialise = function()
{
let init_individuals = [{alive:1, helping_rate: helper_rate_cooperator},
{alive:1,helping_rate: helper_rate_cheater}]
sim.populateSpot(sim.coop, init_individuals, [0.5,0.5], 100, config.ncol/2, config.nrow/2) // cheaters and cooperators 50/50
sim.display()
sim.coop.resetPlots()
}
sim.coop.colourGradient('helping_rate', 100, [103, 64, 181], [59, 82, 139], [33, 144, 140], [93, 201, 99], [253, 231, 37])
sim.createDisplay_continuous({model:"coop", property:"helping_rate", label:"Cooperation",minval:0, maxval:1, num_colours: 100})
sim.coop.canvases["Cooperation"].legend.style.display = "none" // Hack to remove the legend :)
/**
* Define your next-state function here: for each grid point, what determines what that grid point will be like next timestep?
*/
sim.coop.nextState = function (x, y) // Define the next-state function. This example is two mutualists and a cheater
{
// let pA, pB, pC, psum
if (!this.grid[x][y].alive) // If there is no living cell here
{
let neighbours = this.getMoore8(this, x, y,'alive',1)
let winner = this.rouletteWheel(neighbours, 'fitness', 5.0)
if (winner != undefined)
{
this.grid[x][y].alive = winner.alive
this.grid[x][y].helping_rate = winner.helping_rate
}
}
if (this.rng.random() < death){ // Stochastic death (species become 0, which is an empty space for the next step to compete over)
this.grid[x][y].alive = 0
this.grid[x][y].helping_rate = 0
this.grid[x][y].fitness = 0
}
}
calculatefitness = function(x,y)
{
sim.coop.grid[x][y].fitness = sim.coop.sumMoore8(sim.coop, x, y, "helping_rate")
}
/**
* Define your update-function here: stuff that is applied to the entire grid every timestep. E.g. apply the next-state, diffuse stuff, mix individuals, show graphs, etc.
*/
sim.coop.update = function () {
this.apply_sync(calculatefitness)
this.synchronous() // Update all grid points based on the next-state function (defined above)
for(let i=0; i<movement;i++)this.MargolusDiffusion() // Every so often mix individuals a bit
this.updateGraphs() // OPTIONAL: add some graphs (see function below)
}
/**
* OPTIONAL: add some graphs to show how your model progresses. Cacatoo currently supports three graph types, all of which are illustrated in this example
*/
sim.coop.updateGraphs = function () {
// Let's count some stuff every update
let sumhelping = sumalive = sumcheater = sumcoop = 0
for (let x = 0; x < this.nc; x++) // x are columns
for (let y = 0; y < this.nr; y++) // y are rows
{
if (this.grid[x][y].alive == 1) {
sumhelping += this.grid[x][y].helping_rate
sumalive ++
if(this.grid[x][y].helping_rate == helper_rate_cooperator) sumcoop++
if(this.grid[x][y].helping_rate == helper_rate_cheater) sumcheater++
}
}
// Update the plots. If the plot do not yet exist, a new plot will be automatically added by cacatoo
this.plotArray(["Avg helping"],
[sumhelping / sumalive],
["#FF00AA"],
"Average helping rate")
this.plotArray(["population size", "#cheaters", "#cooperators"], [sumalive,sumcheater,sumcoop], ["black","#5f3ba6","#3dab78"], "Population sizes")
}
sim.coop.bottleneck = function()
{
console.log(sim)
for (let x = 0; x < this.nc; x++) {
for (let y = 0; y < this.nr; y++) {
if(this.rng.genrand_real1() < 0.95){
this.grid[x][y].alive = 0
this.grid[x][y].helping_rate = 0
}
}
}
}
/**
* OPTIONAL: add some buttons and sliders so you can play with your model easily
*/
sim.addButton("Play / Pause", function () { sim.toggle_play() }) // Add a button that calls function "display" in "model"
sim.addButton("Well mix", function () { sim.toggle_mix() }) // Add a button that calls function "perfectMix" in "model.cheater"
sim.addButton("Catastrophe!", function () {sim.coop.bottleneck() })
sim.addButton("Restart", function () {sim.initialise() })
sim.addSlider("movement", 0,10,1, "Movement rate")
sim.addSlider("death", 0.005, 1.00, 0.001, "Death rate")
sim.initialise()
sim.start()
sim.pause=true
}
</script>
<!--
CODE FOR SIMULATION ENDS HERE, DO NOT CHANGE BEYOND THIS POINT
-->
<body onload="cacatoo()">
<div class="header" id="header">
<h2>Cacatoo </h2>
</div>
<div class="content">
<br><br><b>Experiment with the simulations below. See if you can answer the following questions:</b>
<center><div style="width:600"><ul>
<li>After a short time, who appears to be 'winning'?</li>
<li>Wait for a longer time. Does something change?</li>
<li>Can you make the cooperators (green) do better?</li>
<li>Can you make cooperators (green) and cheater (purple) coexist?</li>
<li>What do you think would happen if "helping" could evolve?</li>
</ul></div></center>
</div>
<div class="content" id="canvas_holder"><b>The actual simulation (press Play/Pause below to start):</b><br></div>
<div class="content" id="form_holder"></div>
<div class="content" id="graph_holder"></div>
<div class="footer" id="footer">
</div>
</body>
</html>