Edit in JSFiddle

// Initialisation
var sim; // This 'global' variable will hold the entire simulation
var init_nr_of_clones = 150

// Toxin parameters
var nr_of_toxins = 10
var toxin_shown = 1 
var toxin_range = 3											// How far toxins can kill by diffusion

var base_death = 0.05                    // Determines how often an individual spontaneously dies, making a new spot available
var base_fitness = 1.0

// Mutations
var gene_cost = 0.05
var hgt_range = 2			// 1 means cell-cell contact is required
var hgt_rate_resistance = 0.0001
var hgt_rate_production = 0.001
var gene_loss = 0.005

/**
* 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: "Microbial warfare with HGT",
        description: "",
        maxtime: 100000,
        ncol: 140,
        nrow: 140,		                                            // dimensions of the grid to build
        seed: 58,
        fps: 60,                                                   // Note: FPS can only be set in fastmode
        fpsmeter: false,        
        wrap: [true, true],                                        // Wrap boundary [COLS, ROWS]
        scale: 2,				                                    // scale of the grid (nxn pixels per grid point)            
        graph_interval: 10,
        graph_update: 50,
        num_colours: 15,
        statecolours: {'clone': 'random', 'alive': { 1: 'black' }}
    }

    /*
        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("warfare")                                        // Make a new gridmodel named cheater
    sim.createDisplay_discrete({drawdots:false,radius:2,model:"warfare", property:"clone", label:"Clone types"})

    sim.createDisplay_continuous({model:"warfare", fill: "viridis", property:"numP", label:"Num producing", minval:0, maxval:nr_of_toxins, num_colours:40})
    sim.createDisplay_continuous({model:"warfare", fill: "viridis", property:"numR", label:"Num resistant", minval:0, maxval:nr_of_toxins, num_colours:40})
    sim.createDisplay_continuous({model:"warfare", fill: "viridis", property:"toxin_conc", label:"Concentration toxins", minval:0, maxval:10, num_colours:40})    
    // Create a display for the toxin production genotype    
    sim.warfare.colourGradient("genotypeP", 200, [156, 79, 150], [255, 99, 85],                                // pangenome colours setup
                              [251, 169, 73], [250, 228, 66], [139, 212, 72], [42, 168, 242], 
                              [50,100,255])
		sim.createDisplay_continuous({model:"warfare", property:"genotypeP", label:"Production profile", decimals: 0, nticks:5, maxval:200})
    // Create a display for the toxin resistance genotype
    sim.warfare.colourGradient("genotypeR", 200, [156, 79, 150], [255, 99, 85],                                // pangenome colours setup
                              [251, 169, 73], [250, 228, 66], [139, 212, 72], [42, 168, 242], 
                              [50,100,255])
		sim.createDisplay_continuous({model:"warfare", property:"genotypeR", label:"Resistance profile", decimals: 0, nticks:5, maxval:200})
    
		
    sim.initialise = function()
    {
    		sim.warfare.clearGrid()
    	 for (let i = 0; i < sim.warfare.nc; i++)                          // i are columns
       for (let j = 0; j < sim.warfare.nr; j++) 
       {
       		sim.warfare.grid[i][j].toxins = Array(nr_of_toxins).fill(0)
       }
       
    	 for(let c=0;c<init_nr_of_clones;c++)
       {
          let resistance = Array(nr_of_toxins).fill(0)
          let production = Array(nr_of_toxins).fill(0)
          for(let i in resistance)
          {
            if(sim.rng.random() < 0.1) 
            {
              resistance[i] = 1
              production[i] = 1
            }
          }
          
					pos_x = sim.rng.genrand_int(10,config.ncol-10)
          pos_y = sim.rng.genrand_int(10,config.nrow-10)
          let init_individuals = [{alive:1,clone:(c%15)+1, deathrate:base_death, production: production, resistance: resistance}]                         
          sim.populateSpot(sim.warfare, init_individuals, [1.0], 10, pos_x, pos_y)         // Place the three 'species' in grid points (33% A, 33% B, 33% C)                    
          sim.display()
       }
    }
    
    // Slightly modified version of "populateSpot", copied from source here: https://bramvandijk88.github.io/cacatoo/scripts/cacatoo.js
    sim.populateSpot = function(gridmodel,individuals, freqs,size, x, y)
     {
        let sumfreqs =0;
        if(individuals.length != freqs.length) throw new Error("populateGrid should have as many individuals as frequencies")
        for(let i=0; i<freqs.length; i++) sumfreqs += freqs[i];
         
        // Draw a circle
        for (let i = 0; i < gridmodel.nc; i++)                          // i are columns
        for (let j = 0; j < gridmodel.nr; j++)                           // j are rows
        {
            if ((Math.pow((i - x), 2) + Math.pow((j - y), 2)) < size)
            {
                let cumsumfreq = 0;                
                for(let n=0; n<individuals.length; n++)
                {
                    cumsumfreq += freqs[n];
                    if(this.rng.random() < cumsumfreq) {
                        Object.assign(gridmodel.grid[i % gridmodel.nc][j % gridmodel.nr],individuals[n]);
                        getGenotypeColour(i%gridmodel.nc,j%gridmodel.nr)
                        break
                    }
                }
            }
        }
         
     }

    sim.warfare.nextState = function (i, j)                               // Define the next-state function. This example is two mutualists and a cheater
    {
        // let pA, pB, pC, psum     
        let me = this.grid[i][j]
        me.toxin_conc = me.toxins[toxin_shown-1]
        
        if (!me.alive)                                                   // If there is no living cell here
        {
            let neighbours = this.getMoore8(this, i, j,'alive',1)
            let winner = this.rouletteWheel(neighbours, 'fitness', 5.0)
            if (winner != undefined)
            {
                me.alive = winner.alive
                me.genotypeP = winner.genotypeP
                me.genotypeR = winner.genotypeR
                me.numR = winner.numR
                me.numP = winner.numP
                me.clone = winner.clone
                me.deathrate = winner.deathrate
                me.fitness = winner.fitness
                me.resistance = [...winner.resistance]
                let mut = false

                for(let res in me.resistance) if(sim.rng.random() < gene_loss) me.resistance[res] = 0, mut = true
                me.production = [...winner.production]
                for(let tox in me.production) if(sim.rng.random() < gene_loss) me.production[tox] = 0, mut = true
	              mut = doHGT(i,j)
                if(mut) getGenotypeColour(i,j)
            }
        }

        if (this.rng.random() < this.grid[i][j].deathrate){                                    // Stochastic death (species become 0, which is an empty space for the next step to compete over)
            this.grid[i][j].alive = 0
            this.grid[i][j].fitness = 0
            this.grid[i][j].clone = 0
            this.grid[i][j].genotypeP = 0
            this.grid[i][j].genotypeR = 0
            this.grid[i][j].numP = 0
            this.grid[i][j].numR = 0            
        }
    }

    getGenotypeColour = function (i, j){
      me = sim.warfare.grid[i][j]
      if(!me.alive) throw new Error("Only call this function on living points")
      
      me.genotypeP = 0
      me.numP = 0
      me.genotypeR = 0
      me.numR = 0
      
      for(let t=0; t<nr_of_toxins; t++){
        if(me.production[t] == 1)	me.genotypeP += Math.pow(2,t+2), me.numP++
        if(me.resistance[t] == 1)	me.genotypeR += Math.pow(2,t+2), me.numR++
      }
      
      me.genotypeP = me.genotypeP % 200 + 1
      me.genotypeR = me.genotypeR % 200 + 1 
        
        
    }
    
    toxinDynamics = function(i,j)
    {

      let me = sim.warfare.grid[i][j]
      if(me.alive)
      {
        for(let tox in me.production)
        {
          if(me.production[tox] == 1)
          	for(let ii=-toxin_range; ii<=toxin_range; ii++)
            for(let jj=-toxin_range; jj<=toxin_range; jj++)
            {
                let spot = sim.warfare.getGridpoint(i+ii,j+jj)
                spot.toxins[tox] += 0.1/(Math.abs(ii)+Math.abs(jj)+1)
            }                       
        }
      }

      for(let tox in me.toxins){
        me.toxins[tox] *= 0.9
        if(me.toxins[tox]<0.001) me.toxins[tox] = 0.0
      }
    }

    calculateFitness = function(i,j)
    {
   		 let me = sim.warfare.grid[i][j]
       if(me.alive)
       {
         me.fitness = base_fitness
         for(let g in me.resistance)
         {
          if(me.resistance[g] == 1)
          {
          	me.fitness -= gene_cost
          } else {
          	if(me.toxins[g] > 0.5)
            	me.deathrate += 0.05
          }
          if(me.production[g] == 1)
          {
          	me.fitness -= gene_cost
          }

         }
         let resistance_profile = me.resistance
       }
       //sim.coop.sumMoore8(sim.coop, i, j, "helping_rate")-0.1*sim.coop.grid[i][j].helping_rate
    }
    
    doHGT = function(i,j){
    	me = sim.warfare.grid[i][j]
    	if(me.alive){
      	let hgt_happened = false
        for(let ii=-hgt_range; ii<=hgt_range; ii++)
        for(let jj=-hgt_range; jj<=hgt_range; jj++)
        {
           let donor = sim.warfare.getNeighbour(sim.warfare,i,j,sim.rng.genrand_int(1,8))
           if(donor.alive){
             for(let t=0; t<nr_of_toxins; t++){
               if(donor.production[t] >0 && sim.rng.random() < hgt_rate_production) sim.warfare.grid[i][j].production[t] = 1, hgt_happened=true
               if(donor.resistance[t] >0 && sim.rng.random() < hgt_rate_resistance) sim.warfare.grid[i][j].resistance[t] = 1, hgt_happened=true
             }      
           }
        }
        if(hgt_happened) getGenotypeColour(i,j)  	
          
      }
    }
    
    redistributeBarcodes = function(i,j){
    	me = sim.warfare.grid[i][j]
    	if(me.alive){
      	me.clone = Math.floor(sim.rng.random()*15)+1
      }            
    }

    /** 
    * 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.warfare.update = function () {
    		if(this.time%sim.config.graph_interval==0) this.updateGraphs()      
    		//this.apply_async(doHGT)
     		this.apply_async(toxinDynamics)
        this.apply_sync(calculateFitness)
        //this.diffuseStateVector('toxins',0.2)
        this.asynchronous()                                              // 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
        
        if(this.time%10000==0) this.apply_sync(redistributeBarcodes)
    }

    /**
    * 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.warfare.updateGraphs = function () {        
        // Update the plots. If the plot do not yet exist, a new plot will be automatically added by cacatoo
        this.plotPopsizes('clone',[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],{width:""})        
        let g_profiles = {}
        let p_profiles = {}
        let r_profiles = {}
        let pan_p  = []
        let pan_r  = []
        
        for (let i = 0; i < this.nc; i++)                          // i are columns
        for (let j = 0; j < this.nr; j++)                           // j are rows
        {
        	me = sim.warfare.grid[i][j]
          if(me.alive){
          	for(let g in me.production) if(me.production[g]>0) pan_p[g]=1
            for(let g in me.resistance) if(me.resistance[g]>0) pan_r[g]=1
          	let p = me.production.toString()
            let r = me.resistance.toString()
            g_profiles[p+r] = g_profiles[p+r] + 1 || 1
          	p_profiles[p] = p_profiles[p] + 1 || 1
            r_profiles[r] = r_profiles[r] + 1 || 1
          }        	
        }        
        let num_pan_p = pan_p.reduce((sum,a) => sum+a,0)
        let num_pan_r = pan_r.reduce((sum,a) => sum+a,0)
        
        let num_g_profiles = 0        
        let filtered = Object.entries(g_profiles).filter(([k,v]) => v>10);
				for(const [key,value] of Object.entries(g_profiles)){
        	if (value > 10) num_g_profiles++          
        }
        
        let num_abu_p_profiles = 0        
        filtered = Object.entries(p_profiles).filter(([k,v]) => v>10);
				for(const [key,value] of Object.entries(p_profiles)){
        	if (value > 10) num_abu_p_profiles++          
        }
        
        let num_abu_r_profiles = 0
        filtered = Object.entries(r_profiles).filter(([k,v]) => v>10);
				for(const [key,value] of Object.entries(r_profiles)){
        	if (value > 20) num_abu_r_profiles++
        }
        
        this.plotArray(["Genotype richness", "Production profile diversity", "Resistance profile diversity "],
        								[num_g_profiles, num_abu_p_profiles,num_abu_r_profiles],
                        ["grey", "black", "red"], 
                        "Genotype richness and production/resistance profiles",{width:""}) 
                        
        this.plotArray([ "Production (pan)",
        							  "Resistance (pan)"],
                        [num_pan_p, num_pan_r], 
                         ["black","#FF0000"], 
                         "Number of toxin/resistance genes in pangenome",{width:""})
        
    }
    

    /**
    * 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("Restart", function () {sim.initialise() })
    sim.addHTML("form_holder","<br>")
    sim.addSlider("toxin_shown", 1, nr_of_toxins, 1.00, "Show toxin")    
    sim.addSlider("hgt_rate_resistance", 0.00, 0.2, 0.001, "HGT rate for resistance genes")    
    sim.addSlider("hgt_rate_production", 0.00, 0.2, 0.001, "HGT rate for production genes")    
    sim.addSlider("gene_loss", 0.00, 0.2, 0.001, "Gene loss")    
    

    sim.initialise()
    sim.start()
    
    
}