Cancer progression is enhanced by stroma. However, the effect of stroma on treatment is less well understood. Tumours are not solely malignant cells; they are a mixture of cancer cells, fibroblasts and endothelial cells. These different types of cells interact, resulting in tumour growth. Stromal cells such as fibroblasts and stromal factors such as fibronectin are important in tumour growth.

Stromal cells contribute to the growth of melanoma. Melanoma cells activate fibroblasts. Activated fibroblasts provide structural support with extracellular matrix proteins and stimulate melanoma cells with growth factors. This results in increased melanoma cell division. Our model investigates the effect of these interactions on tumour growth.

Inert, healthy tissue: white
Stroma: light blue
Inactive fibroblast: grey
Active fibroblast: dark blue
Cancer: sand
Cancer stabilised by stroma: red

 
 

Code in Processing, its javascript variant p5

var typeColor = [];
var blockSize = 10;
var grid = [];
var treated = false; // to allow treatment

var INERT = 0;
var STROMA = 1;
var INACTIVE = 2;
var FIBROBLAST = 3;
var CANCER = 4;
var STABILISED = 5; // cancer cell + fibroblast

var NONE = 0;
var BASIC = 0.5;
var ASSISTED = 5;
var STIMULATED = 3.3;

//inert, stroma, inactive, fibroblast,  cancer, stabilised -- neighbour
var game = [
  [NONE, NONE, NONE, NONE, NONE, NONE], // other inert material
  [NONE, NONE, NONE, NONE, NONE, NONE], // stroma
  [NONE, NONE, NONE, NONE, STIMULATED, STIMULATED], // inactive fibroblast
  [NONE, NONE, NONE, NONE, STIMULATED, STIMULATED], // active fibroblast
  [BASIC, ASSISTED, ASSISTED, ASSISTED, BASIC, BASIC], // cancer
  [BASIC, ASSISTED, ASSISTED, ASSISTED, BASIC, BASIC] // stabilised cancer
];

function setup() {
  var xGrid = round(0.9 * windowWidth / blockSize);
  var yGrid = round(0.9 * windowHeight / blockSize);
  var canvas = createCanvas(xGrid * blockSize, yGrid * blockSize);
  
  noStroke();
  scale(blockSize);
  typeColor[INERT] = color(255);
  typeColor[STROMA] = color(111, 143, 190);
  typeColor[INACTIVE] = color(158, 151, 138);
  typeColor[FIBROBLAST] = color(30, 37, 111);
  typeColor[CANCER] = color(234, 201, 154);
  typeColor[STABILISED] = color(176, 47, 28);

  makeGrid(xGrid, yGrid);
  setNeighbors();
}

function makeGrid(xGrid, yGrid) {
  for (var x = 0; x < xGrid; x++) {
    grid[x] = [];
    for (var y = 0; y < yGrid; y++) {
      var type = INERT // most of the tissue is healthy

      if (random(8) < 1)
        if (y > yGrid / 2) type = INACTIVE; // dense scattering of fibroblasts (y axis is inverted)
        else if (random(8) < 1) type = INACTIVE; // loose scattering of fibroblasts

      if (dist(x, y, xGrid / 2, yGrid / 2) < 7) { // blob of cancer cells in the middle
        type = CANCER;
      }
      grid[x][y] = new Element(x, y, type);
    }
  }
}

function setNeighbors() {
  for (var x = 0; x < grid.length; x++) {
    for (var y = 0; y < grid[0].length; y++) {
      grid[x][y].setNeighbors();
    }
  }
}

function draw() {
  scale(blockSize);
  for (var i = 0; i < grid.length * grid[0].length; i++) update();
  treat();
}

function update() {
  var element = random(random(grid));

  var fitness = 0;
  for (var i = 0; i < element.neighbors.length; i++) {
    fitness += game[element.type][element.neighbors[i].type] / element.neighbors.length;
  }

  if (fitness > random(10)) { // if sufficiently fit, assimilate a neighbour
    random(element.neighbors).change(element.type);
  }
}

function treat() {
  if (treated) return;

  var count = 0;
  for (var x = 0; x < grid.length; x++) {
    for (var y = 0; y < grid[x].length; y++) {
      if (grid[x][y].type == FIBROBLAST || grid[x][y].type == CANCER || grid[x][y].type == STABILISED) {
        count++;
      }
    }
  }
  if (count < grid.length * grid[0].length / 4) return;

  for (x = 0; x < grid.length; x++) {
    for (y = 0; y < grid[x].length; y++) {
      switch (grid[x][y].type) {
        case INERT:
        case STROMA:
        case INACTIVE:
        case FIBROBLAST:
          break;
        case CANCER:
          grid[x][y].change(INERT); // all unstable cancer is killed
          break;
        case STABILISED:
          if (random(grid.length * grid[x].length / 9) > 1) grid[x][y].change(STROMA); // most stable cancer is killed
          break;
      }
      grid[x][y].draw();
    }
  }
  treated = true;
}

function Element(X, Y, t) {
  this.x = X;
  this.y = Y;
  this.neighbors = [];
  this.type = t;

  this.setNeighbors = function() {
    if (this.x > 0) this.neighbors.push(grid[this.x - 1][this.y]); // left
    if (this.y > 0) this.neighbors.push(grid[this.x][this.y - 1]); // down
    if (this.x < grid.length - 1) this.neighbors.push(grid[this.x + 1][this.y]); // right
    if (this.y < grid[0].length - 1) this.neighbors.push(grid[this.x][this.y + 1]); // up
  };

  this.draw = function() {
    fill(typeColor[this.type]);
    rect(this.x, this.y, 1, 1);
  };

  this.change = function(t) {
    if (this.type == STABILISED && t == CANCER) {
      return; // a free cancer cell doesn't change a stabilised cancer cell
    }

    if ((this.type == FIBROBLAST || this.type == STROMA) && (t == CANCER || t == STABILISED)) {
      this.type = STABILISED; // cancer cells invading fibroblasts or stroma become stabilised
    } else if (t == STABILISED) {
      this.type = CANCER; // a stabilised cancer cell produces a free cancer cell 
    } else if (t == INACTIVE) {
      this.type = FIBROBLAST; // an inactive fibroblast produces an active fibroblast
    } else {
      this.type = t;
    }
    this.draw();
  };

  this.draw();
}