0
\$\begingroup\$

Context:
I'm working on a simple environmental simulation in Javascript. I created some simple plants that grow, reproduce, and pass along their traits to their offspring. The simulation was pretty nice at first, but then I noticed that the plants kept grouping together really tightly, since I wasn't simulating any kind of resource competition between them; they grew the same amount whether there was 1 plant nearby or 100.
In order to encourage my plants to spread out more evenly, I decided to have the plants grow by extracting nutrients from the ground, which is a limited resource that depletes over time. This way plants will be forced to spread out in order to find richer soil to grow in. I'm working with a grid based system for the ground, where the ground is divided into evenly sized cells that have a certain amount of nutrients in them. Plants are not based on this grid, and can be anywhere.
The Goal:
I want there to be some diffusion in the nutrient concentration in the ground, so there isn't one cell with 100 nutrients right beside one with 2. This diffusion shouldn't be too strong: I still want there to be certain areas that have more or less nutrients than others. I don't really need any randomness to this process, since that would add some unnecessary complexity I just don't want.
The Problem: I can't get the nutrient values to distribute evenly, they distribute more in certain cells than others, and I don't know how to fix that.
What I have now:
Each cell is an object with a position, size, nutrients, and maxNutrients variables. (It inherits from a RectArea object that has some useful functions for determining whether it holds a certain point, among other things) maxNutrients is just used for visualization purposes when coloring each cell.

var Cell = function(pos,size,nutrients) {
    RectArea.call(this,pos,0,size,size);
    this.size = this.width;
    this.nutrients = nutrients;
    this.maxNutrients = nutrients;
};

The actual grid is handled by an Environment object that handles creating and simulating the grid. It stores the increment (size of each cell) of the system, the totalNutrients, and the maxNutrients(again just used for coloring purposes). Each cell is stored in the cells array.

var Environment = function(totalNutrients) {
    this.increment = increment;
    this.totalNutrients = totalNutrients;
    this.maxNutrients = totalNutrients;
    this.cells = [];
};

Environment has a function called initialize that pushes the correct number of cells to the array, while distributing the totalNutrients between them all (totalColumns, totalRows, and totalCells should be fairly self explanatory.):

Environment.prototype.initialize = function() {
    for (var i = 0; i < totalColumns; i++) {
        for (var j = 0; j < totalRows; j++) {
            var newPos = new PVector(increment/2 +(i*increment),increment/2 + (j*increment));
            var newNutr = this.totalNutrients/totalCells;
            this.cells.push( new Cell(newPos,increment,newNutr) );
        }
    }
};

Environment also has a function called getNeighbors that returns an array with the IDs (array reference number) of the 8 cells surrounding the given cell. It's kind of long, so I'll just put a summary.

Environment.prototype.getNeighbors = function(cellID) {
    var neighbors = [];
    
    //check if cellID is on any edges
    ...    
    
    //push the array reference numbers of each neighbor to the neighbors array. (doesn't loop around edges)
    ...
    
    return neighbors;
};

Then comes my attempt at implementing diffusion of nutrients (refer to comments in the code for an explanation)

Environment.prototype.distributeNutrients = function() {
    //Loop through each cell
    for (var i = 0; i < this.cells.length; i++) {
        var n = this.getNeighbors(i); //gets the neighbors of the current cell
        var l = []; //array of neighbors to be worked on
        //if a neighbor has less than a certain threshold of nutrients (given by nutrDist) then push that neighbor to l.
        for (var j = 0; j < n.length; j++) {
            if (this.cells[n[j]].nutrients < this.cells[i].nutrients * (1-nutrDist)) {
                l.push(n[j]);
            }
        }
        //sum up the total nutrients of this cell and all neighbors stored in l.
        var totalNutr = this.cells[i].nutrients;
        for (var j = 0; j < l.length; j++) {
            totalNutr += this.cells[l[j]].nutrients;
        }
        l.push(i); //adds the current cell to l so its nutrients.
        //divides the total Nutrients equally between all the cells stored in l.
        for (var j = 0; j < l.length; j++) {
            this.cells[l[j]].nutrients = totalNutr/l.length;
        }
    }
};

Part of the issue with this is that it loops through the grid top left to bottom right, which leads to some cells getting diffused earlier than others, which creates an imbalance in the diffusion. I have no clue how to fix this. Is there a way I can create an algorithm that evenly diffuses nutrients with the system I have created here? I'm open to new ideas for distribution, but I'd prefer to change as little as possible here, ideally just the distributeNutrients() function.
P.S. is this an appropriate amount of detail for my question? or did I go way overboard with explaining how my code works?

\$\endgroup\$

0

You must log in to answer this question.

Browse other questions tagged .